Merge remote-tracking branch 'retro/dev' into dev
# Conflicts: # .github/ISSUE_TEMPLATE/bug_report.md # README.md # app/build.gradle # app/src/main/AndroidManifest.xml # app/src/main/java/code/name/monkey/retromusic/App.kt # app/src/main/java/code/name/monkey/retromusic/Constants.kt # app/src/main/java/code/name/monkey/retromusic/activities/LyricsActivity.kt # app/src/main/java/code/name/monkey/retromusic/activities/PermissionActivity.kt # app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt # app/src/main/java/code/name/monkey/retromusic/activities/PurchaseActivity.kt # app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt # app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt # app/src/main/java/code/name/monkey/retromusic/activities/base/AbsBaseActivity.kt # app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/DeviceInfo.java # app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.kt # app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt # app/src/main/java/code/name/monkey/retromusic/extensions/DrawableExtensions.kt # app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt # app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt # app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt # app/src/main/java/code/name/monkey/retromusic/fragments/folder/FoldersFragment.java # app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt # app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt # app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlaybackControlsFragment.kt # app/src/main/java/code/name/monkey/retromusic/fragments/settings/AbsSettingsFragment.kt # app/src/main/java/code/name/monkey/retromusic/fragments/settings/MainSettingsFragment.kt # app/src/main/java/code/name/monkey/retromusic/model/Artist.kt # app/src/main/java/code/name/monkey/retromusic/model/Song.kt # app/src/main/java/code/name/monkey/retromusic/preferences/AlbumCoverStylePreferenceDialog.kt # app/src/main/java/code/name/monkey/retromusic/preferences/BlacklistPreferenceDialog.kt # app/src/main/java/code/name/monkey/retromusic/preferences/NowPlayingScreenPreferenceDialog.kt # app/src/main/java/code/name/monkey/retromusic/repository/AlbumRepository.kt # app/src/main/java/code/name/monkey/retromusic/service/MusicService.java # app/src/main/java/code/name/monkey/retromusic/util/AppRater.kt # app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java # app/src/main/java/code/name/monkey/retromusic/util/PremiumShow.kt # app/src/main/java/code/name/monkey/retromusic/util/RingtoneManager.kt # app/src/main/java/code/name/monkey/retromusic/util/SAFUtil.java # app/src/main/res/layout-sw600dp/item_list_no_image.xml # app/src/main/res/layout/activity_donation.xml # app/src/main/res/layout/activity_pro_version.xml # app/src/main/res/layout/fragment_blur.xml # app/src/main/res/layout/fragment_classic_player.xml # app/src/main/res/layout/fragment_color_player.xml # app/src/main/res/layout/fragment_fit.xml # app/src/main/res/layout/fragment_flat_player.xml # app/src/main/res/layout/fragment_full.xml # app/src/main/res/layout/fragment_gradient_player.xml # app/src/main/res/layout/fragment_home_player.xml # app/src/main/res/layout/fragment_peak_player.xml # app/src/main/res/layout/fragment_plain_player.xml # app/src/main/res/layout/fragment_simple_player.xml # app/src/main/res/layout/fragment_tiny_player.xml # app/src/main/res/layout/item_storage.xml # app/src/main/res/layout/sliding_music_panel_layout.xml # app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml # app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml # app/src/main/res/values-fa-rIR/strings.xml
This commit is contained in:
commit
4df292bddf
925 changed files with 37703 additions and 63683 deletions
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -13,7 +13,7 @@ assignees: ''
|
|||
[ ] Yes
|
||||
-->
|
||||
|
||||
**Have you read the [FAQ](https://www.github.com/h4h13/RetroMusicPlayer/tree/dev/FAQ.md)?**
|
||||
**Have you read the [FAQ](https://github.com/RetroMusicPlayer/RetroMusicPlayer/blob/master/FAQ.md)?**
|
||||
[Yes/No]
|
||||
|
||||
**Has the bug already been reported? Please search in GitHub issue tab before creating an issue.**
|
||||
|
|
34
.github/workflows/android.yml
vendored
Normal file
34
.github/workflows/android.yml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
name: Android CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ dev ]
|
||||
pull_request:
|
||||
branches: [ dev ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'temurin'
|
||||
cache: gradle
|
||||
|
||||
- name: Generate temporary keystore
|
||||
run: keytool -genkey -v -storetype pkcs12 -keystore store.p12 -storepass android -alias android -keyalg RSA -keysize 2048 -validity 10000 -dname CN=CI
|
||||
- name: Write retro.properties
|
||||
run: |
|
||||
cat >retro.properties <<EOF
|
||||
storeFile=$PWD/store.p12
|
||||
keyAlias=android
|
||||
storePassword=android
|
||||
keyPassword=android
|
||||
EOF
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
|
@ -1,7 +1,7 @@
|
|||
# Contributing
|
||||
|
||||
## 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:
|
||||
The [issue tracker](https://github.com/RetroMusicPlayer/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.
|
||||
|
||||
|
@ -30,6 +30,6 @@ Feature requests are welcome, please make sure to be as detailed as possible and
|
|||
**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).
|
||||
By contributing your code, you agree to license your contribution under the [GNU General Public License](https://github.com/RetroMusicPlayer/RetroMusicPlayer/blob/master/LICENSE.md).
|
||||
|
||||
Please note we have a code of conduct, please follow it in all your interactions with the project.
|
||||
|
|
192
FAQ.md
192
FAQ.md
|
@ -1,153 +1,157 @@
|
|||
#### Q: How do I get the beta version of Retro Music?
|
||||
## **Q: How do I get the beta version of Retro Music?**
|
||||
You can opt-in for the beta build by clicking on this link: https://play.google.com/apps/testing/code.name.monkey.retromusic
|
||||
|
||||
___
|
||||
|
||||
#### Q: How to restore my purchases?
|
||||
Make sure to switch and use account in the Play Store app through which you purchased before installing Retro music. The account used to install the app is also used to purchase/restore the pro license.
|
||||
## **Q: How to restore my purchases?**
|
||||
Make sure to switch and use your account in the Play Store app through which you purchased before installing Retro Music. The Google account used to install the app is also used to purchase/restore the pro license.
|
||||
|
||||
If you already installed, remove all other accounts except the one with which you purchased premium. Then restore the purchase.
|
||||
If you've already installed the app, remove all other accounts except the one from which you purchased premium, and then restore the purchase.
|
||||
|
||||
___
|
||||
|
||||
#### Q: How do I use offline synced lyrics?
|
||||
There are two methods for how to get offline synced lyrics.
|
||||
## Q: **How do I use offline synced lyrics?**
|
||||
There are three methods for adding offline synced lyrics in Retro Music.
|
||||
|
||||
#### Method 1:-
|
||||
##### STEP 1:
|
||||
Find the time stamped lyrics for your songs which don't have lyrics already. A time stamped lyrics looks like this, "[00:04:02] Some lyrics text" for example.
|
||||
##### STEP 2:
|
||||
Copy these time stamped lyrics.
|
||||
##### STEP 3:
|
||||
### ***Method 1:-***
|
||||
#### STEP 1:
|
||||
Find the time-stamped lyrics for your songs that don't have lyrics already. A time-stamped lyric looks like this, "[00:04:02] Some lyrics text" for example.
|
||||
#### STEP 2:
|
||||
Copy these time-stamped lyrics.
|
||||
#### STEP 3:
|
||||
Open retro music and head to the song synced lyrics editor.
|
||||
##### STEP 4:
|
||||
#### STEP 4:
|
||||
Paste the lyrics there normally and exit the editor
|
||||
##### STEP 5:
|
||||
Open lyrics and you should see your time stamped lyrics there.
|
||||
#### STEP 5:
|
||||
Open lyrics and you should see your time-stamped lyrics there.
|
||||
|
||||
#### Method 2:-
|
||||
##### STEP 1:
|
||||
Find the ".lrc" files for your songs which doesn't have lyrics already.
|
||||
##### STEP 2:
|
||||
A ".lrc" is like a text file which contains the time stamped lyrics for example, "[00:04:02] Some lyrics text"
|
||||
##### STEP 3:
|
||||
### ***Method 2:-***
|
||||
#### STEP 1:
|
||||
Download the time-stamped lyrics for your songs that don't have lyrics already. You can find ".lrc" files for popular songs at either of the websites given below. A time-stamped lyric looks like this, "[00:04:02] Some lyrics text" for example.
|
||||
#### STEP 2:
|
||||
A ".lrc" is like a text file that contains the time-stamped lyrics for example, "[00:04:02] Some lyrics text". Save your time-stamped lyrics are ".lrc" file.
|
||||
#### STEP 3:
|
||||
Now you have to rename the file you created in this way: <song_name> - <artist_name>.lrc or for better matching copy the <song_name> and the <artist_name> from the tag editor and then rename the file.
|
||||
##### STEP 4:
|
||||
Now paste the LRC files to the following path : /sdcard/Retromusic/lyrics/
|
||||
### STEP 4:
|
||||
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.
|
||||
### ***Method 3:- (Requires third-party app)***
|
||||
#### STEP 1:
|
||||
Download automatag or autotagger from Play Store.
|
||||
#### STEP 2:
|
||||
Find the time-stamped lyrics for your songs that don't have lyrics already. You can find ".lrc" files for popular songs at either of the websites given below. A time-stamped lyric looks like this, "[00:04:02] Some lyrics text" for example.
|
||||
#### STEP 3:
|
||||
Find your song to edit and paste the synced lyrics.
|
||||
> These apps add those synced lyrics in the music file itself instead of creating a ".lrc file for it."
|
||||
|
||||
**Lyrics Website Links:**
|
||||
- https://www.lyricsify.com/
|
||||
- https://www.syair.info
|
||||
|
||||
#### Q: Why isn't the artist's image downloading?
|
||||
Last.fm has disabled the download of artist's images for the time being, whether functionality for this will be restored in future is uncertain. So we have moved to deezer API for artist images which have very less artists in their database and you might not able to see covers on every artist profiles.
|
||||
**Some Important Notes:**
|
||||
- If you want to skip to a particular timestamp, simply scroll to the time stamp from where you want to start and a 'Play' icon will appear left to the particular stamp. Tap on the play button to play from there.
|
||||
- When you save lyrics by pasting lyrics in lyrics editor, close the lyrics and open again for lyrics to show.
|
||||
- For those who are having difficulty in making the synced lyrics work, we have a short tutorial video on it. Hope this helps you. {[Link in the note or here](https://youtu.be/1oIOTGWhNMY)}
|
||||
|
||||
___
|
||||
|
||||
#### Q: How do I change the theme?
|
||||
## **Q: How do I change the theme?**
|
||||
Settings -> Look and feel -> Select your theme.
|
||||
___
|
||||
|
||||
## **Q: Equalizer is very laggy and unstable or I am getting a "No equalizer found" error. Why?**
|
||||
- The Retro music in-built equalizer was removed updates ago so the only equalizer you will have by your OEM or Android native equalizer which isn't made by us and have no control over them. So you can report those issues to your OEM so that they can provide a fix in the next updates.
|
||||
|
||||
#### Q: Equailizer is very laggy and unstable or I am getting "No equalizer found" error. why?
|
||||
The Retro music in-built equalizer was removed updates ago so the only equalizer you will have by your OEM or android which aren't made by us and have no control over them. So you can report those issues to your OEM so that they can provide a fix in next updates.
|
||||
- If you are seeing "No Equalizer Found" in your device, this means your device doesn't have a stock equalizer "MusicFx" Equalizer. You can try using this one. It's made by AEX ROM developers.
|
||||
|
||||
If you are seeing "No Equalizer Found" in your device, this means your device doesn't have stock equalizer MusicFx Equalizer. You can try using this one. Its made by AEX ROM developers.
|
||||
https://drive.google.com/file/d/1_1bpsn6roeEyElGKikbU39lVKUH8O3xp/view?**usp=drivesdk
|
||||
|
||||
https://drive.google.com/file/d/1_1bpsn6roeEyElGKikbU39lVKUH8O3xp/view?usp=drivesdk
|
||||
___
|
||||
|
||||
|
||||
#### Q: Why aren't last added songs showing?
|
||||
## **Q: Why aren't last added songs showing?**
|
||||
Settings -> Other -> Last added playlist interval -> Select an option from the list.
|
||||
___
|
||||
|
||||
|
||||
#### Q: How do I enable fullscreen lockscreen controls?
|
||||
## **Q: How do I enable fullscreen lock screen controls?**
|
||||
Settings -> Personalize -> Fullscreen controls -> Enable (this will only be visible when songs are playing from Retro Music).
|
||||
___
|
||||
|
||||
|
||||
#### Q: Why are gallery or random pictures showing up as album art?
|
||||
## **Q: Why are my gallery or random pictures showing up as album art?**
|
||||
Settings -> Images -> Ignore media store covers -> Enable
|
||||
___
|
||||
|
||||
|
||||
#### Q: Which file types are supported?
|
||||
## **Q: Which file types are supported?**
|
||||
Retro Music uses the native media player that comes with your Android phone, so as long as a file type is supported by your phone, it's supported by Retro Music.
|
||||
___
|
||||
|
||||
|
||||
#### Q: Why is my device slowing down when I'm using the app?
|
||||
## **Q: Why is my device slowing down when I'm using the app?**
|
||||
Retro Music is image intensive, it keeps images in the cache for quick loading.
|
||||
___
|
||||
|
||||
|
||||
#### Q: The title "Retro Music" is showing on the top of the app, how can i fix this?
|
||||
## **Q: The title "Retro Music" is showing on the top of the app, how can I fix this?**
|
||||
Clear the app's cache and data.
|
||||
___
|
||||
|
||||
## **Q: My app is crashing, how do I fix this?** (Sorry, settings have changed internally)
|
||||
Please try to clear the data of the app. If it doesn't work, reinstalling fresh from the play store should help.
|
||||
___
|
||||
|
||||
#### Q: My app is crashing, how do i fix this? (Sorry, settings have changed internally)
|
||||
Please try clear data of the app. If it doesn't work, reinstalling fresh from play store should help.
|
||||
|
||||
|
||||
#### Q: Why has all the text gone white/dissapeared?
|
||||
## **Q: Why has all the text gone white/disappeared?**
|
||||
Change the theme to Black or Dark and change it back to what you had before.
|
||||
___
|
||||
|
||||
## **Q: Why some of my songs are not showing in my library?**
|
||||
- Try checking up if those songs are not less than 30 seconds, if so head to settings -> other -> filter song duration. Put this to zero and see the songs that should start appearing in the library.
|
||||
|
||||
#### Q: Why some of my songs are not showing in my library?
|
||||
Try checking up if those songs are not less than 30 seconds, if so head to settings -> other -> filter song duration. Put this to zero and see the songs should start appearimg in the library.
|
||||
- If this doesn't work out for you, re-scanning the media folder should help and subsequently rebooting the device to refresh the media store.
|
||||
|
||||
If this doesn't work out for you, re-scanning the media folder should help and subsquently rebooting the device to refresh media store.
|
||||
- At last, resort, If nothing worked and your audio files are stored in SD card. Try moving them to internal memory then back to SD card.
|
||||
|
||||
At last resort, If nothing worked and your audio files are stored in SD card. Try moving them to internal memory then back to SD card.
|
||||
___
|
||||
|
||||
## **Q: Why does my library shows song files twice or no song at all?**
|
||||
If you are seeing duplication of songs in the library or no songs at all, then it's because of the Media Store issue which got affected by some other app.
|
||||
|
||||
#### Q: Why some of my songs are not showing in my library?
|
||||
1. Try checking up if those songs are not less than 30 seconds, if so head to settings -> other -> filter song duration. Put this to zero and see the songs should start appearimg in the library.
|
||||
***To fix this:***
|
||||
1. Head to your device settings
|
||||
|
||||
2. If this doesn't work out for you, re-scanning the media folder should help and subsquently rebooting the device to refresh media store.
|
||||
1. Open up "Apps & notifications" (This name depends from ROM to ROM)
|
||||
|
||||
3. At last resort, If nothing worked and your audio files are stored in SD card. Try moving them to internal memory then back to SD card.
|
||||
1. Find the 'Media storage' app and clear storage (both data and cache) of it.
|
||||
|
||||
1. Then open the Retro Music app and manually scan your music from your storage.
|
||||
|
||||
#### Q: Why my playlist/playlist songs keep disappearing?
|
||||
Playlist/Playlist songs disappearing is based on android media store system. Save those playlist as file(Tap on three dot menu next to available playlist and save as file) and it should get fixed.
|
||||
1. Reboot the device to refresh the media store (Not sure if this is necessary)
|
||||
|
||||
**NOTE:** Don't panic when you will open Retro Music and see "Zero" songs there in the library. It's because you cleared Media Store which is responsible for recognising files on your device.
|
||||
___
|
||||
|
||||
#### Q: Why does my library shows song files twice or no song at all?
|
||||
If you are seeing duplication of songs in the library or no songs at all, then it's because of Media Store issue which got affected by some other app.
|
||||
## **Q: I can't find the folder menu anymore after the latest update?**
|
||||
Head to settings -> personalise. And select folders from "library categories". If there is no option of folders, tap on reset and select folders.
|
||||
___
|
||||
|
||||
To fix this:
|
||||
## **Q: After updating the app to the latest version, the font got removed. Why?**
|
||||
- Retro Music's font has now been replaced with system font, which means the default font your system uses will be used by Retro Music too. It fixes all font-related issues you used to face/are facing in the app.
|
||||
|
||||
• Head to your device settings
|
||||
- With the recent Retro Music v5 release, we have a built-in optional font "Manrope font" which you can toggle from Settings > Look & Feel > Toggle "Use manrope font".
|
||||
|
||||
• Open up "Apps & notifications" (This name depends from ROM to ROM)
|
||||
- If you think the font looks ugly, then you just need to change the default font from your Android settings (or use any Magisk module). If you can't, there's nothing we can do about it.
|
||||
___
|
||||
|
||||
• Find 'Media storage' app and clear storage (both data and cache) of it.
|
||||
## **Q: How to export playlist?**
|
||||
- ***From Retro Music:***
|
||||
|
||||
• Then open Retro Music app and manually scan your music from your storage.
|
||||
Head to the playlists tab > tap on the three-dot menu on the playlist you want to export > save as a file.
|
||||
|
||||
• Reboot the device to refresh media store (Not sure if this is necessary)
|
||||
- ***From Other Music Players:***
|
||||
|
||||
NOTE: Don't panic when you will open Retro Music and see "Zero" songs there in the library. It's because you cleared Media Store which is responsible for recognising files on your device.
|
||||
In your built-in music player, there should be an option to save that playlist as a file. Save them and import them from the file manager by opening it into retro music.
|
||||
|
||||
> Note that such playlist must be of your offline music only since retro music is an offline music player, not an online music player. So if your playlist is of online music, it can't be opened on other offline players nor can be exported
|
||||
___
|
||||
|
||||
#### Q: I can't find folder menu anymore after latest update?
|
||||
Head to settings -> personalise. And select folders from "library categories". If there is option of folders, tap on reset and select folders.
|
||||
## **Q: How to restore/import playlist?**
|
||||
Retro Music will automatically detect any playlist file when that playlist file is stored in internal storage/Playlist. However, if it doesn't, just open any "File manager" and open that playlist file with Retro Music.
|
||||
|
||||
|
||||
#### Q: After updating the app to latest version, font got removed. Why?
|
||||
Retro Music's font have now been replaced with system font now, which means the default font your system uses will be used by Retro too. It fixes all font related issues you used to face/are facing in the app.
|
||||
|
||||
If you think the font looks ugly, then you just need to change the default font from your Android settings (or use any Magisk module). If you can't, there's nothing we can do about it.
|
||||
|
||||
|
||||
#### Q: How to export playlist:
|
||||
In your built-in music player, there should be an option to save those playlist as file. Save them and import from file manager by opening it into retro music.
|
||||
|
||||
Note that those playlist must be of your offline music only since retro music is offline music player not an online music player. So if your playlist are of online music, it can't be opened on other offline players nor can be exported
|
||||
|
||||
|
||||
#### Q: How to restore/import playlist:
|
||||
Retro Music will automatically detect any playlist file when that playlist file is stored in InternalStorage/Playlist. However, if it doesn't, just open "file manager" and open that playlist file with Retro Music.
|
||||
|
||||
For restoring playlists, the location of songs must be same in both Playlist file and in your storage. For example, your music is in "Internalstorage/Music" and playlist file has songs location "Internalstorage/Songs". Then it will not going to work since both these location are different.
|
||||
|
||||
|
||||
#### Q: Adding songs to playlist or marking them as favourite are making app crash. Why?
|
||||
It's a known issue with only android 10 with its media store API when songs are in SD card due to introduction of Scoped Storage by Google. The issue have been created on Google Issue Tracker by many users. Many other players which doesn't have this issue are using a custom database for storing playlist. We will soon be implementing a custom database for playlist to fix this issue!
|
||||
|
||||
Workaround: You can move all songs to internal storage to fix the issue.
|
||||
|
||||
ISSUE link: https://issuetracker.google.com/issues/147619577
|
||||
For restoring playlists successfully, the location of songs must be the same in both the "Playlist" file and in your storage. For example, If your music is in "Internal storage/Music" and the playlist file has songs location "Internal storage/Songs". Then it will not be going to work since both these locations are different.
|
48
README.md
48
README.md
|
@ -2,19 +2,6 @@
|
|||
|
||||
Material Design music player for Android music lovers
|
||||
|
||||
## Table of contents
|
||||
- [Downloads](#downloads)
|
||||
- [Differences between Metro and RetroMusicPlayer](#differences-between-metro-and-retromusicplayer)
|
||||
- [Screenshots](#screenshots)
|
||||
- [App Themes](#app-themes)
|
||||
- [Player screen](#player-screen)
|
||||
- [9+ Now playing themes](#9-now-playing-themes)
|
||||
- [Navigation never made easier](#-navigation-never-made-easier)
|
||||
- [Colorful](#-colorful)
|
||||
- [Home](#-home)
|
||||
- [Included Features](#-included-features)
|
||||
- [License](#%EF%B8%8F-license)
|
||||
|
||||
## Downloads
|
||||
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
|
@ -31,26 +18,35 @@ Material Design music player for Android music lovers
|
|||
- Bug fixes
|
||||
- Minor differences in UI
|
||||
|
||||
## Screenshots
|
||||
## 📱 Screenshots
|
||||
### App Themes
|
||||
| <img src="screenshots/home.jpeg" width="200"/> | <img src="screenshots/home_dark.jpeg" width="200"/> | <img src="screenshots/home_black.jpeg" width="200"/> |
|
||||
| <img src="screenshots/home_light.jpg" width="200"/> | <img src="screenshots/home_dark.jpg" width="200"/> | <img src="screenshots/home_black.jpg" width="200"/> |
|
||||
|:---:|:---:|:---:|
|
||||
|Clearly white| Kinda dark | Just black|
|
||||
|
||||
### Player screen
|
||||
| <img src="screenshots/home.jpeg" width="200"/>| <img src="screenshots/list.jpeg" width="200"/>| <img src="screenshots/albums.jpeg" width="200"/>| <img src="screenshots/settings.jpeg" width="200"/>|
|
||||
|:---:|:---:|:---:|:---:|
|
||||
| Home | Songs | Albums | Settings |
|
||||
| <img src="screenshots/home_light.jpg" width="200"/>| <img src="screenshots/songs.jpg" width="200"/>| <img src="screenshots/albums.jpg" width="200"/>| <img src="screenshots/artists.jpg" width="200"/>| <img src="screenshots/settings.jpg" width="200"/>|
|
||||
|:---:|:---:|:---:|:---:|:---:|
|
||||
| Home | Songs | Albums | Artists | Settings |
|
||||
|
||||
### 9+ Now playing themes
|
||||
|
||||
| <img src="screenshots/np_normal.jpeg" width="200"/> |<img src="screenshots/np_fit.jpeg" width="200"/>| <img src="screenshots/np_flat.jpeg" width="200"/> | <img src="screenshots/np_color.jpeg" width="200"/> | <img src="screenshots/np_material.jpeg" width="200"/> |
|
||||
### Synced lyrics screen (Over Cover)
|
||||
| <img src="screenshots/synced_over_light.jpg" width="200"/>| <img src="screenshots/synced_over_dark.jpg" width="200"/>| <img src="screenshots/synced_over_black.jpg" width="200"/>|
|
||||
|:---:|:---:|:---:|
|
||||
| Synced Over Cover light | Synced Over Cover dark | Synced Over Cover black |
|
||||
|
||||
### Synced lyrics screen (Replace Cover)
|
||||
| <img src="screenshots/synced_replace_light.jpg" width="200"/>| <img src="screenshots/synced_replace_dark.jpg" width="200"/>| <img src="screenshots/synced_replace_black.jpg" width="200"/>|
|
||||
|:---:|:---:|:---:|
|
||||
| Synced Replace Cover light | Synced Replace Cover dark | Synced Replace Cover black |
|
||||
|
||||
### 10+ Now playing themes
|
||||
| <img src="screenshots/normal.jpg" width="200"/> |<img src="screenshots/fit.jpg" width="200"/>| <img src="screenshots/flat.jpg" width="200"/> | <img src="screenshots/color.jpg" width="200"/> | <img src="screenshots/material.jpg" width="200"/> |
|
||||
|:-----: |:-----: |:-----: |:-----: |:-----: |
|
||||
| Normal | Fit | Flat | Color | Material |
|
||||
|
||||
| <img src="screenshots/no_classic.jpeg" width="200"/> |<img src="screenshots/np_adaptive.jpeg" width="200"/>| <img src="screenshots/np_blur.jpeg" width="200"/> | <img src="screenshots/np_tiny.jpeg" width="200"/> | <img src="screenshots/np_peak.jpeg" width="200"/> |
|
||||
| <img src="screenshots/classic.jpg" width="200"/> |<img src="screenshots/adaptive.jpg" width="200"/>| <img src="screenshots/blur.jpg" width="200"/> | <img src="screenshots/tiny.jpg" width="200"/> | <img src="screenshots/peek.jpg" width="200"/> |
|
||||
|:-----: |:-----: |:-----: |:-----: |:-----: |
|
||||
| Classic | Adaptive | Blur | Tiny | Peak |
|
||||
| Classic | Adaptive | Blur | Tiny | Peek |
|
||||
|
||||
## 🧭 Navigation never made easier
|
||||
Self-explanatory interface without overloaded menus.
|
||||
|
@ -66,10 +62,15 @@ favorite songs. No other music player has this feature.
|
|||
|
||||
## 📦 Included Features
|
||||
- Base 3 themes (Clearly White, Kinda Dark and Just Black)
|
||||
- Chromecast support
|
||||
- Choose from 10+ now playing themes
|
||||
- Driving Mode
|
||||
- Headset/Bluetooth support
|
||||
- Music duration filter
|
||||
- Android auto support
|
||||
- Wallpaper accent picker on Android 8.1+
|
||||
- Material You support on Android 12+
|
||||
- Monet themed icon support on Android 13+
|
||||
- Folder support - Play songs by folder
|
||||
- Gapless playback
|
||||
- Volume controls
|
||||
|
@ -89,7 +90,6 @@ favorite songs. No other music player has this feature.
|
|||
- Smart Auto Playlists - Recently played, most played and history
|
||||
- Build your playlist on the go
|
||||
|
||||
|
||||
We are trying our best to bring you the best user experience. The app is regularly being updated for bug fixes and new features.
|
||||
|
||||
## 🗂️ License
|
||||
|
|
127
app/build.gradle
127
app/build.gradle
|
@ -1,23 +1,22 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: "androidx.navigation.safeargs.kotlin"
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion = '30.0.3'
|
||||
compileSdk 32
|
||||
buildTools = '30.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
minSdk 21
|
||||
targetSdk 32
|
||||
|
||||
renderscriptTargetApi 29 //must match target sdk and build tools
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
applicationId 'io.github.muntashirakon.Music'
|
||||
versionCode 10503
|
||||
versionName '4.0.010'
|
||||
versionCode 10579
|
||||
versionName '5.8.4'
|
||||
|
||||
multiDexEnabled true
|
||||
}
|
||||
|
@ -32,15 +31,17 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/LICENSE'
|
||||
exclude 'META-INF/NOTICE'
|
||||
exclude 'META-INF/java.properties'
|
||||
buildFeatures{
|
||||
viewBinding true
|
||||
}
|
||||
lintOptions {
|
||||
disable 'MissingTranslation'
|
||||
disable 'InvalidPackage'
|
||||
packagingOptions {
|
||||
resources {
|
||||
excludes += ['META-INF/LICENSE', 'META-INF/NOTICE', 'META-INF/java.properties']
|
||||
}
|
||||
}
|
||||
lint {
|
||||
abortOnError false
|
||||
disable 'MissingTranslation', 'InvalidPackage'
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
|
@ -53,86 +54,82 @@ android {
|
|||
configurations.all {
|
||||
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
|
||||
}
|
||||
androidExtensions {
|
||||
experimental = true
|
||||
}
|
||||
kapt {
|
||||
generateStubs = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation project(':appthemehelper')
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation "androidx.gridlayout:gridlayout:1.0.0"
|
||||
implementation "androidx.cardview:cardview:1.0.0"
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.annotation:annotation:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.2.5'
|
||||
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
||||
implementation 'androidx.annotation:annotation:1.3.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation "androidx.preference:preference-ktx:$preference_version"
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
implementation 'androidx.palette:palette-ktx:1.0.0'
|
||||
|
||||
def nav_version = "2.3.2"
|
||||
implementation "androidx.navigation:navigation-runtime-ktx:$nav_version"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
|
||||
implementation "androidx.navigation:navigation-runtime-ktx:$navigation_version"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
|
||||
|
||||
def room_version = "2.2.6"
|
||||
def room_version = '2.4.2'
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
|
||||
def lifecycle_version = "2.2.0"
|
||||
def lifecycle_version = "2.5.0-rc01"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||
|
||||
implementation 'com.google.android.material:material:1.3.0-alpha04'
|
||||
implementation "androidx.core:core-splashscreen:1.0.0-beta02"
|
||||
|
||||
implementation "com.google.android.material:material:$mdc_version"
|
||||
|
||||
def retrofit_version = '2.9.0'
|
||||
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
||||
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.6.0'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.7'
|
||||
|
||||
def material_dialog_version = "0.9.6.0"
|
||||
def material_dialog_version = "3.3.0"
|
||||
implementation "com.afollestad.material-dialogs:core:$material_dialog_version"
|
||||
implementation "com.afollestad.material-dialogs:commons:$material_dialog_version"
|
||||
implementation 'com.afollestad:material-cab:0.1.12'
|
||||
implementation "com.afollestad.material-dialogs:input:$material_dialog_version"
|
||||
implementation "com.afollestad.material-dialogs:color:$material_dialog_version"
|
||||
|
||||
def kotlin_coroutines_version = "1.3.8"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10"
|
||||
implementation 'com.afollestad:material-cab:2.0.1'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
|
||||
def kotlin_coroutines_version = '1.6.1'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
|
||||
def koin_version = "2.1.5"
|
||||
implementation "org.koin:koin-core:$koin_version"
|
||||
implementation "org.koin:koin-core-ext:$koin_version"
|
||||
implementation "org.koin:koin-androidx-scope:$koin_version"
|
||||
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
|
||||
implementation "org.koin:koin-androidx-fragment:$koin_version"
|
||||
implementation "org.koin:koin-androidx-ext:$koin_version"
|
||||
def koin_version = '3.2.0'
|
||||
implementation "io.insert-koin:koin-core:$koin_version"
|
||||
implementation "io.insert-koin:koin-android:$koin_version"
|
||||
|
||||
implementation 'com.github.bumptech.glide:glide:3.8.0'
|
||||
implementation 'com.github.bumptech.glide:okhttp3-integration:1.5.0'
|
||||
def glide_version = '4.13.2'
|
||||
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||
kapt "com.github.bumptech.glide:compiler:$glide_version"
|
||||
implementation "com.github.bumptech.glide:okhttp3-integration:$glide_version"
|
||||
|
||||
implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
|
||||
|
||||
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:3.4.0.201406110918-r'
|
||||
implementation 'com.github.ksoichiro:android-observablescrollview:1.6.0'
|
||||
implementation 'com.github.kabouzeid:recyclerview-fastscroll:1.9-kmod'
|
||||
implementation 'com.github.AdrienPoupa:jaudiotagger:2.2.3'
|
||||
implementation 'com.r0adkll:slidableactivity:2.1.0'
|
||||
implementation 'com.heinrichreimersoftware:material-intro:1.6'
|
||||
implementation 'com.github.dhaval2404:imagepicker:1.7.1'
|
||||
implementation 'org.jsoup:jsoup:1.11.1'
|
||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.0'
|
||||
implementation 'me.jorgecastillo:androidcolorx:0.2.0'
|
||||
implementation 'org.jsoup:jsoup:1.11.1'
|
||||
debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'
|
||||
}
|
||||
implementation 'com.github.bosphere.android-fadingedgelayout:fadingedgelayout:1.0.0'
|
||||
|
||||
apply from: '../spotless.gradle'
|
||||
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC3'
|
||||
implementation 'com.github.jetradarmobile:android-snowfall:1.2.1'
|
||||
|
||||
implementation "dev.chrisbanes.insetter:insetter:0.6.1"
|
||||
|
||||
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5'
|
||||
implementation 'com.github.Adonai:jaudiotagger:2.3.15'
|
||||
implementation 'com.r0adkll:slidableactivity:2.1.0'
|
||||
implementation 'com.heinrichreimersoftware:material-intro:2.0.0'
|
||||
implementation 'com.github.dhaval2404:imagepicker:2.1'
|
||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.8'
|
||||
implementation 'cat.ereza:customactivityoncrash:2.3.0'
|
||||
implementation 'me.tankery.lib:circularSeekBar:1.3.2'
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
|
||||
}
|
27
app/proguard-rules.pro
vendored
27
app/proguard-rules.pro
vendored
|
@ -16,9 +16,9 @@
|
|||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# Preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
|
@ -34,9 +34,15 @@
|
|||
|
||||
# Glide
|
||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
-keep class * extends com.bumptech.glide.module.AppGlideModule {
|
||||
<init>(...);
|
||||
}
|
||||
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
|
||||
*** rewind();
|
||||
}
|
||||
|
||||
# OkHttp
|
||||
|
@ -47,16 +53,19 @@
|
|||
|
||||
#-dontwarn
|
||||
#-ignorewarnings
|
||||
-dontshrink
|
||||
-dontobfuscate
|
||||
|
||||
#Jaudiotagger
|
||||
-dontwarn org.jaudiotagger.**
|
||||
-dontwarn org.jcodec.**
|
||||
-keep class org.jaudiotagger.** { *; }
|
||||
-keep class org.jcodec.** { *; }
|
||||
|
||||
-keepclassmembers enum * { *; }
|
||||
-keepattributes *Annotation*, Signature, Exception
|
||||
-keepnames class androidx.navigation.fragment.NavHostFragment
|
||||
-keepnames class code.name.monkey.retromusic.model.Home
|
||||
-keep class * extends androidx.fragment.app.Fragment{}
|
||||
-keepnames class * extends android.os.Parcelable
|
||||
-keepnames class * extends java.io.Serializable
|
||||
-keepnames class * extends java.io.Serializable
|
||||
-keep class code.name.monkey.retromusic.network.model.** { *; }
|
||||
-keep class code.name.monkey.retromusic.model.** { *; }
|
||||
-keep class com.google.android.material.bottomsheet.** { *; }
|
5
app/src/debug/res/values/bools.xml
Normal file
5
app/src/debug/res/values/bools.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<bool name="md3_available">true</bool>
|
||||
<bool name="allowBackup">false</bool>
|
||||
</resources>
|
4
app/src/debug/res/values/donottranslate.xml
Normal file
4
app/src/debug/res/values/donottranslate.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">Retro Music-Debug</string>
|
||||
</resources>
|
|
@ -6,6 +6,11 @@
|
|||
<item name="fontFamily">@font/sans</item>
|
||||
</style>
|
||||
|
||||
<style name="TextViewNormalCompress" parent="TextAppearance.MaterialComponents.Caption">
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="fontFamily">@font/sans</item>
|
||||
</style>
|
||||
|
||||
<style name="TextViewHeadline4" parent="TextAppearance.MaterialComponents.Headline4">
|
||||
<item name="fontFamily">@font/sans</item>
|
||||
</style>
|
||||
|
@ -89,8 +94,12 @@
|
|||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="circleImageView" parent="">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">5%</item>
|
||||
<style name="circleImageView" parent="ShapeAppearance.MaterialComponents">
|
||||
<item name="cornerSize">40dp</item>
|
||||
</style>
|
||||
|
||||
<style name="BottomSheetItemTextAppearance" parent="Widget.MaterialComponents.BottomNavigationView.Colored">
|
||||
<item name="android:textSize">13sp</item>
|
||||
<item name="fontFamily">@font/sans</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -4,11 +4,17 @@
|
|||
package="code.name.monkey.retromusic"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH"
|
||||
android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
|
@ -17,23 +23,25 @@
|
|||
android:name="android.permission.WRITE_SETTINGS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
|
||||
<application
|
||||
android:name="io.github.muntashirakon.retromusic.App"
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="@bool/allowBackup"
|
||||
android:configChanges="locale|layoutDirection"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:restoreAnyVersion="true"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.RetroMusic.FollowSystem"
|
||||
android:usesCleartextTraffic="false"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="AllowBackup,GoogleAppIndexingWarning"
|
||||
tools:targetApi="q">
|
||||
<activity
|
||||
android:name="io.github.muntashirakon.retromusic.activities.MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/SplashTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
@ -105,32 +113,86 @@
|
|||
<data android:mimeType="vnd.android.cursor.dir/audio" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.PlayingQueueActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.SettingsActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.tageditor.AlbumTagEditorActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.tageditor.SongTagEditorActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.LyricsActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.UserInfoActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.LicenseActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.WhatsNewActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.bugreport.BugReportActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.ShareInstagramStory" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.DriveModeActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.PermissionActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.LockScreenActivity" />
|
||||
<activity android:name="io.github.muntashirakon.retromusic.activities.saf.SAFRequestActivity" />
|
||||
<activity android:name=".activities.SettingsActivity" />
|
||||
<activity android:name=".activities.tageditor.AlbumTagEditorActivity" />
|
||||
<activity android:name=".activities.tageditor.SongTagEditorActivity" />
|
||||
<activity android:name=".activities.LicenseActivity" />
|
||||
<activity android:name=".activities.bugreport.BugReportActivity" />
|
||||
<activity android:name=".activities.ShareInstagramStory" />
|
||||
<activity android:name=".activities.DriveModeActivity" />
|
||||
<activity android:name=".activities.PermissionActivity" />
|
||||
<activity android:name=".activities.LockScreenActivity" />
|
||||
<activity android:name=".activities.saf.SAFRequestActivity" />
|
||||
<activity
|
||||
android:name=".fragments.backup.RestoreActivity"
|
||||
android:excludeFromRecents="false"
|
||||
android:exported="true"
|
||||
android:label="@string/restore"
|
||||
android:theme="@style/Theme.RetroMusic.Dialog">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:mimeType="application/octet-stream" />
|
||||
<data android:mimeType="application/x-zip-compressed" />
|
||||
<data android:mimeType="application/zip" />
|
||||
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="content" />
|
||||
<data android:mimeType="*/*" />
|
||||
<!--
|
||||
Work around Android's ugly primitive PatternMatcher
|
||||
implementation that can't cope with finding a . early in
|
||||
the path unless it's explicitly matched.
|
||||
-->
|
||||
<data android:host="*" />
|
||||
<data android:pathPattern=".*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="io.github.muntashirakon.retromusic.appshortcuts.AppShortcutLauncherActivity"
|
||||
android:name=".appshortcuts.AppShortcutLauncherActivity"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||
|
||||
<activity
|
||||
android:name="io.github.muntashirakon.retromusic.activities.saf.SAFGuideActivity"
|
||||
android:name=".saf.SAFGuideActivity"
|
||||
android:theme="@style/Theme.Intro" />
|
||||
|
||||
<activity
|
||||
android:name=".cast.ExpandedControlsActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activities.MainActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activities.ErrorActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="cat.ereza.customactivityoncrash.RESTART" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="io.github.muntashirakon.retromusic.misc.GenericFileProvider"
|
||||
android:name=".misc.GenericFileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
|
@ -139,7 +201,6 @@
|
|||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}"
|
||||
|
@ -150,13 +211,17 @@
|
|||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
<receiver android:name="io.github.muntashirakon.retromusic.service.MediaButtonIntentReceiver">
|
||||
<receiver
|
||||
android:name="androidx.media.session.MediaButtonReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="io.github.muntashirakon.retromusic.appwidgets.BootReceiver">
|
||||
<receiver
|
||||
android:name=".appwidgets.BootReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
|
@ -164,8 +229,8 @@
|
|||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name="io.github.muntashirakon.retromusic.appwidgets.AppWidgetBig"
|
||||
android:exported="false"
|
||||
android:name=".appwidgets.AppWidgetBig"
|
||||
android:exported="true"
|
||||
android:label="@string/app_widget_big_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
|
@ -177,8 +242,8 @@
|
|||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name="io.github.muntashirakon.retromusic.appwidgets.AppWidgetClassic"
|
||||
android:exported="false"
|
||||
android:name=".appwidgets.AppWidgetClassic"
|
||||
android:exported="true"
|
||||
android:label="@string/app_widget_classic_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
|
@ -189,8 +254,8 @@
|
|||
android:resource="@xml/app_widget_classic_info" />
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name="io.github.muntashirakon.retromusic.appwidgets.AppWidgetSmall"
|
||||
android:exported="false"
|
||||
android:name=".appwidgets.AppWidgetSmall"
|
||||
android:exported="true"
|
||||
android:label="@string/app_widget_small_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
|
@ -201,8 +266,8 @@
|
|||
android:resource="@xml/app_widget_small_info" />
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name="io.github.muntashirakon.retromusic.appwidgets.AppWidgetText"
|
||||
android:exported="false"
|
||||
android:name=".appwidgets.AppWidgetText"
|
||||
android:exported="true"
|
||||
android:label="@string/app_widget_text_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
|
@ -213,8 +278,8 @@
|
|||
android:resource="@xml/app_widget_text" />
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name="io.github.muntashirakon.retromusic.appwidgets.AppWidgetCard"
|
||||
android:exported="false"
|
||||
android:name=".appwidgets.AppWidgetCard"
|
||||
android:exported="true"
|
||||
android:label="@string/app_widget_card_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
|
@ -224,16 +289,43 @@
|
|||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/app_widget_card_info" />
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".appwidgets.AppWidgetMD3"
|
||||
android:exported="true"
|
||||
android:label="@string/app_widget_md3_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/app_widget_md3_info" />
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".appwidgets.AppWidgetCircle"
|
||||
android:exported="true"
|
||||
android:label="@string/app_widget_circle_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/app_widget_circle_info" />
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name="io.github.muntashirakon.retromusic.service.MusicService"
|
||||
android:name=".service.MusicService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
tools:ignore="ExportedService">
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<meta-data
|
||||
|
@ -244,12 +336,23 @@
|
|||
android:name="com.lge.support.SPLIT_WINDOW"
|
||||
android:value="true" />
|
||||
|
||||
<!-- Android Auto -->
|
||||
<meta-data
|
||||
android:name="code.name.monkey.retromusic.glide.RetroMusicGlideModule"
|
||||
android:value="GlideModule" />
|
||||
|
||||
android:name="com.google.android.gms.car.application"
|
||||
android:resource="@xml/automotive_app_desc" />
|
||||
<meta-data
|
||||
android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"
|
||||
android:value="GlideModule" />
|
||||
android:name="com.google.android.gms.car.application.theme"
|
||||
android:resource="@style/CarTheme" />
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.car.notification.SmallIcon"
|
||||
android:resource="@drawable/ic_notification" />
|
||||
</application>
|
||||
|
||||
<!--
|
||||
This is not that important, it's just here so that we can query equalizer package
|
||||
and check if it's present on A11+ because of Package visibility restrictions.
|
||||
-->
|
||||
<queries>
|
||||
<package android:name="com.android.musicfx" />
|
||||
</queries>
|
||||
</manifest>
|
||||
|
|
|
@ -6,26 +6,32 @@
|
|||
"image": "https://i.imgur.com/AoVs9oj.jpg"
|
||||
},
|
||||
{
|
||||
"name": "Lennart Glamann",
|
||||
"summary": "Play Store Banner & Images",
|
||||
"link": "https://t.me/FlixbusLennart",
|
||||
"image": "https://i.imgur.com/Q5Nsx1R.jpg"
|
||||
"name": "Prathamesh More",
|
||||
"summary": "Developer",
|
||||
"link": "https://prathameshmm02.github.io",
|
||||
"image": "https://i.imgur.com/ZHoOrHx.jpg"
|
||||
},
|
||||
{
|
||||
"name": "Daksh P. Jain",
|
||||
"summary": "Support Representative & Moderator",
|
||||
"summary": "Website & GitHub Maintainer",
|
||||
"link": "https://daksh.eu.org",
|
||||
"image": "https://i.imgur.com/fnYpg65.jpg"
|
||||
},
|
||||
{
|
||||
"name": "Milind Goel",
|
||||
"summary": "Support Representative & Moderator",
|
||||
"link": "https://t.me/MilindGoel15",
|
||||
"image": "https://i.imgur.com/Bz4De21_d.jpg"
|
||||
"link": "https://milindgoel15.github.io",
|
||||
"image": "https://i.imgur.com/hWL8t48.jpg"
|
||||
},
|
||||
{
|
||||
{
|
||||
"name": "Lennart Glamann",
|
||||
"summary": "Play Store Banner & Images",
|
||||
"link": "https://telegram.me/FlixbusLennart",
|
||||
"image": "https://i.imgur.com/Q5Nsx1R.jpg"
|
||||
},
|
||||
{
|
||||
"name": "Haythem Gataa",
|
||||
"summary": "App Logo Designer",
|
||||
"summary": "App Logo & Banners",
|
||||
"link": "https://dribbble.com/haythemgataa",
|
||||
"image": "https://i.imgur.com/g5RuIZq.jpg"
|
||||
}
|
||||
|
|
|
@ -24,40 +24,56 @@
|
|||
padding-top: 8px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p><b><a href="https://github.com/kabouzeid/Phonograph" title="Phonograph"> Phonograph</a></b> by
|
||||
Karim Abou Zeid</p>
|
||||
<p><b><a href="https://github.com/afollestad" title="Material Dialogs"> Material Dialogs and Cab</a></b>
|
||||
by Aidan Michael Follestad</p>
|
||||
<p><b><a href="http://developer.android.com/tools/support-library/index.html"
|
||||
title="AOSP Support Libraries"> AOSP Support Libraries</a></b>by AOSP contributors</p>
|
||||
title="AOSP Support Libraries">AOSP Support Libraries</a></b> by AOSP contributors</p>
|
||||
<p><b><a href="https://github.com/bumptech/glide" title="Glide"> Glide</a></b> by Sam Judd</p>
|
||||
<p><b><a href="https://github.com/square/retrofit" title="Retrofit"> Retrofit</a></b> by Square team
|
||||
</p>
|
||||
<p><b><a href="http://square.github.io/okhttp/" title="OkHttp"> OkHttp</a></b> by Square team</p>
|
||||
<p><b><a href="https://github.com/InsertKoinIO/koin"
|
||||
title="Koin">Koin</a></b> by Arnaud Giuliani</p>
|
||||
<p><b><a href="https://github.com/afollestad" title="Material Dialogs"> Material Dialogs and Cab</a></b>
|
||||
by Aidan Michael Follestad</p>
|
||||
<p><b><a href="https://github.com/afollestad/material-cab" title="Material Contextual Action Bar">
|
||||
Material Contextual Action Bar</a></b> by Aidan Michael Follestad</p>
|
||||
<p><b><a href="http://square.github.io/okhttp/" title="OkHttp"> OkHttp</a></b> by Square team</p>
|
||||
<p><b><a href="https://github.com/hdodenhof/CircleImageView" title="CircleImageView">
|
||||
CircleImageView</a></b> by Henning Dodenhof</p>
|
||||
<p><b><a href="https://github.com/DreaminginCodeZH/MaterialProgressBar" title="MaterialProgressBar">
|
||||
MaterialProgressBar</a></b> by Zhang Hai</p>
|
||||
<p><b><a href="https://github.com/anjlab/android-inapp-billing-v3"
|
||||
title="Android In-App Billing v3 Library"> Android In-App Billing v3 Library</a></b> by
|
||||
Henning Dodenhof</p>
|
||||
<p><b><a href="https://github.com/h6ah4i/android-advancedrecyclerview"
|
||||
title="Advanced RecyclerView"> Advanced RecyclerView</a></b> by Haruki Hasegawa</p>
|
||||
<p><b><a href="https://github.com/ksoichiro/Android-ObservableScrollView"
|
||||
title="Android-ObservableScrollView"> Android-ObservableScrollView</a></b> by Soichiro
|
||||
Kashima</p>
|
||||
<p><b><a href="https://github.com/Ereza/CustomActivityOnCrash"
|
||||
title="Custom Activity on Crash">Custom Activity on Crash</a></b> by Eduard Ereza Martínez
|
||||
</p>
|
||||
<p><b><a href="https://github.com/NanoHttpd/nanohttpd"
|
||||
title="NanoHttpd">NanoHttpd</a></b> by NanoHttpd Team</p>
|
||||
<p><b><a href="https://github.com/tankery/CircularSeekBar"
|
||||
title="Circular Seekbar">Circular Seekbar</a></b> by Tankery</p>
|
||||
<p><b><a href="https://github.com/Kaned1as/jaudiotagger"
|
||||
title="jAudioTagger">jAudioTagger</a></b> by Kanedias</p>
|
||||
<p><b><a href="https://github.com/zhanghai/AndroidFastScroll"
|
||||
title="Android Fast Scroll">Android Fast Scroll</a></b> by Zhang Hai</p>
|
||||
<p><b><a href="https://github.com/Dhaval2404/ImagePicker"
|
||||
title="Image Picker">Image Picker</a></b> by Dhaval Patel</p>
|
||||
<p><b><a href="https://github.com/heinrichreimer/material-intro"
|
||||
title="Material Intro">Material Intro</a></b> by Jan Heinrich Reimer</p>
|
||||
<p><b><a href="https://github.com/r0adkll/Slidr"
|
||||
title="Slidr">Slidr</a></b> by Drew Heavner</p>
|
||||
<p><b><a href="https://github.com/bosphere/Android-FadingEdgeLayout"
|
||||
title="FadingEdgeLayout">FadingEdgeLayout</a></b> by bosphere</p>
|
||||
<p><b><a href="https://github.com/yshrsmz/KeyboardVisibilityEvent"
|
||||
title="KeyboardVisibilityEvent">KeyboardVisibilityEvent</a></b> by Yasuhiro SHIMIZU</p>
|
||||
<p><b><a href="https://github.com/JetradarMobile/android-snowfall"
|
||||
title="android-snowfall">android-snowfall</a></b> by Jetradar Mobile</p>
|
||||
<p><b><a href="https://github.com/chrisbanes/insetter"
|
||||
title="Insetter">Insetter</a></b> by Chris Banes</p>
|
||||
<p><b><a href="https://materialdesignicons.com" title="Icons"> Icons</a></b> by Austin Andrews</p>
|
||||
<p><b><a href="https://www.techjuice.pk" title="City wallpaper"> Material Design City Wallpaper</a></b>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -6,22 +6,23 @@
|
|||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
div {
|
||||
margin: 20px 10px;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-block-end: 0rem;
|
||||
margin-block-start: 0rem;
|
||||
display: inline-block;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 0.85rem;
|
||||
padding-top: 0.5rem;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
|
@ -36,7 +37,6 @@
|
|||
|
||||
span {
|
||||
font-size: 0.7rem;
|
||||
line-height: 0.7rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
|
@ -44,39 +44,335 @@
|
|||
margin-block-end: 0.5rem;
|
||||
}
|
||||
|
||||
h3 span {
|
||||
border-radius: 0.2rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.3rem;
|
||||
padding-bottom: 0.3rem;
|
||||
h3 {
|
||||
margin: 10px 0px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
border-radius: 5px;
|
||||
margin-left: 10px;
|
||||
padding: 5px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
{style-placeholder}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h5>April 30, 2020</h5>
|
||||
<h2>v3.5.110</h2>
|
||||
<span class="tag"><i>Beta version</i></span>
|
||||
<h3><span class="colorHeader">What's New</span></h3>
|
||||
<ul>
|
||||
<li>Changed profile form image to icon</li>
|
||||
<li>New what's new screen</li>
|
||||
<li>Added In-App language changer, where you can select language</li>
|
||||
</ul>
|
||||
<h3><span class="colorHeader">Improved</span></h3>
|
||||
<ul>
|
||||
<li>Improved loading of Songs, Albums, Artists, Genres, Playlists</li>
|
||||
</ul>
|
||||
<!--<h3><span class="colorHeader">Bug fixes</span></h3>
|
||||
<ul>
|
||||
<li></li>
|
||||
</ul>-->
|
||||
<p>*If you face any UI related issues you clear app data and cache, if itsnot working try to
|
||||
uninstall and install
|
||||
again. </p>
|
||||
<div>
|
||||
<h5>May 13, 2022</h5>
|
||||
<h2>v5.8.4</h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added a toggle to enable/disable swipe down to dismiss mini player</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Fixed Playback speed and pitch not working when CrossFade is enabled</li>
|
||||
<li>Fixed crash when adding folders to blacklist</li>
|
||||
<li>Fix bugs in MD3 theme</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>May 07, 2022</h5>
|
||||
<h2>v5.8.3</h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added a new MD3 now playing theme</li>
|
||||
<li>Swipe down to dismiss Mini player</li>
|
||||
<li>Add support for Just Black with Material You</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Fixed sharing of files from SD Card</li>
|
||||
<li>Fix ChromeCast crash and bugs</li>
|
||||
<li>Fix Audio Crossfade</li>
|
||||
<li>Tried to fix incorrect song data in notification</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>April 8, 2022</h5>
|
||||
<h2>v5.8.0</h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Updated translations</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Fixed Classic Notification crash</li>
|
||||
<li>Fixed crash when clicking on Playlist in the Search Tab</li>
|
||||
<li>Fixed settings change not reflecting immediately</li>
|
||||
<li>Fixed shuffle</li>
|
||||
<li>Fixed Song duration not visible in Card & Blur card themes</li>
|
||||
<li>Fixed some Album skip styles</li>
|
||||
<li>Minor bug fixes & UI improvements</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>March 13, 2022</h5>
|
||||
<h2>v5.7.3</h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added adaptive color in Material now playing theme</li>
|
||||
<li>Added an option to share crash report</li>
|
||||
<li>Added options to clear, pause history</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Adapt Wallpaper accent for better readability</li>
|
||||
<li>Optimized search</li>
|
||||
<li>Made sleep timer precise</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>February 13, 2022</h5>
|
||||
<h2>v5.7.2<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Animated splash screen on Android 12 devices</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Fixed crash when removing song from Playing queue</li>
|
||||
<li>Fixed lyrics editing crash</li>
|
||||
<li>Fixed shuffle button not working</li>
|
||||
<li>Fixed crash on song deletion</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>February 1, 2022</h5>
|
||||
<h2>v5.7.1<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added option to disable changing song by swiping anywhere on the now playing screen</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Fixed Playlist save on A11+</li>
|
||||
<li>Fixed Just Black theme</li>
|
||||
<li>Fixed some UI issues</li>
|
||||
</ul>
|
||||
<h3>Improved</h3>
|
||||
<ul>
|
||||
<li>Improvements to search</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>January 25, 2022</h5>
|
||||
<h2>v5.7.0<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added Android Auto</li>
|
||||
<li>Added accent color extraction on Android 8.1+ devices</li>
|
||||
<li>Added new Circle widget</li>
|
||||
<li>Added Collapsing appbar to library tabs with an option to switch back to simple appbar
|
||||
</li>
|
||||
<li>Added Search tab</li>
|
||||
<li>Added option to use circular play button</li>
|
||||
<li>Added lyrics editing on A11+ devices again</li>
|
||||
<li>Added Long Press to forward, rewind current song</li>
|
||||
<li>Added ability to set Playback speed and pitch</li>
|
||||
<li>Added option to show lyrics over Cover</li>
|
||||
<li>Added option to keep screen on when showing lyrics</li>
|
||||
<li>Added option to switch to Manrope font</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Fixed Gapless Playback</li>
|
||||
<li>Fixed Shuffle FAB going behind Mini Player</li>
|
||||
<li>Fixed crashes on Pre-marshmallow devices</li>
|
||||
<li>Blacklisted songs can't be played after opening from outside the app</li>
|
||||
<li>Fixed various small bugs and some minor improvements</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>January 1, 2021</h5>
|
||||
<h2>v5.6.1</h2>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Fixed artist covers not updating and showing album cover images.</li>
|
||||
<li>Fixed FAB's not visible (Shuffle, Save, etc.)</li>
|
||||
<li>Fixed a crash when a Song is deleted in Artist Details</li>
|
||||
<li>Fixed Snowfall effect</li>
|
||||
<li>Fixed empty notification when queue is cleared</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>December 25, 2021</h5>
|
||||
<h2>v5.6.0<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added Artwork editing for songs</li>
|
||||
<li>Circle theme has album art now</li>
|
||||
<li>Upgraded tag editor library, we should support reading tags of more formats now e.g.
|
||||
opus.
|
||||
</li>
|
||||
<li>Added Snowfall effect</li>
|
||||
<li>Crossfade effect for background when Song is changed for Blur, Blur card themes</li>
|
||||
<li>Album art is hidden when Show lyrics is enabled</li>
|
||||
<li>Added fastscroll in Playlists tab</li>
|
||||
<li>Added Chooser to choose what to restore</li>
|
||||
<li>Hide BottomNavigation when only one tab is selected in Library Categories(This was
|
||||
already there but when changing screens it was getting visible)
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Fixed Ringtone crash</li>
|
||||
<li>Fixed blank album cover bug</li>
|
||||
<li>Fixed bottom navigation visible in Playing Queue</li>
|
||||
<li>Fixed lockscreen dragging glitch</li>
|
||||
<li>Fixed incorrect colors when no cover art is available</li>
|
||||
<li>Fixed favorite not updating when song is changed</li>
|
||||
<li>Fixed playlist not getting created & playlist creation crash with same name</li>
|
||||
<li>Fixed a bug in "Plain" Now playing theme where onClick event is consumed by the views
|
||||
behind the bottom sheet
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>December 6, 2021</h5>
|
||||
<h2>v5.4.2<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Bug Fixes</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>December 1, 2021</h5>
|
||||
<h2>v5.4.1<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added file selection from system file picker for restore</li>
|
||||
<li>Added Grid size selection for Playlists</li>
|
||||
</ul>
|
||||
<h3>Improved</h3>
|
||||
<ul>
|
||||
<li>Enable Material You by default on Android 12</li>
|
||||
<li>Reworked Grid Style saving</li>
|
||||
<li>Improved Playlist preview images</li>
|
||||
<li>UI improvements</li>
|
||||
</ul>
|
||||
<h3>Improved</h3>
|
||||
<ul>
|
||||
<li>Fixed File deletion on Android 10</li>
|
||||
<li>Fixed Sleep Timer crash</li>
|
||||
<li>Fixed Bottom Toolbar not clickable in now playing when Shuffle is clicked</li>
|
||||
<li>Fixed Album Artist sort order</li>
|
||||
<li>Fixed a crash when clicking the "Clear All" button in the Blacklist folder selection
|
||||
</li>
|
||||
<li>Fixed Continuous crashes on A12 when the song ends</li>
|
||||
<li>Fixed New Music Mix multiple clicks crash</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>November 22, 2021</h5>
|
||||
<h2>v5.4.0<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li><b>Material You</b></li>
|
||||
<li>Going Edge-to-Edge</li>
|
||||
<li>Added Backup & restore</li>
|
||||
</ul>
|
||||
<h3>Improved</h3>
|
||||
<ul>
|
||||
<li>Improved Animations</li>
|
||||
<li>Improved Crossfade</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>September 06, 2021</h5>
|
||||
<h2>v5.0.0</h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added ability to Remember last tab</li>
|
||||
<li>Added Whitelisting songs</li>
|
||||
<li>You can now browse SDCard from Folders Tab</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>August 22, 2021</h5>
|
||||
<h2>v4.4.0<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added Crossfade</li>
|
||||
<li>Multi-select in Album and Artist Details</li>
|
||||
<li>Albums now show Album Artists instead of artist of first song</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Fixed Playlist Preview Images</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>August 11, 2021</h5>
|
||||
<h2>v4.2.30<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Revamped Playlist Tab</li>
|
||||
<li>Revamped Genres Tab</li>
|
||||
<li>Added support for embedded Synced Lyrics</li>
|
||||
<li>Added animated icons</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Fixed Language Switching</li>
|
||||
<li>Fixed some reported bugs</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>July 18, 2021</h5>
|
||||
<h2>v4.2.020<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added ChromeCast Support</li>
|
||||
<li>Added Lyrics Editor for Normal and Synced Lyrics</li>
|
||||
<li>Added Ripple Animation for Color Theme</li>
|
||||
<li>Added Drag to seek in Tiny Theme</li>
|
||||
<li>Added Playlist Order</li>
|
||||
<li>Added Search Filters</li>
|
||||
<li>Added Audio Fade</li>
|
||||
<li>Synced Lyrics in all Themes</li>
|
||||
<li>Swipe anywhere to change song</li>
|
||||
</ul>
|
||||
<h3>Improved</h3>
|
||||
<ul>
|
||||
<li>Fixed Navigate by Album Artist</li>
|
||||
<li>Changed New Music Mix Actions</li>
|
||||
<li>Improved Animations</li>
|
||||
<li>And some minor bug fixes and improvements</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>October 12, 2020</h5>
|
||||
<h2>v4.0.10</h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Re-built from scratch using MVVM Architecture and JetPack Components</li>
|
||||
<li>New Material Design icon</li>
|
||||
<li>Implemented a custom database for playlists</li>
|
||||
<li>Added new Material Design motions</li>
|
||||
<li>Bug fixes & performance improvements</li>
|
||||
<li>Revamped Home tab UI</li>
|
||||
<li>Android 11 support</li>
|
||||
<li>And more!</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>April 30, 2020</h5>
|
||||
<h2>v3.5.110<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Changed profile form image to icon</li>
|
||||
<li>New what's new screen</li>
|
||||
<li>Added In-App language changer, where you can select language</li>
|
||||
</ul>
|
||||
<h3>Improved</h3>
|
||||
<ul>
|
||||
<li>Improved loading of Songs, Albums, Artists, Genres, Playlists</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
Binary file not shown.
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 17 KiB |
|
@ -13,14 +13,19 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic
|
||||
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import android.app.Application
|
||||
import cat.ereza.customactivityoncrash.config.CaocConfig
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.activities.ErrorActivity
|
||||
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
|
||||
import code.name.monkey.retromusic.helper.WallpaperAccentManager
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.context.startKoin
|
||||
|
||||
class App : MultiDexApplication() {
|
||||
class App : Application() {
|
||||
|
||||
private val wallpaperAccentManager = WallpaperAccentManager(this)
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
@ -37,9 +42,18 @@ class App : MultiDexApplication() {
|
|||
.coloredNavigationBar(true)
|
||||
.commit()
|
||||
}
|
||||
wallpaperAccentManager.init()
|
||||
|
||||
if (VersionUtils.hasNougatMR())
|
||||
DynamicShortcutManager(this).initDynamicShortcuts()
|
||||
|
||||
// setting Error activity
|
||||
CaocConfig.Builder.create().errorActivity(ErrorActivity::class.java).apply()
|
||||
}
|
||||
|
||||
override fun onTerminate() {
|
||||
super.onTerminate()
|
||||
wallpaperAccentManager.release()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -20,7 +20,8 @@ import android.provider.MediaStore
|
|||
object Constants {
|
||||
const val RATE_ON_GOOGLE_PLAY =
|
||||
"https://play.google.com/store/apps/details?id=code.name.monkey.retromusic"
|
||||
const val TRANSLATE = "https://github.com/h4h13/RetroMusicPlayer"
|
||||
const val TRANSLATE = "https://crowdin.com/project/retromusicplayer"
|
||||
const val WEBSITE = "https://retromusic.app"
|
||||
const val GITHUB_PROJECT = "https://github.com/MuntashirAkon/Metro"
|
||||
const val TELEGRAM_CHANGE_LOG = "https://t.me/retromusiclog"
|
||||
const val USER_PROFILE = "profile.jpg"
|
||||
|
@ -28,30 +29,34 @@ object Constants {
|
|||
const val APP_INSTAGRAM_LINK = "https://www.instagram.com/retromusicapp/"
|
||||
const val APP_TELEGRAM_LINK = "https://t.me/retromusicapp/"
|
||||
const val APP_TWITTER_LINK = "https://twitter.com/retromusicapp"
|
||||
const val FAQ_LINK = "https://github.com/h4h13/RetroMusicPlayer/blob/master/FAQ.md"
|
||||
const val FAQ_LINK = "https://github.com/RetroMusicPlayer/RetroMusicPlayer/blob/master/FAQ.md"
|
||||
const val PINTEREST = "https://in.pinterest.com/retromusicapp/"
|
||||
const val AUDIO_SCROBBLER_URL = "https://ws.audioscrobbler.com/2.0/"
|
||||
|
||||
const val IS_MUSIC =
|
||||
MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" + " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''"
|
||||
|
||||
const val DATA = "_data"
|
||||
|
||||
@Suppress("Deprecation")
|
||||
val baseProjection = arrayOf(
|
||||
BaseColumns._ID, // 0
|
||||
MediaStore.Audio.AudioColumns.TITLE, // 1
|
||||
MediaStore.Audio.AudioColumns.TRACK, // 2
|
||||
MediaStore.Audio.AudioColumns.YEAR, // 3
|
||||
MediaStore.Audio.AudioColumns.DURATION, // 4
|
||||
MediaStore.Audio.AudioColumns.DATA, // 5
|
||||
DATA, // 5
|
||||
MediaStore.Audio.AudioColumns.DATE_MODIFIED, // 6
|
||||
MediaStore.Audio.AudioColumns.ALBUM_ID, // 7
|
||||
MediaStore.Audio.AudioColumns.ALBUM, // 8
|
||||
MediaStore.Audio.AudioColumns.ARTIST_ID, // 9
|
||||
MediaStore.Audio.AudioColumns.ARTIST, // 10
|
||||
MediaStore.Audio.AudioColumns.COMPOSER, // 11
|
||||
"album_artist" // 12
|
||||
ALBUM_ARTIST // 12
|
||||
)
|
||||
const val NUMBER_OF_TOP_TRACKS = 99
|
||||
}
|
||||
|
||||
const val EXTRA_PLAYLIST_TYPE = "type"
|
||||
const val EXTRA_GENRE = "extra_genre"
|
||||
const val EXTRA_PLAYLIST = "extra_playlist"
|
||||
|
@ -70,7 +75,7 @@ const val NOW_PLAYING_SCREEN_ID = "now_playing_screen_id"
|
|||
const val CAROUSEL_EFFECT = "carousel_effect"
|
||||
const val COLORED_NOTIFICATION = "colored_notification"
|
||||
const val CLASSIC_NOTIFICATION = "classic_notification"
|
||||
const val GAP_LESS_PLAYBACK = "gap_less_playback"
|
||||
const val GAP_LESS_PLAYBACK = "gapless_playback"
|
||||
const val ALBUM_ART_ON_LOCK_SCREEN = "album_art_on_lock_screen"
|
||||
const val BLURRED_ALBUM_ART = "blurred_album_art"
|
||||
const val NEW_BLUR_AMOUNT = "new_blur_amount"
|
||||
|
@ -116,6 +121,8 @@ const val ALBUM_GRID_SIZE_LAND = "album_grid_size_land"
|
|||
const val SONG_GRID_SIZE_LAND = "song_grid_size_land"
|
||||
const val ARTIST_GRID_SIZE = "artist_grid_size"
|
||||
const val ARTIST_GRID_SIZE_LAND = "artist_grid_size_land"
|
||||
const val PLAYLIST_GRID_SIZE = "playlist_grid_size"
|
||||
const val PLAYLIST_GRID_SIZE_LAND = "playlist_grid_size_land"
|
||||
const val COLORED_APP_SHORTCUTS = "colored_app_shortcuts"
|
||||
const val AUDIO_DUCKING = "audio_ducking"
|
||||
const val LAST_ADDED_CUTOFF = "last_added_interval"
|
||||
|
@ -128,7 +135,9 @@ const val START_DIRECTORY = "start_directory"
|
|||
const val RECENTLY_PLAYED_CUTOFF = "recently_played_interval"
|
||||
const val LOCK_SCREEN = "lock_screen"
|
||||
const val ALBUM_ARTISTS_ONLY = "album_artists_only"
|
||||
const val ALBUM_ARTIST = "album_artist"
|
||||
const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order"
|
||||
const val ARTIST_DETAIL_SONG_SORT_ORDER = "artist_detail_song_sort_order"
|
||||
const val LYRICS_OPTIONS = "lyrics_tab_position"
|
||||
const val CHOOSE_EQUALIZER = "choose_equalizer"
|
||||
const val EQUALIZER = "equalizer"
|
||||
|
@ -137,3 +146,25 @@ const val SONG_GRID_STYLE = "song_grid_style"
|
|||
const val PAUSE_ON_ZERO_VOLUME = "pause_on_zero_volume"
|
||||
const val FILTER_SONG = "filter_song"
|
||||
const val EXPAND_NOW_PLAYING_PANEL = "expand_now_playing_panel"
|
||||
const val EXTRA_ARTIST_NAME = "extra_artist_name"
|
||||
const val TOGGLE_SUGGESTIONS = "toggle_suggestions"
|
||||
const val AUDIO_FADE_DURATION = "audio_fade_duration"
|
||||
const val CROSS_FADE_DURATION = "cross_fade_duration"
|
||||
const val SHOW_LYRICS = "show_lyrics"
|
||||
const val REMEMBER_LAST_TAB = "remember_last_tab"
|
||||
const val LAST_USED_TAB = "last_used_tab"
|
||||
const val WHITELIST_MUSIC = "whitelist_music"
|
||||
const val MATERIAL_YOU = "material_you"
|
||||
const val SNOWFALL = "snowfall"
|
||||
const val LYRICS_TYPE = "lyrics_type"
|
||||
const val PLAYBACK_SPEED = "playback_speed"
|
||||
const val PLAYBACK_PITCH = "playback_pitch"
|
||||
const val CUSTOM_FONT = "custom_font"
|
||||
const val APPBAR_MODE = "appbar_mode"
|
||||
const val WALLPAPER_ACCENT = "wallpaper_accent"
|
||||
const val SCREEN_ON_LYRICS = "screen_on_lyrics"
|
||||
const val CIRCLE_PLAY_BUTTON = "circle_play_button"
|
||||
const val SWIPE_ANYWHERE_NOW_PLAYING = "swipe_anywhere_now_playing"
|
||||
const val PAUSE_HISTORY = "pause_history"
|
||||
const val MANAGE_AUDIO_FOCUS = "manage_audio_focus"
|
||||
const val SWIPE_DOWN_DISMISS = "swipe_to_dismiss"
|
|
@ -1,41 +0,0 @@
|
|||
package code.name.monkey.retromusic;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.LocaleList;
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils;
|
||||
import java.util.Locale;
|
||||
|
||||
public class LanguageContextWrapper extends ContextWrapper {
|
||||
|
||||
public LanguageContextWrapper(Context base) {
|
||||
super(base);
|
||||
}
|
||||
|
||||
public static LanguageContextWrapper wrap(Context context, Locale newLocale) {
|
||||
Resources res = context.getResources();
|
||||
Configuration configuration = res.getConfiguration();
|
||||
|
||||
if (VersionUtils.INSTANCE.hasNougatMR()) {
|
||||
configuration.setLocale(newLocale);
|
||||
|
||||
LocaleList localeList = new LocaleList(newLocale);
|
||||
LocaleList.setDefault(localeList);
|
||||
configuration.setLocales(localeList);
|
||||
|
||||
context = context.createConfigurationContext(configuration);
|
||||
|
||||
} else if (VersionUtils.INSTANCE.hasLollipop()) {
|
||||
configuration.setLocale(newLocale);
|
||||
context = context.createConfigurationContext(configuration);
|
||||
|
||||
} else {
|
||||
configuration.locale = newLocale;
|
||||
res.updateConfiguration(configuration, res.getDisplayMetrics());
|
||||
}
|
||||
|
||||
return new LanguageContextWrapper(context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package code.name.monkey.retromusic
|
||||
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.os.LocaleList
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils.hasNougatMR
|
||||
import com.google.android.gms.common.annotation.KeepName
|
||||
import java.util.*
|
||||
|
||||
class LanguageContextWrapper(base: Context?) : ContextWrapper(base) {
|
||||
companion object {
|
||||
@KeepName
|
||||
fun wrap(context: Context?, newLocale: Locale?): LanguageContextWrapper {
|
||||
if (context == null) return LanguageContextWrapper(context)
|
||||
val configuration = context.resources.configuration
|
||||
if (hasNougatMR()) {
|
||||
configuration.setLocale(newLocale)
|
||||
val localeList = LocaleList(newLocale)
|
||||
LocaleList.setDefault(localeList)
|
||||
configuration.setLocales(localeList)
|
||||
} else {
|
||||
configuration.setLocale(newLocale)
|
||||
}
|
||||
return LanguageContextWrapper(context.createConfigurationContext(configuration))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ package code.name.monkey.retromusic
|
|||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import code.name.monkey.retromusic.auto.AutoMusicProvider
|
||||
import code.name.monkey.retromusic.cast.RetroWebServer
|
||||
import code.name.monkey.retromusic.db.BlackListStoreDao
|
||||
import code.name.monkey.retromusic.db.BlackListStoreEntity
|
||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||
|
@ -85,10 +87,26 @@ private val roomModule = module {
|
|||
RealRoomRepository(get(), get(), get(), get(), get())
|
||||
} bind RoomRepository::class
|
||||
}
|
||||
private val autoModule = module {
|
||||
single {
|
||||
AutoMusicProvider(
|
||||
androidContext(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get()
|
||||
)
|
||||
}
|
||||
}
|
||||
private val mainModule = module {
|
||||
single {
|
||||
androidContext().contentResolver
|
||||
}
|
||||
single {
|
||||
RetroWebServer(get())
|
||||
}
|
||||
}
|
||||
private val dataModule = module {
|
||||
single {
|
||||
|
@ -167,10 +185,11 @@ private val viewModules = module {
|
|||
)
|
||||
}
|
||||
|
||||
viewModel { (artistId: Long) ->
|
||||
viewModel { (artistId: Long?, artistName: String?) ->
|
||||
ArtistDetailsViewModel(
|
||||
get(),
|
||||
artistId
|
||||
artistId,
|
||||
artistName
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -189,4 +208,4 @@ private val viewModules = module {
|
|||
}
|
||||
}
|
||||
|
||||
val appModules = listOf(mainModule, dataModule, viewModules, networkModule, roomModule)
|
||||
val appModules = listOf(mainModule, dataModule, autoModule, viewModules, networkModule, roomModule)
|
|
@ -1,46 +0,0 @@
|
|||
package code.name.monkey.retromusic
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class PeekingLinearLayoutManager : LinearLayoutManager {
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
context: Context?,
|
||||
@RecyclerView.Orientation orientation: Int = RecyclerView.VERTICAL,
|
||||
reverseLayout: Boolean = false
|
||||
) : super(context, orientation, reverseLayout)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr,
|
||||
defStyleRes
|
||||
)
|
||||
|
||||
override fun generateDefaultLayoutParams() =
|
||||
scaledLayoutParams(super.generateDefaultLayoutParams())
|
||||
|
||||
override fun generateLayoutParams(lp: ViewGroup.LayoutParams?) =
|
||||
scaledLayoutParams(super.generateLayoutParams(lp))
|
||||
|
||||
override fun generateLayoutParams(c: Context?, attrs: AttributeSet?) =
|
||||
scaledLayoutParams(super.generateLayoutParams(c, attrs))
|
||||
|
||||
private fun scaledLayoutParams(layoutParams: RecyclerView.LayoutParams) =
|
||||
layoutParams.apply {
|
||||
when (orientation) {
|
||||
HORIZONTAL -> width = (horizontalSpace * ratio).toInt()
|
||||
VERTICAL -> height = (verticalSpace * ratio).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
private val horizontalSpace get() = width - paddingStart - paddingEnd
|
||||
|
||||
private val verticalSpace get() = height - paddingTop - paddingBottom
|
||||
|
||||
private val ratio = 0.8f // change to 0.7f for 70%
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package code.name.monkey.retromusic;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class RetroBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
|
||||
|
||||
private static final String TAG = "RetroBottomSheetBehavior";
|
||||
|
||||
private boolean allowDragging = true;
|
||||
|
||||
public RetroBottomSheetBehavior() {}
|
||||
|
||||
public RetroBottomSheetBehavior(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setAllowDragging(boolean allowDragging) {
|
||||
this.allowDragging = allowDragging;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(
|
||||
@NotNull CoordinatorLayout parent, @NotNull V child, @NotNull MotionEvent event) {
|
||||
if (!allowDragging) {
|
||||
return false;
|
||||
}
|
||||
return super.onInterceptTouchEvent(parent, child, event);
|
||||
}
|
||||
}
|
|
@ -15,32 +15,37 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityDriveModeBinding
|
||||
import code.name.monkey.retromusic.db.toSongEntity
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
||||
import code.name.monkey.retromusic.glide.BlurTransformation
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback
|
||||
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
|
||||
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.repository.RealRepository
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.activity_drive_mode.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2020-02-02.
|
||||
|
@ -48,21 +53,24 @@ import kotlinx.coroutines.withContext
|
|||
|
||||
class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
||||
|
||||
private lateinit var binding: ActivityDriveModeBinding
|
||||
private var lastPlaybackControlsColor: Int = Color.GRAY
|
||||
private var lastDisabledPlaybackControlsColor: Int = Color.GRAY
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
private val repository: RealRepository by inject()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_drive_mode)
|
||||
binding = ActivityDriveModeBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setUpMusicControllers()
|
||||
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
lastPlaybackControlsColor = ThemeStore.accentColor(this)
|
||||
close.setOnClickListener {
|
||||
lastPlaybackControlsColor = accentColor()
|
||||
binding.close.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
binding.repeatButton.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
|
@ -75,26 +83,37 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
|||
}
|
||||
|
||||
private fun setupFavouriteToggle() {
|
||||
songFavourite.setOnClickListener {
|
||||
MusicUtil.toggleFavorite(
|
||||
this@DriveModeActivity,
|
||||
MusicPlayerRemote.currentSong
|
||||
)
|
||||
binding.songFavourite.setOnClickListener {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleFavourite() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val isFavourite =
|
||||
MusicUtil.isFavorite(this@DriveModeActivity, MusicPlayerRemote.currentSong)
|
||||
private fun toggleFavorite(song: Song) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val playlist = repository.favoritePlaylist()
|
||||
val songEntity = song.toSongEntity(playlist.playListId)
|
||||
val isFavorite = repository.isSongFavorite(song.id)
|
||||
if (isFavorite) {
|
||||
repository.removeSongFromPlaylist(songEntity)
|
||||
} else {
|
||||
repository.insertSongs(listOf(song.toSongEntity(playlist.playListId)))
|
||||
}
|
||||
sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED))
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFavorite() {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val isFavorite: Boolean =
|
||||
repository.isSongFavorite(MusicPlayerRemote.currentSong.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
songFavourite.setImageResource(if (isFavourite) R.drawable.ic_favorite else R.drawable.ic_favorite_border)
|
||||
binding.songFavourite.setImageResource(if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
|
@ -118,21 +137,20 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
|||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
|
@ -156,24 +174,24 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
|||
updateSong()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
toggleFavourite()
|
||||
updateFavorite()
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause)
|
||||
binding.playPauseButton.setImageResource(R.drawable.ic_pause)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow)
|
||||
binding.playPauseButton.setImageResource(R.drawable.ic_play_arrow)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
else -> binding.shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
|
@ -183,19 +201,25 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
|||
private fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat)
|
||||
repeatButton.setColorFilter(
|
||||
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
|
||||
binding.repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
|
||||
binding.repeatButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
|
||||
binding.repeatButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -203,35 +227,36 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
|||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
toggleFavourite()
|
||||
updateFavorite()
|
||||
}
|
||||
|
||||
override fun onFavoriteStateChanged() {
|
||||
super.onFavoriteStateChanged()
|
||||
updateFavorite()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
|
||||
songTitle.text = song.title
|
||||
songText.text = song.artistName
|
||||
binding.songTitle.text = song.title
|
||||
binding.songText.text = song.artistName
|
||||
|
||||
SongGlideRequest.Builder.from(Glide.with(this), song)
|
||||
.checkIgnoreMediaStore(this)
|
||||
.generatePalette(this)
|
||||
.build()
|
||||
GlideApp.with(this)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.songCoverOptions(song)
|
||||
.transform(BlurTransformation.Builder(this).build())
|
||||
.into(object : RetroMusicColoredTarget(image) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
}
|
||||
})
|
||||
.into(binding.image)
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
binding.progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
|
||||
animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import cat.ereza.customactivityoncrash.CustomActivityOnCrash
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.util.FileUtils.createFile
|
||||
import code.name.monkey.retromusic.util.Share.shareFile
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class ErrorActivity : AppCompatActivity() {
|
||||
private val dayFormat: DateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||
private val ReportPrefix = "bug_report-"
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.customactivityoncrash_default_error_activity)
|
||||
|
||||
val restartButton =
|
||||
findViewById<Button>(R.id.customactivityoncrash_error_activity_restart_button)
|
||||
|
||||
val config = CustomActivityOnCrash.getConfigFromIntent(intent)
|
||||
if (config == null) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
restartButton.setText(R.string.customactivityoncrash_error_activity_restart_app)
|
||||
restartButton.setOnClickListener {
|
||||
CustomActivityOnCrash.restartApplication(
|
||||
this@ErrorActivity,
|
||||
config
|
||||
)
|
||||
}
|
||||
val moreInfoButton =
|
||||
findViewById<Button>(R.id.customactivityoncrash_error_activity_more_info_button)
|
||||
|
||||
moreInfoButton.setOnClickListener { //We retrieve all the error data and show it
|
||||
AlertDialog.Builder(this@ErrorActivity)
|
||||
.setTitle(R.string.customactivityoncrash_error_activity_error_details_title)
|
||||
.setMessage(
|
||||
CustomActivityOnCrash.getAllErrorDetailsFromIntent(
|
||||
this@ErrorActivity,
|
||||
intent
|
||||
)
|
||||
)
|
||||
.setPositiveButton(
|
||||
R.string.customactivityoncrash_error_activity_error_details_close,
|
||||
null
|
||||
)
|
||||
.setNeutralButton(
|
||||
R.string.customactivityoncrash_error_activity_error_details_share
|
||||
) { _, _ ->
|
||||
|
||||
val bugReport = createFile(
|
||||
context = this,
|
||||
"Bug Report",
|
||||
"$ReportPrefix${dayFormat.format(Date())}",
|
||||
CustomActivityOnCrash.getAllErrorDetailsFromIntent(
|
||||
this@ErrorActivity,
|
||||
intent
|
||||
), ".txt"
|
||||
)
|
||||
shareFile(this, bugReport)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
val errorActivityDrawableId = config.errorDrawable
|
||||
val errorImageView =
|
||||
findViewById<ImageView>(R.id.customactivityoncrash_error_activity_image)
|
||||
if (errorActivityDrawableId != null) {
|
||||
errorImageView.setImageResource(
|
||||
errorActivityDrawableId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,103 +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.activities;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.webkit.WebView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
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.ToolbarContentTintHelper;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/** Created by hemanths on 2019-09-27. */
|
||||
public class LicenseActivity extends AbsBaseActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
setDrawUnderStatusBar();
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_license);
|
||||
setStatusbarColorAuto();
|
||||
setNavigationbarColorAuto();
|
||||
setLightNavigationBar(true);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar);
|
||||
toolbar.setBackgroundColor(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface));
|
||||
WebView webView = findViewById(R.id.license);
|
||||
try {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
InputStream json = getAssets().open("oldindex.html");
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(json, StandardCharsets.UTF_8));
|
||||
String str;
|
||||
while ((str = in.readLine()) != null) {
|
||||
buf.append(str);
|
||||
}
|
||||
in.close();
|
||||
|
||||
// Inject color values for WebView body background and links
|
||||
final boolean isDark = ATHUtil.INSTANCE.isWindowBackgroundDark(this);
|
||||
final String backgroundColor =
|
||||
colorToCSS(
|
||||
ATHUtil.INSTANCE.resolveColor(
|
||||
this, R.attr.colorSurface, Color.parseColor(isDark ? "#424242" : "#ffffff")));
|
||||
final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000"));
|
||||
final String changeLog =
|
||||
buf.toString()
|
||||
.replace(
|
||||
"{style-placeholder}",
|
||||
String.format(
|
||||
"body { background-color: %s; color: %s; }", backgroundColor, contentColor))
|
||||
.replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this)))
|
||||
.replace(
|
||||
"{link-color-active}",
|
||||
colorToCSS(
|
||||
ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this))));
|
||||
|
||||
webView.loadData(changeLog, "text/html", "UTF-8");
|
||||
} catch (Throwable e) {
|
||||
webView.loadData(
|
||||
"<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", "UTF-8");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private String colorToCSS(int color) {
|
||||
return String.format(
|
||||
"rgb(%d, %d, %d)",
|
||||
Color.red(color),
|
||||
Color.green(color),
|
||||
Color.blue(color)); // on API 29, WebView doesn't load with hex colors
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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.activities
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil.isWindowBackgroundDark
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil.lightenColor
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityLicenseBinding
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
/** Created by hemanths on 2019-09-27. */
|
||||
class LicenseActivity : AbsThemeActivity() {
|
||||
private lateinit var binding: ActivityLicenseBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityLicenseBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
||||
try {
|
||||
val buf = StringBuilder()
|
||||
val json = assets.open("license.html")
|
||||
BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8)).use { br ->
|
||||
var str: String?
|
||||
while (br.readLine().also { str = it } != null) {
|
||||
buf.append(str)
|
||||
}
|
||||
}
|
||||
|
||||
// Inject color values for WebView body background and links
|
||||
val isDark = isWindowBackgroundDark(this)
|
||||
val backgroundColor = colorToCSS(
|
||||
surfaceColor(Color.parseColor(if (isDark) "#424242" else "#ffffff"))
|
||||
)
|
||||
val contentColor = colorToCSS(Color.parseColor(if (isDark) "#ffffff" else "#000000"))
|
||||
val changeLog = buf.toString()
|
||||
.replace(
|
||||
"{style-placeholder}", String.format(
|
||||
"body { background-color: %s; color: %s; }", backgroundColor, contentColor
|
||||
)
|
||||
)
|
||||
.replace("{link-color}", colorToCSS(accentColor()))
|
||||
.replace(
|
||||
"{link-color-active}",
|
||||
colorToCSS(
|
||||
lightenColor(accentColor())
|
||||
)
|
||||
)
|
||||
binding.license.loadData(changeLog, "text/html", "UTF-8")
|
||||
} catch (e: Throwable) {
|
||||
binding.license.loadData(
|
||||
"<h1>Unable to load</h1><p>" + e.localizedMessage + "</p>", "text/html", "UTF-8"
|
||||
)
|
||||
}
|
||||
binding.license.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun colorToCSS(color: Int): String {
|
||||
return String.format(
|
||||
"rgb(%d, %d, %d)",
|
||||
Color.red(color),
|
||||
Color.green(color),
|
||||
Color.blue(color)
|
||||
) // on API 29, WebView doesn't load with hex colors
|
||||
}
|
||||
}
|
|
@ -15,40 +15,38 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.app.KeyguardManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityLockScreenBinding
|
||||
import code.name.monkey.retromusic.extensions.hideStatusBar
|
||||
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
||||
import code.name.monkey.retromusic.extensions.whichFragment
|
||||
import code.name.monkey.retromusic.fragments.player.lockscreen.LockScreenControlsFragment
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import com.r0adkll.slidr.Slidr
|
||||
import com.r0adkll.slidr.model.SlidrConfig
|
||||
import com.r0adkll.slidr.model.SlidrListener
|
||||
import com.r0adkll.slidr.model.SlidrPosition
|
||||
import kotlinx.android.synthetic.main.activity_lock_screen.*
|
||||
|
||||
class LockScreenActivity : AbsMusicServiceActivity() {
|
||||
private lateinit var binding: ActivityLockScreenBinding
|
||||
private var fragment: LockScreenControlsFragment? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
lockScreenInit()
|
||||
setContentView(R.layout.activity_lock_screen)
|
||||
binding = ActivityLockScreenBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
hideStatusBar()
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
val config = SlidrConfig.Builder().listener(object : SlidrListener {
|
||||
override fun onSlideStateChanged(state: Int) {
|
||||
|
@ -61,10 +59,10 @@ class LockScreenActivity : AbsMusicServiceActivity() {
|
|||
}
|
||||
|
||||
override fun onSlideClosed(): Boolean {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (VersionUtils.hasOreo()) {
|
||||
val keyguardManager =
|
||||
getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
keyguardManager.requestDismissKeyguard(this@LockScreenActivity, null)
|
||||
getSystemService<KeyguardManager>()
|
||||
keyguardManager?.requestDismissKeyguard(this@LockScreenActivity, null)
|
||||
}
|
||||
finish()
|
||||
return true
|
||||
|
@ -75,18 +73,19 @@ class LockScreenActivity : AbsMusicServiceActivity() {
|
|||
|
||||
fragment = whichFragment<LockScreenControlsFragment>(R.id.playback_controls_fragment)
|
||||
|
||||
findViewById<View>(R.id.slide).apply {
|
||||
binding.slide.apply {
|
||||
translationY = 100f
|
||||
alpha = 0f
|
||||
ViewCompat.animate(this).translationY(0f).alpha(1f).setDuration(1500).start()
|
||||
animate().translationY(0f).alpha(1f).setDuration(1500).start()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("Deprecation")
|
||||
private fun lockScreenInit() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
if (VersionUtils.hasOreoMR1()) {
|
||||
setShowWhenLocked(true)
|
||||
val keyguardManager: KeyguardManager = getSystemService(KeyguardManager::class.java)
|
||||
keyguardManager.requestDismissKeyguard(this, null)
|
||||
val keyguardManager = getSystemService<KeyguardManager>()
|
||||
keyguardManager?.requestDismissKeyguard(this, null)
|
||||
} else {
|
||||
this.window.addFlags(
|
||||
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
|
||||
|
@ -107,9 +106,12 @@ class LockScreenActivity : AbsMusicServiceActivity() {
|
|||
|
||||
private fun updateSongs() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
SongGlideRequest.Builder.from(Glide.with(this), song).checkIgnoreMediaStore(this)
|
||||
.generatePalette(this).build().dontAnimate()
|
||||
.into(object : RetroMusicColoredTarget(image) {
|
||||
GlideApp.with(this)
|
||||
.asBitmapPalette()
|
||||
.songCoverOptions(song)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.dontAnimate()
|
||||
.into(object : RetroMusicColoredTarget(binding.image) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
fragment?.setColor(colors)
|
||||
}
|
||||
|
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hemanth Savarla.
|
||||
*
|
||||
* 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.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.WindowManager
|
||||
import androidx.core.view.ViewCompat
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.lyrics.LrcView
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.LyricUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.transition.platform.MaterialContainerTransform
|
||||
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
|
||||
get() {
|
||||
var baseUrl = "http://www.google.com/search?"
|
||||
var query = song.title + "+" + song.artistName
|
||||
query = "q=" + query.replace(" ", "+") + " .lrc"
|
||||
baseUrl += query
|
||||
return baseUrl
|
||||
}
|
||||
|
||||
private fun buildContainerTransform(): MaterialContainerTransform {
|
||||
val transform = MaterialContainerTransform()
|
||||
transform.setAllContainerColors(
|
||||
MaterialColors.getColor(findViewById(R.id.container), R.attr.colorSurface)
|
||||
)
|
||||
transform.addTarget(R.id.container)
|
||||
transform.duration = 300
|
||||
return transform
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_lyrics)
|
||||
ViewCompat.setTransitionName(container, "lyrics")
|
||||
setStatusbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
|
||||
setupWakelock()
|
||||
|
||||
toolbar.setBackgroundColor(surfaceColor())
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
updateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
|
||||
setupLyricsView()
|
||||
}
|
||||
|
||||
private fun setupLyricsView() {
|
||||
lyricsView.apply {
|
||||
setCurrentColor(ThemeStore.accentColor(context))
|
||||
setTimeTextColor(ThemeStore.accentColor(context))
|
||||
setTimelineColor(ThemeStore.accentColor(context))
|
||||
setTimelineTextColor(ThemeStore.accentColor(context))
|
||||
setDraggable(true, LrcView.OnPlayClickListener {
|
||||
MusicPlayerRemote.seekTo(it.toInt())
|
||||
return@OnPlayClickListener true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
updateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
lyricsView.updateTime(progress.toLong())
|
||||
}
|
||||
|
||||
private fun loadLRCLyrics() {
|
||||
lyricsView.setLabel("Empty")
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
if (LyricUtil.isLrcOriginalFileExist(song.data)) {
|
||||
lyricsView.loadLrc(LyricUtil.getLocalLyricOriginalFile(song.data))
|
||||
} else if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
||||
lyricsView.loadLrc(LyricUtil.getLocalLyricFile(song.title, song.artistName))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateTitleSong()
|
||||
loadLRCLyrics()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateTitleSong()
|
||||
loadLRCLyrics()
|
||||
}
|
||||
|
||||
private fun updateTitleSong() {
|
||||
song = MusicPlayerRemote.currentSong
|
||||
toolbar.title = song.title
|
||||
toolbar.subtitle = song.artistName
|
||||
}
|
||||
|
||||
private fun setupWakelock() {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_search, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
if (item.itemId == R.id.action_search) {
|
||||
RetroUtil.openUrl(this, googleSearchLrcUrl)
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
|
@ -20,40 +20,16 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
|||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.view.View
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.ui.NavigationUI
|
||||
import code.name.monkey.retromusic.ADAPTIVE_COLOR_APP
|
||||
import code.name.monkey.retromusic.ALBUM_COVER_STYLE
|
||||
import code.name.monkey.retromusic.ALBUM_COVER_TRANSFORM
|
||||
import code.name.monkey.retromusic.BANNER_IMAGE_PATH
|
||||
import code.name.monkey.retromusic.BLACK_THEME
|
||||
import code.name.monkey.retromusic.CAROUSEL_EFFECT
|
||||
import code.name.monkey.retromusic.CIRCULAR_ALBUM_ART
|
||||
import code.name.monkey.retromusic.DESATURATED_COLOR
|
||||
import code.name.monkey.retromusic.EXTRA_SONG_INFO
|
||||
import code.name.monkey.retromusic.GENERAL_THEME
|
||||
import code.name.monkey.retromusic.HOME_ARTIST_GRID_STYLE
|
||||
import code.name.monkey.retromusic.KEEP_SCREEN_ON
|
||||
import code.name.monkey.retromusic.LANGUAGE_NAME
|
||||
import code.name.monkey.retromusic.LIBRARY_CATEGORIES
|
||||
import code.name.monkey.retromusic.NOW_PLAYING_SCREEN_ID
|
||||
import code.name.monkey.retromusic.PROFILE_IMAGE_PATH
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.ROUND_CORNERS
|
||||
import code.name.monkey.retromusic.TAB_TEXT_MODE
|
||||
import code.name.monkey.retromusic.TOGGLE_ADD_CONTROLS
|
||||
import code.name.monkey.retromusic.TOGGLE_FULL_SCREEN
|
||||
import code.name.monkey.retromusic.TOGGLE_GENRE
|
||||
import code.name.monkey.retromusic.TOGGLE_HOME_BANNER
|
||||
import code.name.monkey.retromusic.TOGGLE_SEPARATE_LINE
|
||||
import code.name.monkey.retromusic.TOGGLE_VOLUME
|
||||
import code.name.monkey.retromusic.USER_NAME
|
||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||
import code.name.monkey.retromusic.extensions.extra
|
||||
import code.name.monkey.retromusic.extensions.findNavController
|
||||
import androidx.navigation.contains
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import code.name.monkey.retromusic.*
|
||||
import code.name.monkey.retromusic.activities.base.AbsCastActivity
|
||||
import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs
|
||||
import code.name.monkey.retromusic.interfaces.IScrollHelper
|
||||
import code.name.monkey.retromusic.model.CategoryInfo
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.repository.PlaylistSongsLoader
|
||||
|
@ -63,22 +39,18 @@ import kotlinx.coroutines.Dispatchers.IO
|
|||
import kotlinx.coroutines.launch
|
||||
import org.koin.android.ext.android.get
|
||||
|
||||
class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeListener {
|
||||
class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
||||
companion object {
|
||||
const val TAG = "MainActivity"
|
||||
const val EXPAND_PANEL = "expand_panel"
|
||||
}
|
||||
|
||||
override fun createContentView(): View {
|
||||
override fun createContentView(): SlidingMusicPanelLayoutBinding {
|
||||
return wrapSlidingMusicPanel()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setTaskDescriptionColorAuto()
|
||||
hideStatusBar()
|
||||
updateTabs()
|
||||
|
@ -87,6 +59,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
|
|||
if (!hasPermissions()) {
|
||||
findNavController(R.id.fragment_container).navigate(R.id.permissionFragment)
|
||||
}
|
||||
WhatsNewFragment.showChangeLog(this)
|
||||
}
|
||||
|
||||
private fun setupNavigationController() {
|
||||
|
@ -96,23 +69,75 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
|
|||
|
||||
val categoryInfo: CategoryInfo = PreferenceUtil.libraryCategory.first { it.visible }
|
||||
if (categoryInfo.visible) {
|
||||
navGraph.startDestination = categoryInfo.category.id
|
||||
if (!navGraph.contains(PreferenceUtil.lastTab)) PreferenceUtil.lastTab =
|
||||
categoryInfo.category.id
|
||||
navGraph.setStartDestination(
|
||||
if (PreferenceUtil.rememberLastTab) {
|
||||
PreferenceUtil.lastTab.let {
|
||||
if (it == 0) {
|
||||
categoryInfo.category.id
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
} else categoryInfo.category.id
|
||||
)
|
||||
}
|
||||
navController.graph = navGraph
|
||||
NavigationUI.setupWithNavController(getBottomNavigationView(), navController)
|
||||
bottomNavigationView.setupWithNavController(navController)
|
||||
// Scroll Fragment to top
|
||||
bottomNavigationView.setOnItemReselectedListener {
|
||||
currentFragment(R.id.fragment_container).apply {
|
||||
if (this is IScrollHelper) {
|
||||
scrollToTop()
|
||||
}
|
||||
}
|
||||
}
|
||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||
if (destination.id == navGraph.startDestinationId) {
|
||||
currentFragment(R.id.fragment_container)?.enterTransition = null
|
||||
}
|
||||
when (destination.id) {
|
||||
R.id.action_home, R.id.action_song, R.id.action_album, R.id.action_artist, R.id.action_folder, R.id.action_playlist, R.id.action_genre, R.id.action_search -> {
|
||||
// Save the last tab
|
||||
if (PreferenceUtil.rememberLastTab) {
|
||||
saveTab(destination.id)
|
||||
}
|
||||
// Show Bottom Navigation Bar
|
||||
setBottomNavVisibility(visible = true, animate = true)
|
||||
}
|
||||
R.id.playing_queue_fragment -> {
|
||||
setBottomNavVisibility(visible = false, hideBottomSheet = true)
|
||||
}
|
||||
else -> setBottomNavVisibility(
|
||||
visible = false,
|
||||
animate = true
|
||||
) // Hide Bottom Navigation Bar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveTab(id: Int) {
|
||||
PreferenceUtil.lastTab = id
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean =
|
||||
findNavController(R.id.fragment_container).navigateUp()
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
val expand = intent?.extra<Boolean>(EXPAND_PANEL)?.value ?: false
|
||||
if (expand && PreferenceUtil.isExpandPanel) {
|
||||
fromNotification = true
|
||||
slidingPanel.bringToFront()
|
||||
expandPanel()
|
||||
intent?.removeExtra(EXPAND_PANEL)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
|
||||
val expand = extra<Boolean>(EXPAND_PANEL).value ?: false
|
||||
if (expand && PreferenceUtil.isExpandPanel) {
|
||||
expandPanel()
|
||||
intent.removeExtra(EXPAND_PANEL)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -121,7 +146,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
|
|||
}
|
||||
|
||||
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) {
|
||||
if (key == GENERAL_THEME || key == MATERIAL_YOU || key == WALLPAPER_ACCENT || 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 || key == CUSTOM_FONT || key == APPBAR_MODE || key == CIRCLE_PLAY_BUTTON || key == SWIPE_DOWN_DISMISS) {
|
||||
postRecreate()
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +174,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
|
|||
handled = true
|
||||
}
|
||||
if (uri != null && uri.toString().isNotEmpty()) {
|
||||
MusicPlayerRemote.playFromUri(uri)
|
||||
MusicPlayerRemote.playFromUri(this@MainActivity, uri)
|
||||
handled = true
|
||||
} else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) {
|
||||
val id = parseLongFromIntent(intent, "playlistId", "playlist")
|
||||
|
|
|
@ -14,42 +14,61 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.Manifest
|
||||
import android.Manifest.permission.BLUETOOTH_CONNECT
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.core.view.isVisible
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.extensions.accentBackgroundColor
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.util.RingtoneManager
|
||||
import kotlinx.android.synthetic.main.activity_permission.*
|
||||
import code.name.monkey.retromusic.databinding.ActivityPermissionBinding
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
|
||||
class PermissionActivity : AbsMusicServiceActivity() {
|
||||
private lateinit var binding: ActivityPermissionBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView((R.layout.activity_permission))
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
binding = ActivityPermissionBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setStatusBarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setupTitle()
|
||||
|
||||
storagePermission.setButtonClick {
|
||||
binding.storagePermission.setButtonClick {
|
||||
requestPermissions()
|
||||
}
|
||||
if (VersionUtils.hasMarshmallow()) audioPermission.show()
|
||||
audioPermission.setButtonClick {
|
||||
if (RingtoneManager.requiresDialog(this@PermissionActivity)) {
|
||||
val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)
|
||||
intent.data = Uri.parse("package:" + applicationContext.packageName)
|
||||
startActivity(intent)
|
||||
if (VersionUtils.hasMarshmallow()) {
|
||||
binding.audioPermission.show()
|
||||
binding.audioPermission.setButtonClick {
|
||||
if (!hasAudioPermission()) {
|
||||
val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)
|
||||
intent.data = ("package:" + applicationContext.packageName).toUri()
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
finish.accentBackgroundColor()
|
||||
finish.setOnClickListener {
|
||||
|
||||
if (VersionUtils.hasS()) {
|
||||
binding.bluetoothPermission.show()
|
||||
binding.bluetoothPermission.setButtonClick {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
arrayOf(BLUETOOTH_CONNECT),
|
||||
PERMISSION_REQUEST)
|
||||
}
|
||||
}
|
||||
|
||||
binding.finish.accentBackgroundColor()
|
||||
binding.finish.setOnClickListener {
|
||||
if (hasPermissions()) {
|
||||
startActivity(
|
||||
Intent(this, MainActivity::class.java).addFlags(
|
||||
|
@ -63,10 +82,55 @@ class PermissionActivity : AbsMusicServiceActivity() {
|
|||
}
|
||||
|
||||
private fun setupTitle() {
|
||||
val appName = HtmlCompat.fromHtml(
|
||||
"Hello there! <br>Welcome to <b>Metro</b>",
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
appNameText.text = appName
|
||||
val appName =
|
||||
getString(R.string.message_welcome,
|
||||
"<b>Metro</b>")
|
||||
.parseAsHtml()
|
||||
binding.appNameText.text = appName
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
binding.finish.isEnabled = hasStoragePermission()
|
||||
if (hasStoragePermission()) {
|
||||
binding.storagePermission.checkImage.isVisible = true
|
||||
binding.storagePermission.checkImage.imageTintList =
|
||||
ColorStateList.valueOf(accentColor())
|
||||
}
|
||||
if (VersionUtils.hasMarshmallow()) {
|
||||
if (hasAudioPermission()) {
|
||||
binding.audioPermission.checkImage.isVisible = true
|
||||
binding.audioPermission.checkImage.imageTintList =
|
||||
ColorStateList.valueOf(accentColor())
|
||||
}
|
||||
}
|
||||
if (VersionUtils.hasS()) {
|
||||
if (hasBluetoothPermission()) {
|
||||
binding.bluetoothPermission.checkImage.isVisible = true
|
||||
binding.bluetoothPermission.checkImage.imageTintList =
|
||||
ColorStateList.valueOf(accentColor())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasStoragePermission(): Boolean {
|
||||
return ActivityCompat.checkSelfPermission(this,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
private fun hasBluetoothPermission(): Boolean {
|
||||
return ActivityCompat.checkSelfPermission(this,
|
||||
BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
private fun hasAudioPermission(): Boolean {
|
||||
return Settings.System.canWrite(this)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
super.onBackPressed()
|
||||
finishAffinity()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,202 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hemanth Savarla.
|
||||
*
|
||||
* 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.activities
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
|
||||
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 kotlinx.android.synthetic.main.activity_playing_queue.*
|
||||
|
||||
open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||
|
||||
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
||||
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
|
||||
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
|
||||
private var playingQueueAdapter: PlayingQueueAdapter? = null
|
||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||
|
||||
private fun getUpNextAndQueueTime(): String {
|
||||
val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position)
|
||||
return MusicUtil.buildInfoString(
|
||||
resources.getString(R.string.up_next),
|
||||
MusicUtil.getReadableDurationString(duration)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_playing_queue)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
setupToolbar()
|
||||
setUpRecyclerView()
|
||||
|
||||
clearQueue.setOnClickListener {
|
||||
MusicPlayerRemote.clearQueue()
|
||||
}
|
||||
checkForPadding()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressed()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRecyclerView() {
|
||||
recyclerViewTouchActionGuardManager = RecyclerViewTouchActionGuardManager()
|
||||
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||
recyclerViewSwipeManager = RecyclerViewSwipeManager()
|
||||
|
||||
val animator = DraggableItemAnimator()
|
||||
animator.supportsChangeAnimations = false
|
||||
|
||||
playingQueueAdapter = PlayingQueueAdapter(
|
||||
this,
|
||||
MusicPlayerRemote.playingQueue.toMutableList(),
|
||||
MusicPlayerRemote.position,
|
||||
R.layout.item_queue
|
||||
)
|
||||
wrappedAdapter = recyclerViewDragDropManager?.createWrappedAdapter(playingQueueAdapter!!)
|
||||
wrappedAdapter = wrappedAdapter?.let { recyclerViewSwipeManager?.createWrappedAdapter(it) }
|
||||
|
||||
linearLayoutManager = LinearLayoutManager(this)
|
||||
|
||||
recyclerView.layoutManager = linearLayoutManager
|
||||
recyclerView.adapter = wrappedAdapter
|
||||
recyclerView.itemAnimator = animator
|
||||
recyclerViewTouchActionGuardManager?.attachRecyclerView(recyclerView)
|
||||
recyclerViewDragDropManager?.attachRecyclerView(recyclerView)
|
||||
recyclerViewSwipeManager?.attachRecyclerView(recyclerView)
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (dy > 0) {
|
||||
clearQueue.shrink()
|
||||
} else if (dy < 0) {
|
||||
clearQueue.extend()
|
||||
}
|
||||
}
|
||||
})
|
||||
ThemedFastScroller.create(recyclerView)
|
||||
}
|
||||
|
||||
private fun checkForPadding() {
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
if (MusicPlayerRemote.playingQueue.isEmpty()) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
checkForPadding()
|
||||
updateQueue()
|
||||
updateCurrentSong()
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
updateQueue()
|
||||
updateCurrentSong()
|
||||
}
|
||||
|
||||
private fun updateCurrentSong() {
|
||||
toolbar.subtitle = getUpNextAndQueueTime()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateQueuePosition()
|
||||
}
|
||||
|
||||
private fun updateQueuePosition() {
|
||||
playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
toolbar.subtitle = getUpNextAndQueueTime()
|
||||
}
|
||||
|
||||
private fun updateQueue() {
|
||||
playingQueueAdapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
}
|
||||
|
||||
private fun resetToCurrentPosition() {
|
||||
recyclerView.stopScroll()
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.cancelDrag()
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.release()
|
||||
recyclerViewDragDropManager = null
|
||||
}
|
||||
if (recyclerViewSwipeManager != null) {
|
||||
recyclerViewSwipeManager?.release()
|
||||
recyclerViewSwipeManager = null
|
||||
}
|
||||
if (wrappedAdapter != null) {
|
||||
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||
wrappedAdapter = null
|
||||
}
|
||||
playingQueueAdapter = null
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
toolbar.subtitle = getUpNextAndQueueTime()
|
||||
toolbar.setBackgroundColor(surfaceColor())
|
||||
setSupportActionBar(toolbar)
|
||||
clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor())
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor()))
|
||||
).apply {
|
||||
clearQueue.setTextColor(this)
|
||||
clearQueue.iconTint = this
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,37 +14,44 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.Manifest.permission.BLUETOOTH_CONNECT
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDestination
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import com.afollestad.materialdialogs.color.ColorChooserDialog
|
||||
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 kotlinx.android.synthetic.main.activity_settings.*
|
||||
import code.name.monkey.retromusic.databinding.ActivitySettingsBinding
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.color.ColorCallback
|
||||
|
||||
class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback {
|
||||
class SettingsActivity : AbsBaseActivity(), ColorCallback, OnThemeChangedListener {
|
||||
private lateinit var binding: ActivitySettingsBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_settings)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
val mSavedInstanceState = extra<Bundle>(TAG).value ?: savedInstanceState
|
||||
super.onCreate(mSavedInstanceState)
|
||||
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setupToolbar()
|
||||
setPermissionDeniedMessage(getString(R.string.permission_bluetooth_denied))
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
setNavigationBarColorPreOreo(surfaceColor())
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
setTitle(R.string.action_settings)
|
||||
applyToolbar(toolbar)
|
||||
applyToolbar(binding.toolbar)
|
||||
val navController: NavController = findNavController(R.id.contentFrame)
|
||||
navController.addOnDestinationChangedListener { _, _, _ ->
|
||||
toolbar.title = navController.currentDestination?.let { getStringFromDestination(it) }
|
||||
binding.collapsingToolbarLayout.title =
|
||||
navController.currentDestination?.let { getStringFromDestination(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +66,7 @@ class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback {
|
|||
R.id.personalizeSettingsFragment -> R.string.personalize
|
||||
R.id.themeSettingsFragment -> R.string.general_settings_title
|
||||
R.id.aboutActivity -> R.string.action_about
|
||||
R.id.backup_fragment -> R.string.backup_restore_title
|
||||
else -> R.id.action_settings
|
||||
}
|
||||
return getString(idRes)
|
||||
|
@ -68,24 +76,47 @@ class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback {
|
|||
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)
|
||||
}
|
||||
|
||||
override fun getPermissionsToRequest(): Array<String> {
|
||||
return if (VersionUtils.hasS()) {
|
||||
arrayOf(BLUETOOTH_CONNECT)
|
||||
} else {
|
||||
arrayOf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(dialog: MaterialDialog, color: Int) {
|
||||
ThemeStore.editTheme(this).accentColor(color).commit()
|
||||
if (VersionUtils.hasNougatMR())
|
||||
DynamicShortcutManager(this).updateDynamicShortcuts()
|
||||
restart()
|
||||
}
|
||||
|
||||
override fun onThemeValuesChanged() {
|
||||
restart()
|
||||
}
|
||||
|
||||
private fun restart() {
|
||||
val savedInstanceState = Bundle().apply {
|
||||
onSaveInstanceState(this)
|
||||
}
|
||||
finish()
|
||||
val intent = Intent(this, this::class.java).putExtra(TAG, savedInstanceState)
|
||||
startActivity(intent)
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = SettingsActivity::class.java.simpleName
|
||||
}
|
||||
}
|
||||
|
||||
interface OnThemeChangedListener {
|
||||
fun onThemeValuesChanged()
|
||||
}
|
||||
|
|
|
@ -18,23 +18,24 @@ import android.content.res.ColorStateList
|
|||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore.Images.Media
|
||||
import android.view.MenuItem
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.drawToBitmap
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityShareInstagramBinding
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.setLightStatusBar
|
||||
import code.name.monkey.retromusic.extensions.setStatusBarColor
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.Share
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.activity_share_instagram.*
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2020-02-02.
|
||||
|
@ -42,6 +43,8 @@ import kotlinx.android.synthetic.main.activity_share_instagram.*
|
|||
|
||||
class ShareInstagramStory : AbsBaseActivity() {
|
||||
|
||||
private lateinit var binding: ActivityShareInstagramBinding
|
||||
|
||||
companion object {
|
||||
const val EXTRA_SONG = "extra_song"
|
||||
}
|
||||
|
@ -55,61 +58,60 @@ class ShareInstagramStory : AbsBaseActivity() {
|
|||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_share_instagram)
|
||||
setStatusbarColor(Color.TRANSPARENT)
|
||||
setNavigationbarColor(Color.BLACK)
|
||||
binding = ActivityShareInstagramBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setStatusBarColor(Color.TRANSPARENT)
|
||||
|
||||
toolbar.setBackgroundColor(Color.TRANSPARENT)
|
||||
setSupportActionBar(toolbar)
|
||||
binding.toolbar.setBackgroundColor(Color.TRANSPARENT)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
|
||||
val song = intent.extras?.getParcelable<Song>(EXTRA_SONG)
|
||||
song?.let { songFinal ->
|
||||
SongGlideRequest.Builder.from(Glide.with(this), songFinal)
|
||||
.checkIgnoreMediaStore(this@ShareInstagramStory)
|
||||
.generatePalette(this@ShareInstagramStory)
|
||||
.build()
|
||||
.into(object : RetroMusicColoredTarget(image) {
|
||||
GlideApp.with(this)
|
||||
.asBitmapPalette()
|
||||
.songCoverOptions(songFinal)
|
||||
.load(RetroGlideExtension.getSongModel(songFinal))
|
||||
.into(object : RetroMusicColoredTarget(binding.image) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
val isColorLight = ColorUtil.isColorLight(colors.backgroundColor)
|
||||
setColors(isColorLight, colors.backgroundColor)
|
||||
}
|
||||
})
|
||||
|
||||
shareTitle.text = songFinal.title
|
||||
shareText.text = songFinal.artistName
|
||||
shareButton.setOnClickListener {
|
||||
binding.shareTitle.text = songFinal.title
|
||||
binding.shareText.text = songFinal.artistName
|
||||
binding.shareButton.setOnClickListener {
|
||||
val path: String = Media.insertImage(
|
||||
contentResolver,
|
||||
mainContent.drawToBitmap(Bitmap.Config.ARGB_8888),
|
||||
binding.mainContent.drawToBitmap(Bitmap.Config.ARGB_8888),
|
||||
"Design", null
|
||||
)
|
||||
val uri = Uri.parse(path)
|
||||
Share.shareStoryToSocial(
|
||||
this@ShareInstagramStory,
|
||||
uri
|
||||
path.toUri()
|
||||
)
|
||||
}
|
||||
}
|
||||
shareButton.setTextColor(
|
||||
binding.shareButton.setTextColor(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this,
|
||||
ColorUtil.isColorLight(ThemeStore.accentColor(this))
|
||||
ColorUtil.isColorLight(accentColor())
|
||||
)
|
||||
)
|
||||
shareButton.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
binding.shareButton.backgroundTintList =
|
||||
ColorStateList.valueOf(accentColor())
|
||||
}
|
||||
|
||||
private fun setColors(colorLight: Boolean, color: Int) {
|
||||
setLightStatusbar(colorLight)
|
||||
toolbar.setTitleTextColor(
|
||||
setLightStatusBar(colorLight)
|
||||
binding.toolbar.setTitleTextColor(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this@ShareInstagramStory,
|
||||
colorLight
|
||||
)
|
||||
)
|
||||
toolbar.navigationIcon?.setTintList(
|
||||
binding.toolbar.navigationIcon?.setTintList(
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this@ShareInstagramStory,
|
||||
|
@ -117,7 +119,7 @@ class ShareInstagramStory : AbsBaseActivity() {
|
|||
)
|
||||
)
|
||||
)
|
||||
mainContent.background =
|
||||
binding.mainContent.background =
|
||||
GradientDrawable(
|
||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
intArrayOf(color, Color.BLACK)
|
||||
|
|
|
@ -1,229 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hemanth Savarla.
|
||||
*
|
||||
* 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.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
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.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.Constants.USER_BANNER
|
||||
import code.name.monkey.retromusic.Constants.USER_PROFILE
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||
import code.name.monkey.retromusic.glide.ProfileBannerGlideRequest
|
||||
import code.name.monkey.retromusic.glide.UserProfileGlideRequest
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
||||
import com.github.dhaval2404.imagepicker.constant.ImageProvider
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import kotlinx.android.synthetic.main.activity_user_info.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class UserInfoActivity : AbsBaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_user_info)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
applyToolbar(toolbar)
|
||||
|
||||
nameContainer.accentColor()
|
||||
name.setText(PreferenceUtil.userName)
|
||||
|
||||
userImage.setOnClickListener {
|
||||
pickNewPhoto()
|
||||
}
|
||||
|
||||
bannerImage.setOnClickListener {
|
||||
selectBannerImage()
|
||||
}
|
||||
|
||||
next.setOnClickListener {
|
||||
val nameString = name.text.toString().trim { it <= ' ' }
|
||||
if (TextUtils.isEmpty(nameString)) {
|
||||
Toast.makeText(this, "Umm you're name can't be empty!", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
PreferenceUtil.userName = nameString
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
|
||||
val textColor =
|
||||
MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor()))
|
||||
next.backgroundTintList = ColorStateList.valueOf(accentColor())
|
||||
next.iconTint = ColorStateList.valueOf(textColor)
|
||||
next.setTextColor(textColor)
|
||||
loadProfile()
|
||||
}
|
||||
|
||||
private fun loadProfile() {
|
||||
bannerImage?.let {
|
||||
ProfileBannerGlideRequest.Builder.from(
|
||||
Glide.with(this),
|
||||
ProfileBannerGlideRequest.getBannerModel()
|
||||
).build().into(it)
|
||||
}
|
||||
UserProfileGlideRequest.Builder.from(
|
||||
Glide.with(this),
|
||||
UserProfileGlideRequest.getUserModel()
|
||||
).build().into(userImage)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun selectBannerImage() {
|
||||
ImagePicker.with(this)
|
||||
.compress(1440)
|
||||
.provider(ImageProvider.GALLERY)
|
||||
.crop(16f, 9f)
|
||||
.start(PICK_BANNER_REQUEST)
|
||||
}
|
||||
|
||||
private fun pickNewPhoto() {
|
||||
ImagePicker.with(this)
|
||||
.provider(ImageProvider.GALLERY)
|
||||
.cropSquare()
|
||||
.compress(1440)
|
||||
.start(PICK_IMAGE_REQUEST)
|
||||
}
|
||||
|
||||
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == Activity.RESULT_OK && requestCode == PICK_IMAGE_REQUEST) {
|
||||
val fileUri = data?.data
|
||||
fileUri?.let { setAndSaveUserImage(it) }
|
||||
} else if (resultCode == Activity.RESULT_OK && requestCode == PICK_BANNER_REQUEST) {
|
||||
val fileUri = data?.data
|
||||
fileUri?.let { setAndSaveBannerImage(it) }
|
||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
||||
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(this, "Task Cancelled", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setAndSaveBannerImage(fileUri: Uri) {
|
||||
Glide.with(this)
|
||||
.load(fileUri)
|
||||
.asBitmap()
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.listener(object : RequestListener<Any, Bitmap> {
|
||||
override fun onException(
|
||||
e: java.lang.Exception?,
|
||||
model: Any?,
|
||||
target: Target<Bitmap>?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap?,
|
||||
model: Any?,
|
||||
target: Target<Bitmap>?,
|
||||
isFromMemoryCache: Boolean,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
resource?.let { saveImage(it, USER_BANNER) }
|
||||
return false
|
||||
}
|
||||
})
|
||||
.into(bannerImage)
|
||||
}
|
||||
|
||||
private fun saveImage(bitmap: Bitmap, fileName: String) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val appDir = applicationContext.filesDir
|
||||
val file = File(appDir, fileName)
|
||||
var successful = false
|
||||
try {
|
||||
val os = BufferedOutputStream(FileOutputStream(file))
|
||||
successful = ImageUtil.resizeBitmap(bitmap, 2048)
|
||||
.compress(Bitmap.CompressFormat.WEBP, 100, os)
|
||||
withContext(Dispatchers.IO) { os.close() }
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
if (successful) {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(this@UserInfoActivity, "Updated", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setAndSaveUserImage(fileUri: Uri) {
|
||||
Glide.with(this)
|
||||
.load(fileUri)
|
||||
.asBitmap()
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.listener(object : RequestListener<Any, Bitmap> {
|
||||
override fun onException(
|
||||
e: java.lang.Exception?,
|
||||
model: Any?,
|
||||
target: Target<Bitmap>?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap?,
|
||||
model: Any?,
|
||||
target: Target<Bitmap>?,
|
||||
isFromMemoryCache: Boolean,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
resource?.let { saveImage(it, USER_PROFILE) }
|
||||
return false
|
||||
}
|
||||
})
|
||||
.into(userImage)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PICK_IMAGE_REQUEST = 9002
|
||||
private const val PICK_BANNER_REQUEST = 9004
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.webkit.WebView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import code.name.monkey.appthemehelper.ThemeStore;
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil;
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil;
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper;
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
|
||||
public class WhatsNewActivity extends AbsBaseActivity {
|
||||
|
||||
private static String colorToCSS(int color) {
|
||||
return String.format(
|
||||
Locale.getDefault(),
|
||||
"rgba(%d, %d, %d, %d)",
|
||||
Color.red(color),
|
||||
Color.green(color),
|
||||
Color.blue(color),
|
||||
Color.alpha(color)); // on API 29, WebView doesn't load with hex colors
|
||||
}
|
||||
|
||||
private static void setChangelogRead(@NonNull Context context) {
|
||||
try {
|
||||
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
int currentVersion = pInfo.versionCode;
|
||||
PreferenceUtil.INSTANCE.setLastVersion(currentVersion);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
setDrawUnderStatusBar();
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_whats_new);
|
||||
setStatusbarColorAuto();
|
||||
setNavigationbarColorAuto();
|
||||
setTaskDescriptionColorAuto();
|
||||
|
||||
WebView webView = findViewById(R.id.webView);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
toolbar.setBackgroundColor(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface));
|
||||
toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar);
|
||||
|
||||
try {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
InputStream json = getAssets().open("retro-changelog.html");
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(json, StandardCharsets.UTF_8));
|
||||
String str;
|
||||
while ((str = in.readLine()) != null) {
|
||||
buf.append(str);
|
||||
}
|
||||
in.close();
|
||||
|
||||
// Inject color values for WebView body background and links
|
||||
final boolean isDark = ATHUtil.INSTANCE.isWindowBackgroundDark(this);
|
||||
final int accentColor = ThemeStore.Companion.accentColor(this);
|
||||
final String backgroundColor =
|
||||
colorToCSS(
|
||||
ATHUtil.INSTANCE.resolveColor(
|
||||
this, R.attr.colorSurface, Color.parseColor(isDark ? "#424242" : "#ffffff")));
|
||||
final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000"));
|
||||
final String textColor = colorToCSS(Color.parseColor(isDark ? "#60FFFFFF" : "#80000000"));
|
||||
final String accentColorString = colorToCSS(ThemeStore.Companion.accentColor(this));
|
||||
final String accentTextColor =
|
||||
colorToCSS(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this, ColorUtil.INSTANCE.isColorLight(accentColor)));
|
||||
final String changeLog =
|
||||
buf.toString()
|
||||
.replace(
|
||||
"{style-placeholder}",
|
||||
String.format(
|
||||
"body { background-color: %s; color: %s; } li {color: %s;} .colorHeader {background-color: %s; color: %s;} .tag {color: %s;}",
|
||||
backgroundColor,
|
||||
contentColor,
|
||||
textColor,
|
||||
accentColorString,
|
||||
accentTextColor,
|
||||
accentColorString))
|
||||
.replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this)))
|
||||
.replace(
|
||||
"{link-color-active}",
|
||||
colorToCSS(
|
||||
ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this))));
|
||||
webView.loadData(changeLog, "text/html", "UTF-8");
|
||||
} catch (Throwable e) {
|
||||
webView.loadData(
|
||||
"<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", "UTF-8");
|
||||
}
|
||||
setChangelogRead(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil.isWindowBackgroundDark
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil.isColorLight
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil.lightenColor
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper.getPrimaryTextColor
|
||||
import code.name.monkey.retromusic.BuildConfig
|
||||
import code.name.monkey.retromusic.Constants
|
||||
import code.name.monkey.retromusic.databinding.FragmentWhatsNewBinding
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.openUrl
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil.lastVersion
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.*
|
||||
|
||||
class WhatsNewFragment : BottomSheetDialogFragment() {
|
||||
private var _binding: FragmentWhatsNewBinding? = null
|
||||
val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentWhatsNewBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
try {
|
||||
val buf = StringBuilder()
|
||||
val stream= requireContext().assets.open("retro-changelog.html")
|
||||
stream.reader(StandardCharsets.UTF_8).buffered().use { br ->
|
||||
var str: String?
|
||||
while (br.readLine().also { str = it } != null) {
|
||||
buf.append(str)
|
||||
}
|
||||
}
|
||||
|
||||
// Inject color values for WebView body background and links
|
||||
val isDark = isWindowBackgroundDark(requireContext())
|
||||
val accentColor = accentColor()
|
||||
binding.webView.setBackgroundColor(0)
|
||||
val contentColor = colorToCSS(Color.parseColor(if (isDark) "#ffffff" else "#000000"))
|
||||
val textColor = colorToCSS(Color.parseColor(if (isDark) "#60FFFFFF" else "#80000000"))
|
||||
val accentColorString = colorToCSS(accentColor())
|
||||
val cardBackgroundColor =
|
||||
colorToCSS(Color.parseColor(if (isDark) "#353535" else "#ffffff"))
|
||||
val accentTextColor = colorToCSS(
|
||||
getPrimaryTextColor(
|
||||
requireContext(), isColorLight(accentColor)
|
||||
)
|
||||
)
|
||||
val changeLog = buf.toString()
|
||||
.replace(
|
||||
"{style-placeholder}",
|
||||
"body { color: $contentColor; } li {color: $textColor;} h3 {color: $accentColorString;} .tag {background-color: $accentColorString; color: $accentTextColor; } div{background-color: $cardBackgroundColor;}"
|
||||
)
|
||||
.replace("{link-color}", colorToCSS(accentColor()))
|
||||
.replace(
|
||||
"{link-color-active}",
|
||||
colorToCSS(
|
||||
lightenColor(accentColor())
|
||||
)
|
||||
)
|
||||
binding.webView.loadData(changeLog, "text/html", "UTF-8")
|
||||
} catch (e: Throwable) {
|
||||
binding.webView.loadData(
|
||||
"<h1>Unable to load</h1><p>" + e.localizedMessage + "</p>", "text/html", "UTF-8"
|
||||
)
|
||||
}
|
||||
setChangelogRead(requireContext())
|
||||
binding.tgFab.setOnClickListener {
|
||||
openUrl(Constants.TELEGRAM_CHANGE_LOG)
|
||||
}
|
||||
binding.tgFab.accentColor()
|
||||
binding.tgFab.shrink()
|
||||
binding.container.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, oldScrollY: Int ->
|
||||
val dy = scrollY - oldScrollY
|
||||
if (dy > 0) {
|
||||
binding.tgFab.shrink()
|
||||
} else if (dy < 0) {
|
||||
binding.tgFab.extend()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val TAG = "WhatsNewFragment"
|
||||
private fun colorToCSS(color: Int): String {
|
||||
return String.format(
|
||||
Locale.getDefault(),
|
||||
"rgba(%d, %d, %d, %d)",
|
||||
Color.red(color),
|
||||
Color.green(color),
|
||||
Color.blue(color),
|
||||
Color.alpha(color)
|
||||
) // on API 29, WebView doesn't load with hex colors
|
||||
}
|
||||
|
||||
private fun setChangelogRead(context: Context) {
|
||||
try {
|
||||
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||
val currentVersion = PackageInfoCompat.getLongVersionCode(pInfo)
|
||||
lastVersion = currentVersion
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun showChangeLog(activity: FragmentActivity) {
|
||||
val pInfo = activity.packageManager.getPackageInfo(activity.packageName, 0)
|
||||
val currentVersion = PackageInfoCompat.getLongVersionCode(pInfo)
|
||||
if (currentVersion > lastVersion && !BuildConfig.DEBUG) {
|
||||
val changelogBottomSheet = WhatsNewFragment()
|
||||
changelogBottomSheet.show(activity.supportFragmentManager, TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,16 +17,21 @@ package code.name.monkey.retromusic.activities.base
|
|||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Rect
|
||||
import android.media.AudioManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.EditText
|
||||
import androidx.core.app.ActivityCompat
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import androidx.core.content.getSystemService
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
abstract class AbsBaseActivity : AbsThemeActivity() {
|
||||
|
@ -57,19 +62,12 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
|
|||
permissionDeniedMessage = null
|
||||
}
|
||||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
if (!hasPermissions()) {
|
||||
// requestPermissions()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val hasPermissions = hasPermissions()
|
||||
if (hasPermissions != hadPermissions) {
|
||||
hadPermissions = hasPermissions
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (VersionUtils.hasMarshmallow()) {
|
||||
onHasPermissionsChanged(hasPermissions)
|
||||
}
|
||||
}
|
||||
|
@ -92,17 +90,15 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
|
|||
}
|
||||
|
||||
protected open fun requestPermissions() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
requestPermissions(permissions, PERMISSION_REQUEST)
|
||||
}
|
||||
ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST)
|
||||
}
|
||||
|
||||
protected fun hasPermissions(): Boolean {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
for (permission in permissions) {
|
||||
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
return false
|
||||
}
|
||||
for (permission in permissions) {
|
||||
if (ActivityCompat.checkSelfPermission(this,
|
||||
permission) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
@ -111,7 +107,7 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
|
|||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray
|
||||
grantResults: IntArray,
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == PERMISSION_REQUEST) {
|
||||
|
@ -128,24 +124,41 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
|
|||
Snackbar.LENGTH_INDEFINITE
|
||||
)
|
||||
.setAction(R.string.action_grant) { requestPermissions() }
|
||||
.setActionTextColor(ThemeStore.accentColor(this)).show()
|
||||
.setActionTextColor(accentColor()).show()
|
||||
} else if (ActivityCompat.shouldShowRequestPermissionRationale(
|
||||
this@AbsBaseActivity, Manifest.permission.BLUETOOTH_CONNECT
|
||||
)
|
||||
) {
|
||||
// User has deny from permission dialog
|
||||
Snackbar.make(
|
||||
snackBarContainer,
|
||||
R.string.permission_bluetooth_denied,
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
)
|
||||
.setAction(R.string.action_grant) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
arrayOf(Manifest.permission.BLUETOOTH_CONNECT),
|
||||
PERMISSION_REQUEST)
|
||||
}
|
||||
.setActionTextColor(accentColor()).show()
|
||||
} else {
|
||||
// User has deny permission and checked never show permission dialog so you can redirect to Application settings page
|
||||
Snackbar.make(
|
||||
snackBarContainer,
|
||||
permissionDeniedMessage!!,
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
).setAction(R.string.action_settings) {
|
||||
val intent = Intent()
|
||||
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
val uri = Uri.fromParts(
|
||||
"package",
|
||||
this@AbsBaseActivity.packageName,
|
||||
null
|
||||
)
|
||||
intent.data = uri
|
||||
startActivity(intent)
|
||||
}.setActionTextColor(ThemeStore.accentColor(this)).show()
|
||||
)
|
||||
.setAction(R.string.action_settings) {
|
||||
val intent = Intent()
|
||||
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
val uri = Uri.fromParts(
|
||||
"package",
|
||||
this@AbsBaseActivity.packageName,
|
||||
null
|
||||
)
|
||||
intent.data = uri
|
||||
startActivity(intent)
|
||||
}.setActionTextColor(accentColor()).show()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -158,4 +171,23 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
|
|||
companion object {
|
||||
const val PERMISSION_REQUEST = 100
|
||||
}
|
||||
|
||||
// this lets keyboard close when clicked in background
|
||||
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
||||
if (event.action == MotionEvent.ACTION_DOWN) {
|
||||
val v = currentFocus
|
||||
if (v is EditText) {
|
||||
val outRect = Rect()
|
||||
v.getGlobalVisibleRect(outRect)
|
||||
if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
|
||||
v.clearFocus()
|
||||
getSystemService<InputMethodManager>()?.hideSoftInputFromWindow(
|
||||
v.windowToken,
|
||||
0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.dispatchTouchEvent(event)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.os.Bundle
|
||||
import code.name.monkey.retromusic.cast.CastHelper
|
||||
import code.name.monkey.retromusic.cast.RetroSessionManagerListener
|
||||
import code.name.monkey.retromusic.cast.RetroWebServer
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import com.google.android.gms.cast.framework.CastContext
|
||||
import com.google.android.gms.cast.framework.CastSession
|
||||
import com.google.android.gms.cast.framework.SessionManager
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
|
||||
abstract class AbsCastActivity : AbsSlidingMusicPanelActivity() {
|
||||
|
||||
private var mCastSession: CastSession? = null
|
||||
private lateinit var sessionManager: SessionManager
|
||||
private val webServer: RetroWebServer by inject()
|
||||
|
||||
private var playServicesAvailable: Boolean = false
|
||||
|
||||
private val sessionManagerListener by lazy {
|
||||
object : RetroSessionManagerListener {
|
||||
override fun onSessionStarting(castSession: CastSession) {
|
||||
webServer.start()
|
||||
}
|
||||
|
||||
override fun onSessionStarted(castSession: CastSession, p1: String) {
|
||||
invalidateOptionsMenu()
|
||||
mCastSession = castSession
|
||||
loadCastQueue()
|
||||
MusicPlayerRemote.isCasting = true
|
||||
setAllowDragging(false)
|
||||
collapsePanel()
|
||||
}
|
||||
|
||||
override fun onSessionEnding(castSession: CastSession) {
|
||||
MusicPlayerRemote.isCasting = false
|
||||
castSession.remoteMediaClient?.let {
|
||||
val position = it.mediaQueue.indexOfItemWithId(it.currentItem?.itemId ?: 0)
|
||||
val progress = it.approximateStreamPosition
|
||||
MusicPlayerRemote.position = position
|
||||
MusicPlayerRemote.seekTo(progress.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSessionEnded(castSession: CastSession, p1: Int) {
|
||||
invalidateOptionsMenu()
|
||||
if (mCastSession == castSession) {
|
||||
mCastSession = null
|
||||
}
|
||||
setAllowDragging(true)
|
||||
webServer.stop()
|
||||
}
|
||||
|
||||
override fun onSessionResumed(castSession: CastSession, p1: Boolean) {
|
||||
invalidateOptionsMenu()
|
||||
mCastSession = castSession
|
||||
webServer.start()
|
||||
mCastSession?.remoteMediaClient?.let {
|
||||
loadCastQueue(it.mediaQueue.indexOfItemWithId(it.currentItem?.itemId ?: 0),
|
||||
it.approximateStreamPosition)
|
||||
}
|
||||
|
||||
MusicPlayerRemote.isCasting = true
|
||||
setAllowDragging(false)
|
||||
collapsePanel()
|
||||
}
|
||||
|
||||
override fun onSessionSuspended(castSession: CastSession, p1: Int) {
|
||||
invalidateOptionsMenu()
|
||||
if (mCastSession == castSession) {
|
||||
mCastSession = null
|
||||
}
|
||||
MusicPlayerRemote.isCasting = false
|
||||
setAllowDragging(true)
|
||||
webServer.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
playServicesAvailable = try {
|
||||
GoogleApiAvailability
|
||||
.getInstance().isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
if (playServicesAvailable) {
|
||||
setupCast()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupCast() {
|
||||
sessionManager = CastContext.getSharedInstance(this).sessionManager
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (playServicesAvailable) {
|
||||
sessionManager.addSessionManagerListener(
|
||||
sessionManagerListener,
|
||||
CastSession::class.java
|
||||
)
|
||||
if (mCastSession == null) {
|
||||
mCastSession = sessionManager.currentCastSession
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (playServicesAvailable) {
|
||||
sessionManager.removeSessionManagerListener(
|
||||
sessionManagerListener,
|
||||
CastSession::class.java
|
||||
)
|
||||
mCastSession = null
|
||||
}
|
||||
}
|
||||
|
||||
fun loadCastQueue(
|
||||
position: Int = MusicPlayerRemote.position,
|
||||
progress: Long = MusicPlayerRemote.songProgressMillis.toLong(),
|
||||
) {
|
||||
mCastSession?.let {
|
||||
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||
CastHelper.castQueue(
|
||||
it,
|
||||
MusicPlayerRemote.playingQueue,
|
||||
position,
|
||||
progress
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
super.onQueueChanged()
|
||||
if (playServicesAvailable) {
|
||||
loadCastQueue()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,26 +15,28 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.Manifest
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.ServiceConnection
|
||||
import android.content.*
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.db.toPlayCount
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
||||
import code.name.monkey.retromusic.repository.RealRepository
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.FAVORITE_STATE_CHANGED
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.MEDIA_STORE_CHANGED
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.META_CHANGED
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.PLAY_STATE_CHANGED
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.QUEUE_CHANGED
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.REPEAT_MODE_CHANGED
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.SHUFFLE_MODE_CHANGED
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.android.ext.android.inject
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventListener {
|
||||
|
||||
|
@ -123,7 +125,10 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
|||
if (entity != null) {
|
||||
repository.updateHistorySong(MusicPlayerRemote.currentSong)
|
||||
} else {
|
||||
repository.addSongToHistory(MusicPlayerRemote.currentSong)
|
||||
// Check whether pause history option is ON or OFF
|
||||
if (!PreferenceUtil.pauseHistory) {
|
||||
repository.addSongToHistory(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
}
|
||||
val songs = repository.checkSongExistInPlayCount(MusicPlayerRemote.currentSong.id)
|
||||
if (songs.isNotEmpty()) {
|
||||
|
@ -166,6 +171,12 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
|||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteStateChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onFavoriteStateChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHasPermissionsChanged(hasPermissions: Boolean) {
|
||||
super.onHasPermissionsChanged(hasPermissions)
|
||||
val intent = Intent(MEDIA_STORE_CHANGED)
|
||||
|
@ -178,11 +189,11 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
|||
}
|
||||
|
||||
override fun getPermissionsToRequest(): Array<String> {
|
||||
return arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
Manifest.permission.BLUETOOTH
|
||||
)
|
||||
return mutableListOf(Manifest.permission.READ_EXTERNAL_STORAGE).apply {
|
||||
if (!VersionUtils.hasQ()) {
|
||||
add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
private class MusicStateReceiver(activity: AbsMusicServiceActivity) : BroadcastReceiver() {
|
||||
|
@ -194,7 +205,8 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
|||
val activity = reference.get()
|
||||
if (activity != null && action != null) {
|
||||
when (action) {
|
||||
FAVORITE_STATE_CHANGED, META_CHANGED -> activity.onPlayingMetaChanged()
|
||||
FAVORITE_STATE_CHANGED -> activity.onFavoriteStateChanged()
|
||||
META_CHANGED -> activity.onPlayingMetaChanged()
|
||||
QUEUE_CHANGED -> activity.onQueueChanged()
|
||||
PLAY_STATE_CHANGED -> activity.onPlayStateChanged()
|
||||
REPEAT_MODE_CHANGED -> activity.onRepeatModeChanged()
|
||||
|
|
|
@ -14,27 +14,29 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver
|
||||
import android.view.animation.PathInterpolator
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.view.*
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.commit
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.RetroBottomSheetBehavior
|
||||
import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding
|
||||
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.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.other.MiniPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.adaptive.AdaptiveFragment
|
||||
import code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.card.CardFragment
|
||||
|
@ -47,58 +49,85 @@ import code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment
|
|||
import code.name.monkey.retromusic.fragments.player.full.FullPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.gradient.GradientPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.material.MaterialFragment
|
||||
import code.name.monkey.retromusic.fragments.player.md3.MD3PlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.peak.PeakPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.peek.PeekPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.plain.PlainPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.simple.SimplePlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.tiny.TinyPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.queue.PlayingQueueFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.CategoryInfo
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.views.BottomNavigationBarTinted
|
||||
import code.name.monkey.retromusic.util.ViewUtil
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.*
|
||||
import kotlinx.android.synthetic.main.sliding_music_panel_layout.*
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
|
||||
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||
companion object {
|
||||
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
|
||||
}
|
||||
|
||||
var fromNotification = false
|
||||
private var windowInsets: WindowInsetsCompat? = null
|
||||
protected val libraryViewModel by viewModel<LibraryViewModel>()
|
||||
private lateinit var bottomSheetBehavior: RetroBottomSheetBehavior<FrameLayout>
|
||||
private lateinit var bottomSheetBehavior: BottomSheetBehavior<FrameLayout>
|
||||
private var playerFragment: AbsPlayerFragment? = null
|
||||
private var miniPlayerFragment: MiniPlayerFragment? = null
|
||||
private var nowPlayingScreen: NowPlayingScreen? = null
|
||||
private var navigationBarColor: Int = 0
|
||||
private var taskColor: Int = 0
|
||||
private var lightStatusBar: Boolean = false
|
||||
private var lightNavigationBar: Boolean = false
|
||||
private var paletteColor: Int = Color.WHITE
|
||||
protected abstract fun createContentView(): View
|
||||
private var navigationBarColor = 0
|
||||
protected abstract fun createContentView(): SlidingMusicPanelLayoutBinding
|
||||
private val panelState: Int
|
||||
get() = bottomSheetBehavior.state
|
||||
private lateinit var binding: SlidingMusicPanelLayoutBinding
|
||||
private var isInOneTabMode = false
|
||||
|
||||
private var navigationBarColorAnimator: ValueAnimator? = null
|
||||
private val argbEvaluator: ArgbEvaluator = ArgbEvaluator()
|
||||
|
||||
private val bottomSheetCallbackList = object : BottomSheetCallback() {
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
setMiniPlayerAlphaProgress(slideOffset)
|
||||
dimBackground.show()
|
||||
dimBackground.alpha = slideOffset
|
||||
navigationBarColorAnimator?.cancel()
|
||||
setNavigationBarColorPreOreo(
|
||||
argbEvaluator.evaluate(
|
||||
slideOffset,
|
||||
surfaceColor(),
|
||||
navigationBarColor
|
||||
) as Int
|
||||
)
|
||||
}
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
when (newState) {
|
||||
STATE_EXPANDED -> {
|
||||
onPanelExpanded()
|
||||
if (PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics) {
|
||||
keepScreenOn(true)
|
||||
}
|
||||
}
|
||||
STATE_COLLAPSED -> {
|
||||
onPanelCollapsed()
|
||||
dimBackground.hide()
|
||||
if ((PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics) || !PreferenceUtil.isScreenOnEnabled) {
|
||||
keepScreenOn(false)
|
||||
}
|
||||
}
|
||||
STATE_SETTLING, STATE_DRAGGING -> {
|
||||
if (fromNotification) {
|
||||
binding.bottomNavigationView.bringToFront()
|
||||
fromNotification = false
|
||||
}
|
||||
}
|
||||
STATE_HIDDEN -> {
|
||||
MusicPlayerRemote.clearQueue()
|
||||
}
|
||||
|
||||
else -> {
|
||||
println("Do something")
|
||||
println("Do a flip")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -108,23 +137,31 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(createContentView())
|
||||
binding = createContentView()
|
||||
setContentView(binding.root)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _, insets ->
|
||||
windowInsets = insets
|
||||
insets
|
||||
}
|
||||
chooseFragmentForTheme()
|
||||
setupSlidingUpPanel()
|
||||
setupBottomSheet()
|
||||
updateColor()
|
||||
|
||||
val themeColor = resolveColor(android.R.attr.windowBackground, Color.GRAY)
|
||||
dimBackground.setBackgroundColor(ColorUtil.withAlpha(themeColor, 0.5f))
|
||||
dimBackground.setOnClickListener {
|
||||
println("dimBackground")
|
||||
collapsePanel()
|
||||
if (!PreferenceUtil.materialYou) {
|
||||
binding.slidingPanel.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
|
||||
bottomNavigationView.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
|
||||
}
|
||||
|
||||
navigationBarColor = surfaceColor()
|
||||
}
|
||||
|
||||
private fun setupBottomSheet() {
|
||||
bottomSheetBehavior = from(slidingPanel) as RetroBottomSheetBehavior
|
||||
bottomSheetBehavior = from(binding.slidingPanel)
|
||||
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList)
|
||||
bottomSheetBehavior.isHideable = PreferenceUtil.swipeDownToDismiss
|
||||
setMiniPlayerAlphaProgress(0F)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -142,55 +179,69 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallbackList)
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
protected fun wrapSlidingMusicPanel(): View {
|
||||
val slidingMusicPanelLayout =
|
||||
layoutInflater.inflate(R.layout.sliding_music_panel_layout, null)
|
||||
val contentContainer: ViewGroup =
|
||||
slidingMusicPanelLayout.findViewById(R.id.mainContentFrame)
|
||||
layoutInflater.inflate(R.layout.activity_main_content, contentContainer)
|
||||
return slidingMusicPanelLayout
|
||||
protected fun wrapSlidingMusicPanel(): SlidingMusicPanelLayoutBinding {
|
||||
return SlidingMusicPanelLayoutBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
fun collapsePanel() {
|
||||
bottomSheetBehavior.state = STATE_COLLAPSED
|
||||
setMiniPlayerAlphaProgress(0f)
|
||||
}
|
||||
|
||||
fun expandPanel() {
|
||||
bottomSheetBehavior.state = STATE_EXPANDED
|
||||
setMiniPlayerAlphaProgress(1f)
|
||||
}
|
||||
|
||||
private fun setMiniPlayerAlphaProgress(progress: Float) {
|
||||
if (progress < 0) return
|
||||
val alpha = 1 - progress
|
||||
miniPlayerFragment?.view?.alpha = alpha
|
||||
miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
|
||||
bottomNavigationView.translationY = progress * 500
|
||||
bottomNavigationView.alpha = alpha
|
||||
miniPlayerFragment?.view?.alpha = 1 - (progress / 0.2F)
|
||||
miniPlayerFragment?.view?.isGone = alpha == 0f
|
||||
binding.bottomNavigationView.translationY = progress * 500
|
||||
binding.bottomNavigationView.alpha = alpha
|
||||
binding.playerFragmentContainer.alpha = (progress - 0.2F) / 0.2F
|
||||
}
|
||||
|
||||
private fun animateNavigationBarColor(color: Int) {
|
||||
if (VersionUtils.hasOreo()) return
|
||||
navigationBarColorAnimator?.cancel()
|
||||
navigationBarColorAnimator = ValueAnimator
|
||||
.ofArgb(window.navigationBarColor, color).apply {
|
||||
duration = ViewUtil.RETRO_MUSIC_ANIM_TIME.toLong()
|
||||
interpolator = PathInterpolator(0.4f, 0f, 1f, 1f)
|
||||
addUpdateListener { animation: ValueAnimator ->
|
||||
setNavigationBarColorPreOreo(
|
||||
animation.animatedValue as Int
|
||||
)
|
||||
}
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
open fun onPanelCollapsed() {
|
||||
setMiniPlayerAlphaProgress(0F)
|
||||
// restore values
|
||||
super.setLightStatusbar(lightStatusBar)
|
||||
super.setTaskDescriptionColor(taskColor)
|
||||
super.setNavigationbarColor(navigationBarColor)
|
||||
super.setLightNavigationBar(lightNavigationBar)
|
||||
animateNavigationBarColor(surfaceColor())
|
||||
setLightStatusBarAuto()
|
||||
setLightNavigationBarAuto()
|
||||
setTaskDescriptionColor(taskColor)
|
||||
playerFragment?.onHide()
|
||||
}
|
||||
|
||||
open fun onPanelExpanded() {
|
||||
setMiniPlayerAlphaProgress(1F)
|
||||
onPaletteColorChanged()
|
||||
playerFragment?.onShow()
|
||||
}
|
||||
|
||||
private fun setupSlidingUpPanel() {
|
||||
slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||
binding.slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||
ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
if (nowPlayingScreen != Peak) {
|
||||
val params = slidingPanel.layoutParams as ViewGroup.LayoutParams
|
||||
params.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
slidingPanel.layoutParams = params
|
||||
binding.slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
if (nowPlayingScreen != Peek) {
|
||||
binding.slidingPanel.updateLayoutParams<ViewGroup.LayoutParams> {
|
||||
height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
}
|
||||
}
|
||||
when (panelState) {
|
||||
STATE_EXPANDED -> onPanelExpanded()
|
||||
|
@ -203,26 +254,30 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
})
|
||||
}
|
||||
|
||||
fun getBottomNavigationView(): BottomNavigationBarTinted {
|
||||
return bottomNavigationView
|
||||
}
|
||||
val bottomNavigationView get() = binding.bottomNavigationView
|
||||
|
||||
val slidingPanel get() = binding.slidingPanel
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||
slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||
binding.slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||
ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
hideBottomBar(false)
|
||||
binding.slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
hideBottomSheet(false)
|
||||
}
|
||||
})
|
||||
} // don't call hideBottomBar(true) here as it causes a bug with the SlidingUpPanelLayout
|
||||
} // don't call hideBottomSheet(true) here as it causes a bug with the SlidingUpPanelLayout
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
super.onQueueChanged()
|
||||
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
// Mini player should be hidden in Playing Queue
|
||||
// it may pop up if hideBottomSheet is called
|
||||
if (currentFragment(R.id.fragment_container) !is PlayingQueueFragment) {
|
||||
hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
|
@ -235,127 +290,153 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
collapsePanel()
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun onPaletteColorChanged() {
|
||||
if (panelState == STATE_EXPANDED) {
|
||||
super.setTaskDescriptionColor(paletteColor)
|
||||
val isColorLight = ColorUtil.isColorLight(paletteColor)
|
||||
if (PreferenceUtil.isAdaptiveColor && (nowPlayingScreen == Normal || nowPlayingScreen == Flat)) {
|
||||
super.setLightNavigationBar(true)
|
||||
super.setLightStatusbar(isColorLight)
|
||||
navigationBarColor = surfaceColor()
|
||||
setTaskDescColor(paletteColor)
|
||||
val isColorLight = paletteColor.isColorLight
|
||||
if (PreferenceUtil.isAdaptiveColor && (nowPlayingScreen == Normal || nowPlayingScreen == Flat || nowPlayingScreen == Material)) {
|
||||
setLightNavigationBar(true)
|
||||
setLightStatusBar(isColorLight)
|
||||
} else if (nowPlayingScreen == Card || nowPlayingScreen == Blur || nowPlayingScreen == BlurCard) {
|
||||
super.setLightStatusbar(false)
|
||||
super.setLightNavigationBar(true)
|
||||
super.setNavigationbarColor(Color.BLACK)
|
||||
animateNavigationBarColor(Color.BLACK)
|
||||
navigationBarColor = Color.BLACK
|
||||
setLightStatusBar(false)
|
||||
setLightNavigationBar(true)
|
||||
} else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) {
|
||||
super.setNavigationbarColor(paletteColor)
|
||||
super.setLightNavigationBar(isColorLight)
|
||||
super.setLightStatusbar(isColorLight)
|
||||
animateNavigationBarColor(paletteColor)
|
||||
navigationBarColor = paletteColor
|
||||
setLightNavigationBar(isColorLight)
|
||||
setLightStatusBar(isColorLight)
|
||||
} else if (nowPlayingScreen == Full) {
|
||||
super.setNavigationbarColor(paletteColor)
|
||||
super.setLightNavigationBar(isColorLight)
|
||||
super.setLightStatusbar(false)
|
||||
animateNavigationBarColor(paletteColor)
|
||||
navigationBarColor = paletteColor
|
||||
setLightNavigationBar(isColorLight)
|
||||
setLightStatusBar(false)
|
||||
} else if (nowPlayingScreen == Classic) {
|
||||
super.setLightStatusbar(false)
|
||||
setLightStatusBar(false)
|
||||
} else if (nowPlayingScreen == Fit) {
|
||||
super.setLightStatusbar(false)
|
||||
} else {
|
||||
super.setLightStatusbar(
|
||||
ColorUtil.isColorLight(
|
||||
ATHUtil.resolveColor(
|
||||
this,
|
||||
android.R.attr.windowBackground
|
||||
)
|
||||
)
|
||||
)
|
||||
super.setLightNavigationBar(true)
|
||||
setLightStatusBar(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setLightStatusbar(enabled: Boolean) {
|
||||
lightStatusBar = enabled
|
||||
if (panelState == STATE_COLLAPSED) {
|
||||
super.setLightStatusbar(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setLightNavigationBar(enabled: Boolean) {
|
||||
lightNavigationBar = enabled
|
||||
if (panelState == STATE_COLLAPSED) {
|
||||
super.setLightNavigationBar(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setNavigationbarColor(color: Int) {
|
||||
navigationBarColor = color
|
||||
if (panelState == STATE_COLLAPSED) {
|
||||
super.setNavigationbarColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setTaskDescriptionColor(color: Int) {
|
||||
private fun setTaskDescColor(color: Int) {
|
||||
taskColor = color
|
||||
if (panelState == STATE_COLLAPSED) {
|
||||
super.setTaskDescriptionColor(color)
|
||||
setTaskDescriptionColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateTabs() {
|
||||
bottomNavigationView.menu.clear()
|
||||
binding.bottomNavigationView.menu.clear()
|
||||
val currentTabs: List<CategoryInfo> = PreferenceUtil.libraryCategory
|
||||
for (tab in currentTabs) {
|
||||
if (tab.visible) {
|
||||
val menu = tab.category
|
||||
bottomNavigationView.menu.add(0, menu.id, 0, menu.stringRes).setIcon(menu.icon)
|
||||
binding.bottomNavigationView.menu.add(0, menu.id, 0, menu.stringRes)
|
||||
.setIcon(menu.icon)
|
||||
}
|
||||
}
|
||||
if (bottomNavigationView.menu.size() == 1) {
|
||||
bottomNavigationView.hide()
|
||||
if (binding.bottomNavigationView.menu.size() == 1) {
|
||||
isInOneTabMode = true
|
||||
binding.bottomNavigationView.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateColor() {
|
||||
libraryViewModel.paletteColor.observe(this, { color ->
|
||||
libraryViewModel.paletteColor.observe(this) { color ->
|
||||
this.paletteColor = color
|
||||
onPaletteColorChanged()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun setBottomBarVisibility(visible: Boolean) {
|
||||
bottomNavigationView.isVisible = visible
|
||||
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
fun setBottomNavVisibility(
|
||||
visible: Boolean,
|
||||
animate: Boolean = false,
|
||||
hideBottomSheet: Boolean = MusicPlayerRemote.playingQueue.isEmpty(),
|
||||
) {
|
||||
if (isInOneTabMode) {
|
||||
hideBottomSheet(
|
||||
hide = hideBottomSheet,
|
||||
animate = animate,
|
||||
isBottomNavVisible = false
|
||||
)
|
||||
return
|
||||
}
|
||||
val mAnimate = animate && bottomSheetBehavior.state == STATE_COLLAPSED
|
||||
if (mAnimate) {
|
||||
if (visible) {
|
||||
binding.bottomNavigationView.bringToFront()
|
||||
binding.bottomNavigationView.show()
|
||||
} else {
|
||||
binding.bottomNavigationView.hide()
|
||||
}
|
||||
} else {
|
||||
binding.bottomNavigationView.isVisible = false
|
||||
if (visible && bottomSheetBehavior.state != STATE_EXPANDED) {
|
||||
binding.bottomNavigationView.bringToFront()
|
||||
}
|
||||
}
|
||||
hideBottomSheet(
|
||||
hide = hideBottomSheet,
|
||||
animate = animate,
|
||||
isBottomNavVisible = visible
|
||||
)
|
||||
}
|
||||
|
||||
private fun hideBottomBar(hide: Boolean) {
|
||||
val heightOfBar = dip(R.dimen.mini_player_height)
|
||||
val heightOfBarWithTabs = heightOfBar * 2
|
||||
val isVisible = bottomNavigationView.isVisible
|
||||
fun hideBottomSheet(
|
||||
hide: Boolean,
|
||||
animate: Boolean = false,
|
||||
isBottomNavVisible: Boolean = bottomNavigationView.isVisible,
|
||||
) {
|
||||
val heightOfBar =
|
||||
windowInsets.safeGetBottomInsets() +
|
||||
if (MusicPlayerRemote.isCasting) dip(R.dimen.cast_mini_player_height) else dip(R.dimen.mini_player_height)
|
||||
val heightOfBarWithTabs = heightOfBar + dip(R.dimen.bottom_nav_height)
|
||||
if (hide) {
|
||||
bottomSheetBehavior.isHideable = true
|
||||
bottomSheetBehavior.peekHeight = 0
|
||||
ViewCompat.setElevation(slidingPanel, 0f)
|
||||
ViewCompat.setElevation(bottomNavigationView, 10f)
|
||||
collapsePanel()
|
||||
bottomSheetBehavior.peekHeight = -windowInsets.safeGetBottomInsets()
|
||||
bottomSheetBehavior.state = STATE_COLLAPSED
|
||||
libraryViewModel.setFabMargin(
|
||||
this,
|
||||
if (isBottomNavVisible) dip(R.dimen.bottom_nav_height) else 0
|
||||
)
|
||||
} else {
|
||||
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||
bottomSheetBehavior.isHideable = false
|
||||
ViewCompat.setElevation(slidingPanel, 10f)
|
||||
ViewCompat.setElevation(bottomNavigationView, 10f)
|
||||
if (isVisible) {
|
||||
binding.slidingPanel.elevation = 0F
|
||||
binding.bottomNavigationView.elevation = 5F
|
||||
if (isBottomNavVisible) {
|
||||
println("List")
|
||||
bottomSheetBehavior.peekHeight = heightOfBarWithTabs - 22
|
||||
if (animate) {
|
||||
bottomSheetBehavior.peekHeightAnimate(heightOfBarWithTabs)
|
||||
} else {
|
||||
bottomSheetBehavior.peekHeight = heightOfBarWithTabs
|
||||
}
|
||||
libraryViewModel.setFabMargin(this, dip(R.dimen.mini_player_height_expanded))
|
||||
} else {
|
||||
println("Details")
|
||||
bottomSheetBehavior.peekHeight = heightOfBar
|
||||
if (animate) {
|
||||
bottomSheetBehavior.peekHeightAnimate(heightOfBar).doOnEnd {
|
||||
binding.slidingPanel.bringToFront()
|
||||
}
|
||||
} else {
|
||||
bottomSheetBehavior.peekHeight = heightOfBar
|
||||
binding.slidingPanel.bringToFront()
|
||||
}
|
||||
libraryViewModel.setFabMargin(this, dip(R.dimen.mini_player_height))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setAllowDragging(allowDragging: Boolean) {
|
||||
bottomSheetBehavior.isDraggable = allowDragging
|
||||
hideBottomSheet(false)
|
||||
}
|
||||
|
||||
private fun chooseFragmentForTheme() {
|
||||
nowPlayingScreen = PreferenceUtil.nowPlayingScreen
|
||||
|
||||
|
@ -374,9 +455,10 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
Color -> ColorFragment()
|
||||
Gradient -> GradientPlayerFragment()
|
||||
Tiny -> TinyPlayerFragment()
|
||||
Peak -> PeakPlayerFragment()
|
||||
Peek -> PeekPlayerFragment()
|
||||
Circle -> CirclePlayerFragment()
|
||||
Classic -> ClassicPlayerFragment()
|
||||
MD3 -> MD3PlayerFragment()
|
||||
else -> PlayerFragment()
|
||||
} // must implement AbsPlayerFragment
|
||||
supportFragmentManager.commit {
|
||||
|
|
|
@ -15,52 +15,54 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.content.res.Resources
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
|
||||
import code.name.monkey.appthemehelper.ATH
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialDialogsUtil
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.LanguageContextWrapper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.theme.ThemeManager
|
||||
import code.name.monkey.retromusic.util.theme.getNightMode
|
||||
import code.name.monkey.retromusic.util.theme.getThemeResValue
|
||||
import com.google.android.play.core.splitcompat.SplitCompat
|
||||
import java.util.*
|
||||
|
||||
abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
||||
|
||||
private val handler = Handler()
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
updateTheme()
|
||||
hideStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setImmersiveFullscreen()
|
||||
setEdgeToEdgeOrImmersive()
|
||||
registerSystemUiVisibility()
|
||||
toggleScreenOn()
|
||||
MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this)
|
||||
setLightNavigationBarAuto()
|
||||
setLightStatusBarAuto(surfaceColor())
|
||||
if (VersionUtils.hasQ()) {
|
||||
window.decorView.isForceDarkAllowed = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTheme() {
|
||||
setTheme(ThemeManager.getThemeResValue(this))
|
||||
setDefaultNightMode(ThemeManager.getNightMode(this))
|
||||
}
|
||||
setTheme(getThemeResValue())
|
||||
if (PreferenceUtil.materialYou) {
|
||||
setDefaultNightMode(getNightMode())
|
||||
}
|
||||
|
||||
private fun toggleScreenOn() {
|
||||
if (PreferenceUtil.isScreenOnEnabled) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
if (PreferenceUtil.isCustomFont) {
|
||||
setTheme(R.style.FontThemeOverlay)
|
||||
}
|
||||
if (PreferenceUtil.circlePlayButton) {
|
||||
setTheme(R.style.CircleFABOverlay)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,92 +77,6 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
fun hideStatusBar() {
|
||||
hideStatusBar(PreferenceUtil.isFullScreenMode)
|
||||
}
|
||||
|
||||
private fun hideStatusBar(fullscreen: Boolean) {
|
||||
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
|
||||
if (statusBar != null) {
|
||||
statusBar.visibility = if (fullscreen) View.GONE else View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
fun setDrawUnderStatusBar() {
|
||||
RetroUtil.setAllowDrawUnderStatusBar(window)
|
||||
}
|
||||
|
||||
fun setDrawUnderNavigationBar() {
|
||||
RetroUtil.setAllowDrawUnderNavigationBar(window)
|
||||
}
|
||||
|
||||
/**
|
||||
* This will set the color of the view with the id "status_bar" on KitKat and Lollipop. On
|
||||
* Lollipop if no such view is found it will set the statusbar color using the native method.
|
||||
*
|
||||
* @param color the new statusbar color (will be shifted down on Lollipop and above)
|
||||
*/
|
||||
fun setStatusbarColor(color: Int) {
|
||||
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
|
||||
if (statusBar != null) {
|
||||
when {
|
||||
VersionUtils.hasMarshmallow() -> statusBar.setBackgroundColor(color)
|
||||
VersionUtils.hasLollipop() -> statusBar.setBackgroundColor(
|
||||
ColorUtil.darkenColor(
|
||||
color
|
||||
)
|
||||
)
|
||||
else -> statusBar.setBackgroundColor(color)
|
||||
}
|
||||
} else {
|
||||
when {
|
||||
VersionUtils.hasMarshmallow() -> window.statusBarColor = color
|
||||
else -> window.statusBarColor = ColorUtil.darkenColor(color)
|
||||
}
|
||||
}
|
||||
setLightStatusbarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
}
|
||||
|
||||
fun setStatusbarColorAuto() {
|
||||
// we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat
|
||||
setStatusbarColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
setLightStatusbarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
}
|
||||
|
||||
open fun setTaskDescriptionColor(@ColorInt color: Int) {
|
||||
ATH.setTaskDescriptionColor(this, color)
|
||||
}
|
||||
|
||||
fun setTaskDescriptionColorAuto() {
|
||||
setTaskDescriptionColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
}
|
||||
|
||||
open fun setNavigationbarColor(color: Int) {
|
||||
if (ThemeStore.coloredNavigationBar(this)) {
|
||||
ATH.setNavigationbarColor(this, color)
|
||||
} else {
|
||||
ATH.setNavigationbarColor(this, Color.BLACK)
|
||||
}
|
||||
}
|
||||
|
||||
fun setNavigationbarColorAuto() {
|
||||
setNavigationbarColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
}
|
||||
|
||||
open fun setLightStatusbar(enabled: Boolean) {
|
||||
ATH.setLightStatusbar(this, enabled)
|
||||
}
|
||||
|
||||
fun setLightStatusbarAuto(bgColor: Int) {
|
||||
setLightStatusbar(ColorUtil.isColorLight(bgColor))
|
||||
}
|
||||
|
||||
open fun setLightNavigationBar(enabled: Boolean) {
|
||||
if (!ATHUtil.isWindowBackgroundDark(this) and ThemeStore.coloredNavigationBar(this)) {
|
||||
ATH.setLightNavigationbar(this, enabled)
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerSystemUiVisibility() {
|
||||
val decorView = window.decorView
|
||||
decorView.setOnSystemUiVisibilityChangeListener { visibility ->
|
||||
|
@ -175,19 +91,6 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
|||
decorView.setOnSystemUiVisibilityChangeListener(null)
|
||||
}
|
||||
|
||||
private fun setImmersiveFullscreen() {
|
||||
val flags =
|
||||
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
|
||||
|
||||
if (PreferenceUtil.isFullScreenMode) {
|
||||
window.decorView.systemUiVisibility = flags
|
||||
}
|
||||
}
|
||||
|
||||
private fun exitFullscreen() {
|
||||
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
setImmersiveFullscreen()
|
||||
}
|
||||
|
@ -213,8 +116,13 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
|||
|
||||
override fun attachBaseContext(newBase: Context?) {
|
||||
val code = PreferenceUtil.languageCode
|
||||
if (code != "auto") {
|
||||
super.attachBaseContext(LanguageContextWrapper.wrap(newBase, Locale(code)))
|
||||
} else super.attachBaseContext(newBase)
|
||||
val locale = if (code == "auto") {
|
||||
// Get the device default locale
|
||||
ConfigurationCompat.getLocales(Resources.getSystem().configuration)[0]
|
||||
} else {
|
||||
Locale.forLanguageTag(code)
|
||||
}
|
||||
super.attachBaseContext(LanguageContextWrapper.wrap(newBase, locale))
|
||||
SplitCompat.install(this)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,23 +14,17 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.activities.bugreport
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.MenuItem
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringDef
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
|
@ -41,18 +35,21 @@ import code.name.monkey.retromusic.activities.bugreport.model.Report
|
|||
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubLogin
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubTarget
|
||||
import code.name.monkey.retromusic.misc.DialogAsyncTask
|
||||
import code.name.monkey.retromusic.databinding.ActivityBugReportBinding
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
||||
import code.name.monkey.retromusic.extensions.showToast
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import java.io.IOException
|
||||
import kotlinx.android.synthetic.main.activity_bug_report.*
|
||||
import kotlinx.android.synthetic.main.bug_report_card_device_info.*
|
||||
import kotlinx.android.synthetic.main.bug_report_card_report.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.eclipse.egit.github.core.Issue
|
||||
import org.eclipse.egit.github.core.client.GitHubClient
|
||||
import org.eclipse.egit.github.core.client.RequestException
|
||||
import org.eclipse.egit.github.core.service.IssueService
|
||||
import java.io.IOException
|
||||
|
||||
private const val RESULT_SUCCESS = "RESULT_OK"
|
||||
private const val RESULT_BAD_CREDENTIALS = "RESULT_BAD_CREDENTIALS"
|
||||
|
@ -72,65 +69,62 @@ private annotation class Result
|
|||
|
||||
open class BugReportActivity : AbsThemeActivity() {
|
||||
|
||||
private lateinit var binding: ActivityBugReportBinding
|
||||
private var deviceInfo: DeviceInfo? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_bug_report)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
binding = ActivityBugReportBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setTaskDescriptionColorAuto()
|
||||
|
||||
initViews()
|
||||
|
||||
if (TextUtils.isEmpty(title)) setTitle(R.string.report_an_issue)
|
||||
if (title.isNullOrEmpty()) setTitle(R.string.report_an_issue)
|
||||
|
||||
deviceInfo = DeviceInfo(this)
|
||||
airTextDeviceInfo.text = deviceInfo.toString()
|
||||
binding.cardDeviceInfo.airTextDeviceInfo.text = deviceInfo.toString()
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
val accentColor = ThemeStore.accentColor(this)
|
||||
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
|
||||
toolbar.setBackgroundColor(primaryColor)
|
||||
setSupportActionBar(toolbar)
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
||||
val accentColor = accentColor()
|
||||
setSupportActionBar(binding.toolbar)
|
||||
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
TintHelper.setTintAuto(optionUseAccount, accentColor, false)
|
||||
optionUseAccount?.setOnClickListener {
|
||||
inputTitle.isEnabled = true
|
||||
inputDescription.isEnabled = true
|
||||
inputUsername.isEnabled = true
|
||||
inputPassword.isEnabled = true
|
||||
TintHelper.setTintAuto(binding.cardReport.optionUseAccount, accentColor, false)
|
||||
binding.cardReport.optionUseAccount.setOnClickListener {
|
||||
binding.cardReport.inputTitle.isEnabled = true
|
||||
binding.cardReport.inputDescription.isEnabled = true
|
||||
binding.cardReport.inputUsername.isEnabled = true
|
||||
binding.cardReport.inputPassword.isEnabled = true
|
||||
|
||||
optionAnonymous.isChecked = false
|
||||
sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||
binding.cardReport.optionAnonymous.isChecked = false
|
||||
binding.sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||
override fun onHidden(fab: FloatingActionButton?) {
|
||||
super.onHidden(fab)
|
||||
sendFab.setImageResource(R.drawable.ic_send)
|
||||
sendFab.show()
|
||||
binding.sendFab.setImageResource(R.drawable.ic_send)
|
||||
binding.sendFab.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
TintHelper.setTintAuto(optionAnonymous, accentColor, false)
|
||||
optionAnonymous.setOnClickListener {
|
||||
inputTitle.isEnabled = false
|
||||
inputDescription.isEnabled = false
|
||||
inputUsername.isEnabled = false
|
||||
inputPassword.isEnabled = false
|
||||
TintHelper.setTintAuto(binding.cardReport.optionAnonymous, accentColor, false)
|
||||
binding.cardReport.optionAnonymous.setOnClickListener {
|
||||
binding.cardReport.inputTitle.isEnabled = false
|
||||
binding.cardReport.inputDescription.isEnabled = false
|
||||
binding.cardReport.inputUsername.isEnabled = false
|
||||
binding.cardReport.inputPassword.isEnabled = false
|
||||
|
||||
optionUseAccount.isChecked = false
|
||||
sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||
binding.cardReport.optionUseAccount.isChecked = false
|
||||
binding.sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||
override fun onHidden(fab: FloatingActionButton?) {
|
||||
super.onHidden(fab)
|
||||
sendFab.setImageResource(R.drawable.ic_open_in_browser)
|
||||
sendFab.show()
|
||||
binding.sendFab.setImageResource(R.drawable.ic_open_in_browser)
|
||||
binding.sendFab.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
inputPassword.setOnEditorActionListener { _, actionId, _ ->
|
||||
binding.cardReport.inputPassword.setOnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
||||
reportIssue()
|
||||
return@setOnEditorActionListener true
|
||||
|
@ -138,75 +132,71 @@ open class BugReportActivity : AbsThemeActivity() {
|
|||
false
|
||||
}
|
||||
|
||||
airTextDeviceInfo.setOnClickListener { copyDeviceInfoToClipBoard() }
|
||||
binding.cardDeviceInfo.airTextDeviceInfo.setOnClickListener { copyDeviceInfoToClipBoard() }
|
||||
|
||||
TintHelper.setTintAuto(sendFab, accentColor, true)
|
||||
sendFab.setOnClickListener { reportIssue() }
|
||||
TintHelper.setTintAuto(binding.sendFab, accentColor, true)
|
||||
binding.sendFab.setOnClickListener { reportIssue() }
|
||||
|
||||
MaterialUtil.setTint(inputLayoutTitle, false)
|
||||
MaterialUtil.setTint(inputLayoutDescription, false)
|
||||
MaterialUtil.setTint(inputLayoutUsername, false)
|
||||
MaterialUtil.setTint(inputLayoutPassword, false)
|
||||
MaterialUtil.setTint(binding.cardReport.inputLayoutTitle, false)
|
||||
MaterialUtil.setTint(binding.cardReport.inputLayoutDescription, false)
|
||||
MaterialUtil.setTint(binding.cardReport.inputLayoutUsername, false)
|
||||
MaterialUtil.setTint(binding.cardReport.inputLayoutPassword, false)
|
||||
}
|
||||
|
||||
private fun reportIssue() {
|
||||
if (optionUseAccount.isChecked) {
|
||||
if (binding.cardReport.optionUseAccount.isChecked) {
|
||||
if (!validateInput()) return
|
||||
val username = inputUsername.text.toString()
|
||||
val password = inputPassword.text.toString()
|
||||
val username = binding.cardReport.inputUsername.text.toString()
|
||||
val password = binding.cardReport.inputPassword.text.toString()
|
||||
sendBugReport(GithubLogin(username, password))
|
||||
} else {
|
||||
copyDeviceInfoToClipBoard()
|
||||
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
i.data = Uri.parse(ISSUE_TRACKER_LINK)
|
||||
i.data = ISSUE_TRACKER_LINK.toUri()
|
||||
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(i)
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyDeviceInfoToClipBoard() {
|
||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clipboard = getSystemService<ClipboardManager>()
|
||||
val clip = ClipData.newPlainText(getString(R.string.device_info), deviceInfo?.toMarkdown())
|
||||
clipboard.setPrimaryClip(clip)
|
||||
Toast.makeText(
|
||||
this@BugReportActivity,
|
||||
R.string.copied_device_info_to_clipboard,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
clipboard?.setPrimaryClip(clip)
|
||||
showToast(R.string.copied_device_info_to_clipboard)
|
||||
}
|
||||
|
||||
private fun validateInput(): Boolean {
|
||||
var hasErrors = false
|
||||
|
||||
if (optionUseAccount.isChecked) {
|
||||
if (TextUtils.isEmpty(inputUsername.text)) {
|
||||
setError(inputLayoutUsername, R.string.bug_report_no_username)
|
||||
if (binding.cardReport.optionUseAccount.isChecked) {
|
||||
if (binding.cardReport.inputUsername.text.isNullOrEmpty()) {
|
||||
setError(binding.cardReport.inputLayoutUsername, R.string.bug_report_no_username)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutUsername)
|
||||
removeError(binding.cardReport.inputLayoutUsername)
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(inputPassword.text)) {
|
||||
setError(inputLayoutPassword, R.string.bug_report_no_password)
|
||||
if (binding.cardReport.inputPassword.text.isNullOrEmpty()) {
|
||||
setError(binding.cardReport.inputLayoutPassword, R.string.bug_report_no_password)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutPassword)
|
||||
removeError(binding.cardReport.inputLayoutPassword)
|
||||
}
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(inputTitle.text)) {
|
||||
setError(inputLayoutTitle, R.string.bug_report_no_title)
|
||||
if (binding.cardReport.inputTitle.text.isNullOrEmpty()) {
|
||||
setError(binding.cardReport.inputLayoutTitle, R.string.bug_report_no_title)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutTitle)
|
||||
removeError(binding.cardReport.inputLayoutTitle)
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(inputDescription.text)) {
|
||||
setError(inputLayoutDescription, R.string.bug_report_no_description)
|
||||
if (binding.cardReport.inputDescription.text.isNullOrEmpty()) {
|
||||
setError(binding.cardReport.inputLayoutDescription, R.string.bug_report_no_description)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutDescription)
|
||||
removeError(binding.cardReport.inputLayoutDescription)
|
||||
}
|
||||
|
||||
return !hasErrors
|
||||
|
@ -223,16 +213,16 @@ open class BugReportActivity : AbsThemeActivity() {
|
|||
private fun sendBugReport(login: GithubLogin) {
|
||||
if (!validateInput()) return
|
||||
|
||||
val bugTitle = inputTitle.text.toString()
|
||||
val bugDescription = inputDescription.text.toString()
|
||||
val bugTitle = binding.cardReport.inputTitle.text.toString()
|
||||
val bugDescription = binding.cardReport.inputDescription.text.toString()
|
||||
|
||||
val extraInfo = ExtraInfo()
|
||||
onSaveExtraInfo()
|
||||
|
||||
val report = Report(bugTitle, bugDescription, deviceInfo, extraInfo)
|
||||
val target = GithubTarget("h4h13", "RetroMusicPlayer")
|
||||
val target = GithubTarget("RetroMusicPlayer", "RetroMusicPlayer")
|
||||
|
||||
ReportIssueAsyncTask.report(this, report, target, login)
|
||||
reportIssue(report, target, login)
|
||||
}
|
||||
|
||||
private fun onSaveExtraInfo() {}
|
||||
|
@ -244,92 +234,75 @@ open class BugReportActivity : AbsThemeActivity() {
|
|||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private class ReportIssueAsyncTask private constructor(
|
||||
activity: Activity,
|
||||
private val report: Report,
|
||||
private val target: GithubTarget,
|
||||
private val login: GithubLogin
|
||||
) : DialogAsyncTask<Void, Void, String>(activity) {
|
||||
|
||||
override fun createDialog(context: Context): Dialog {
|
||||
return AlertDialog.Builder(context).show()
|
||||
private fun reportIssue(
|
||||
report: Report,
|
||||
target: GithubTarget,
|
||||
login: GithubLogin
|
||||
) {
|
||||
val client: GitHubClient = if (login.shouldUseApiToken()) {
|
||||
GitHubClient().setOAuth2Token(login.apiToken)
|
||||
} else {
|
||||
GitHubClient().setCredentials(login.username, login.password)
|
||||
}
|
||||
|
||||
@Result
|
||||
override fun doInBackground(vararg params: Void): String {
|
||||
val client: GitHubClient = if (login.shouldUseApiToken()) {
|
||||
GitHubClient().setOAuth2Token(login.apiToken)
|
||||
} else {
|
||||
GitHubClient().setCredentials(login.username, login.password)
|
||||
}
|
||||
val issue = Issue().setTitle(report.title).setBody(report.getDescription())
|
||||
|
||||
val issue = Issue().setTitle(report.title).setBody(report.description)
|
||||
try {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val result = try {
|
||||
IssueService(client).createIssue(target.username, target.repository, issue)
|
||||
return RESULT_SUCCESS
|
||||
RESULT_SUCCESS
|
||||
} catch (e: RequestException) {
|
||||
return when (e.status) {
|
||||
when (e.status) {
|
||||
STATUS_BAD_CREDENTIALS -> {
|
||||
if (login.shouldUseApiToken()) RESULT_INVALID_TOKEN else RESULT_BAD_CREDENTIALS
|
||||
}
|
||||
STATUS_ISSUES_NOT_ENABLED -> RESULT_ISSUES_NOT_ENABLED
|
||||
else -> {
|
||||
e.printStackTrace()
|
||||
RESULT_UNKNOWN
|
||||
throw e
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
return RESULT_UNKNOWN
|
||||
RESULT_UNKNOWN
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
val activity = this@BugReportActivity
|
||||
when (result) {
|
||||
RESULT_SUCCESS -> MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.bug_report_success)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> tryToFinishActivity() }
|
||||
.show()
|
||||
RESULT_BAD_CREDENTIALS -> MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_wrong_credentials)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
RESULT_INVALID_TOKEN -> MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_invalid_token)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
RESULT_ISSUES_NOT_ENABLED -> MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_issues_not_available)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
else -> MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_unknown)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> tryToFinishActivity() }
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> tryToFinishActivity() }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPostExecute(@Result result: String) {
|
||||
super.onPostExecute(result)
|
||||
|
||||
val context = context ?: return
|
||||
|
||||
when (result) {
|
||||
RESULT_SUCCESS -> tryToFinishActivity()
|
||||
RESULT_BAD_CREDENTIALS -> MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_wrong_credentials)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
RESULT_INVALID_TOKEN -> MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_invalid_token)
|
||||
.setPositiveButton(android.R.string.ok, null).show()
|
||||
RESULT_ISSUES_NOT_ENABLED -> MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_issues_not_available)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
|
||||
else -> MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_unknown)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> tryToFinishActivity() }
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> tryToFinishActivity() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryToFinishActivity() {
|
||||
val context = context
|
||||
if (context is Activity && !context.isFinishing) {
|
||||
context.finish()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun report(
|
||||
activity: Activity,
|
||||
report: Report,
|
||||
target: GithubTarget,
|
||||
login: GithubLogin
|
||||
) {
|
||||
ReportIssueAsyncTask(activity, report, target, login).execute()
|
||||
}
|
||||
private fun tryToFinishActivity() {
|
||||
if (!isFinishing) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -337,6 +310,7 @@ open class BugReportActivity : AbsThemeActivity() {
|
|||
|
||||
private const val STATUS_BAD_CREDENTIALS = 401
|
||||
private const val STATUS_ISSUES_NOT_ENABLED = 410
|
||||
private const val ISSUE_TRACKER_LINK = "https://github.com/h4h13/RetroMusicPlayer"
|
||||
private const val ISSUE_TRACKER_LINK =
|
||||
"https://github.com/RetroMusicPlayer/RetroMusicPlayer"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.IntRange;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
|
||||
public class DeviceInfo {
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@SuppressWarnings("deprecation")
|
||||
private final String[] abis =
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
|
||||
? Build.SUPPORTED_ABIS
|
||||
: new String[] {Build.CPU_ABI, Build.CPU_ABI2};
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private final String[] abis32Bits =
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? Build.SUPPORTED_32_BIT_ABIS : null;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private final String[] abis64Bits =
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? Build.SUPPORTED_64_BIT_ABIS : null;
|
||||
|
||||
private final String baseTheme;
|
||||
|
||||
private final String brand = Build.BRAND;
|
||||
|
||||
private final String buildID = Build.DISPLAY;
|
||||
|
||||
private final String buildVersion = Build.VERSION.INCREMENTAL;
|
||||
|
||||
private final String device = Build.DEVICE;
|
||||
|
||||
private final String hardware = Build.HARDWARE;
|
||||
|
||||
private final boolean isAdaptive;
|
||||
|
||||
private final String manufacturer = Build.MANUFACTURER;
|
||||
|
||||
private final String model = Build.MODEL;
|
||||
|
||||
private final String nowPlayingTheme;
|
||||
|
||||
private final String product = Build.PRODUCT;
|
||||
|
||||
private final String releaseVersion = Build.VERSION.RELEASE;
|
||||
|
||||
@IntRange(from = 0)
|
||||
private final int sdkVersion = Build.VERSION.SDK_INT;
|
||||
|
||||
private final int versionCode;
|
||||
|
||||
private final String versionName;
|
||||
private final String selectedLang;
|
||||
|
||||
public DeviceInfo(@NotNull Context context) {
|
||||
PackageInfo packageInfo;
|
||||
try {
|
||||
packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
packageInfo = null;
|
||||
}
|
||||
if (packageInfo != null) {
|
||||
versionCode = packageInfo.versionCode;
|
||||
versionName = packageInfo.versionName;
|
||||
} else {
|
||||
versionCode = -1;
|
||||
versionName = null;
|
||||
}
|
||||
baseTheme = PreferenceUtil.INSTANCE.getBaseTheme();
|
||||
nowPlayingTheme =
|
||||
context.getString(PreferenceUtil.INSTANCE.getNowPlayingScreen().getTitleRes());
|
||||
isAdaptive = PreferenceUtil.INSTANCE.isAdaptiveColor();
|
||||
selectedLang = PreferenceUtil.INSTANCE.getLanguageCode();
|
||||
}
|
||||
|
||||
public String toMarkdown() {
|
||||
return "Device info:\n"
|
||||
+ "---\n"
|
||||
+ "<table>\n"
|
||||
+ "<tr><td><b>App version</b></td><td>"
|
||||
+ versionName
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>App version code</td><td>"
|
||||
+ versionCode
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>Android build version</td><td>"
|
||||
+ buildVersion
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>Android release version</td><td>"
|
||||
+ releaseVersion
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>Android SDK version</td><td>"
|
||||
+ sdkVersion
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>Android build ID</td><td>"
|
||||
+ buildID
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>Device brand</td><td>"
|
||||
+ brand
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>Device manufacturer</td><td>"
|
||||
+ manufacturer
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>Device name</td><td>"
|
||||
+ device
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>Device model</td><td>"
|
||||
+ model
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>Device product name</td><td>"
|
||||
+ product
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>Device hardware name</td><td>"
|
||||
+ hardware
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>ABIs</td><td>"
|
||||
+ Arrays.toString(abis)
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>ABIs (32bit)</td><td>"
|
||||
+ Arrays.toString(abis32Bits)
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>ABIs (64bit)</td><td>"
|
||||
+ Arrays.toString(abis64Bits)
|
||||
+ "</td></tr>\n"
|
||||
+ "<tr><td>Language</td><td>"
|
||||
+ selectedLang
|
||||
+ "</td></tr>\n"
|
||||
+ "</table>\n";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "App version: "
|
||||
+ versionName
|
||||
+ "\n"
|
||||
+ "App version code: "
|
||||
+ versionCode
|
||||
+ "\n"
|
||||
+ "Android build version: "
|
||||
+ buildVersion
|
||||
+ "\n"
|
||||
+ "Android release version: "
|
||||
+ releaseVersion
|
||||
+ "\n"
|
||||
+ "Android SDK version: "
|
||||
+ sdkVersion
|
||||
+ "\n"
|
||||
+ "Android build ID: "
|
||||
+ buildID
|
||||
+ "\n"
|
||||
+ "Device brand: "
|
||||
+ brand
|
||||
+ "\n"
|
||||
+ "Device manufacturer: "
|
||||
+ manufacturer
|
||||
+ "\n"
|
||||
+ "Device name: "
|
||||
+ device
|
||||
+ "\n"
|
||||
+ "Device model: "
|
||||
+ model
|
||||
+ "\n"
|
||||
+ "Device product name: "
|
||||
+ product
|
||||
+ "\n"
|
||||
+ "Device hardware name: "
|
||||
+ hardware
|
||||
+ "\n"
|
||||
+ "ABIs: "
|
||||
+ Arrays.toString(abis)
|
||||
+ "\n"
|
||||
+ "ABIs (32bit): "
|
||||
+ Arrays.toString(abis32Bits)
|
||||
+ "\n"
|
||||
+ "ABIs (64bit): "
|
||||
+ Arrays.toString(abis64Bits)
|
||||
+ "\n"
|
||||
+ "Base theme: "
|
||||
+ baseTheme
|
||||
+ "\n"
|
||||
+ "Now playing theme: "
|
||||
+ nowPlayingTheme
|
||||
+ "\n"
|
||||
+ "Adaptive: "
|
||||
+ isAdaptive
|
||||
+ "\n"
|
||||
+ "System language: "
|
||||
+ Locale.getDefault().toLanguageTag()
|
||||
+ "\n"
|
||||
+ "In-App Language: "
|
||||
+ selectedLang;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import androidx.annotation.IntRange
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil.isAdaptiveColor
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil.languageCode
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil.nowPlayingScreen
|
||||
import java.util.*
|
||||
|
||||
class DeviceInfo(context: Context) {
|
||||
@SuppressLint("NewApi")
|
||||
private val abis = Build.SUPPORTED_ABIS
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private val abis32Bits = Build.SUPPORTED_32_BIT_ABIS
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private val abis64Bits = Build.SUPPORTED_64_BIT_ABIS
|
||||
private val baseTheme: String
|
||||
private val brand = Build.BRAND
|
||||
private val buildID = Build.DISPLAY
|
||||
private val buildVersion = Build.VERSION.INCREMENTAL
|
||||
private val device = Build.DEVICE
|
||||
private val hardware = Build.HARDWARE
|
||||
private val isAdaptive: Boolean
|
||||
private val manufacturer = Build.MANUFACTURER
|
||||
private val model = Build.MODEL
|
||||
private val nowPlayingTheme: String
|
||||
private val product = Build.PRODUCT
|
||||
private val releaseVersion = Build.VERSION.RELEASE
|
||||
|
||||
@IntRange(from = 0)
|
||||
private val sdkVersion = Build.VERSION.SDK_INT
|
||||
private var versionCode = 0L
|
||||
private var versionName: String? = null
|
||||
private val selectedLang: String
|
||||
fun toMarkdown(): String {
|
||||
return """
|
||||
Device info:
|
||||
---
|
||||
<table>
|
||||
<tr><td><b>App version</b></td><td>$versionName</td></tr>
|
||||
<tr><td>App version code</td><td>$versionCode</td></tr>
|
||||
<tr><td>Android build version</td><td>$buildVersion</td></tr>
|
||||
<tr><td>Android release version</td><td>$releaseVersion</td></tr>
|
||||
<tr><td>Android SDK version</td><td>$sdkVersion</td></tr>
|
||||
<tr><td>Android build ID</td><td>$buildID</td></tr>
|
||||
<tr><td>Device brand</td><td>$brand</td></tr>
|
||||
<tr><td>Device manufacturer</td><td>$manufacturer</td></tr>
|
||||
<tr><td>Device name</td><td>$device</td></tr>
|
||||
<tr><td>Device model</td><td>$model</td></tr>
|
||||
<tr><td>Device product name</td><td>$product</td></tr>
|
||||
<tr><td>Device hardware name</td><td>$hardware</td></tr>
|
||||
<tr><td>ABIs</td><td>${Arrays.toString(abis)}</td></tr>
|
||||
<tr><td>ABIs (32bit)</td><td>${Arrays.toString(abis32Bits)}</td></tr>
|
||||
<tr><td>ABIs (64bit)</td><td>${Arrays.toString(abis64Bits)}</td></tr>
|
||||
<tr><td>Language</td><td>$selectedLang</td></tr>
|
||||
</table>
|
||||
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return """
|
||||
App version: $versionName
|
||||
App version code: $versionCode
|
||||
Android build version: $buildVersion
|
||||
Android release version: $releaseVersion
|
||||
Android SDK version: $sdkVersion
|
||||
Android build ID: $buildID
|
||||
Device brand: $brand
|
||||
Device manufacturer: $manufacturer
|
||||
Device name: $device
|
||||
Device model: $model
|
||||
Device product name: $product
|
||||
Device hardware name: $hardware
|
||||
ABIs: ${Arrays.toString(abis)}
|
||||
ABIs (32bit): ${Arrays.toString(abis32Bits)}
|
||||
ABIs (64bit): ${Arrays.toString(abis64Bits)}
|
||||
Base theme: $baseTheme
|
||||
Now playing theme: $nowPlayingTheme
|
||||
Adaptive: $isAdaptive
|
||||
System language: ${Locale.getDefault().toLanguageTag()}
|
||||
In-App Language: $selectedLang
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
init {
|
||||
val packageInfo = try {
|
||||
context.packageManager.getPackageInfo(context.packageName, 0)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
null
|
||||
}
|
||||
if (packageInfo != null) {
|
||||
versionCode = PackageInfoCompat.getLongVersionCode(packageInfo)
|
||||
versionName = packageInfo.versionName
|
||||
} else {
|
||||
versionCode = -1
|
||||
versionName = null
|
||||
}
|
||||
baseTheme = PreferenceUtil.baseTheme
|
||||
nowPlayingTheme = context.getString(nowPlayingScreen.titleRes)
|
||||
isAdaptive = isAdaptiveColor
|
||||
selectedLang = languageCode
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model;
|
||||
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo;
|
||||
|
||||
public class Report {
|
||||
|
||||
private final String description;
|
||||
|
||||
private final DeviceInfo deviceInfo;
|
||||
|
||||
private final ExtraInfo extraInfo;
|
||||
|
||||
private final String title;
|
||||
|
||||
public Report(String title, String description, DeviceInfo deviceInfo, ExtraInfo extraInfo) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.deviceInfo = deviceInfo;
|
||||
this.extraInfo = extraInfo;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description
|
||||
+ "\n\n"
|
||||
+ "-\n\n"
|
||||
+ deviceInfo.toMarkdown()
|
||||
+ "\n\n"
|
||||
+ extraInfo.toMarkdown();
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model
|
||||
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo
|
||||
|
||||
class Report(
|
||||
val title: String,
|
||||
private val description: String,
|
||||
private val deviceInfo: DeviceInfo?,
|
||||
private val extraInfo: ExtraInfo
|
||||
) {
|
||||
fun getDescription(): String {
|
||||
return """
|
||||
$description
|
||||
|
||||
-
|
||||
|
||||
${deviceInfo?.toMarkdown()}
|
||||
|
||||
${extraInfo.toMarkdown()}
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ExtraInfo {
|
||||
|
||||
private final Map<String, String> extraInfo = new LinkedHashMap<>();
|
||||
|
||||
public void put(String key, String value) {
|
||||
extraInfo.put(key, value);
|
||||
}
|
||||
|
||||
public void put(String key, boolean value) {
|
||||
extraInfo.put(key, Boolean.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, double value) {
|
||||
extraInfo.put(key, Double.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, float value) {
|
||||
extraInfo.put(key, Float.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, long value) {
|
||||
extraInfo.put(key, Long.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, int value) {
|
||||
extraInfo.put(key, Integer.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, Object value) {
|
||||
extraInfo.put(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
extraInfo.remove(key);
|
||||
}
|
||||
|
||||
public String toMarkdown() {
|
||||
if (extraInfo.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
output.append("Extra info:\n" + "---\n" + "<table>\n");
|
||||
for (String key : extraInfo.keySet()) {
|
||||
output
|
||||
.append("<tr><td>")
|
||||
.append(key)
|
||||
.append("</td><td>")
|
||||
.append(extraInfo.get(key))
|
||||
.append("</td></tr>\n");
|
||||
}
|
||||
output.append("</table>\n");
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github
|
||||
|
||||
class ExtraInfo {
|
||||
private val extraInfo: MutableMap<String, String> = LinkedHashMap()
|
||||
fun put(key: String, value: String) {
|
||||
extraInfo[key] = value
|
||||
}
|
||||
|
||||
fun put(key: String, value: Boolean) {
|
||||
extraInfo[key] = value.toString()
|
||||
}
|
||||
|
||||
fun put(key: String, value: Double) {
|
||||
extraInfo[key] = value.toString()
|
||||
}
|
||||
|
||||
fun put(key: String, value: Float) {
|
||||
extraInfo[key] = value.toString()
|
||||
}
|
||||
|
||||
fun put(key: String, value: Long) {
|
||||
extraInfo[key] = value.toString()
|
||||
}
|
||||
|
||||
fun put(key: String, value: Int) {
|
||||
extraInfo[key] = value.toString()
|
||||
}
|
||||
|
||||
fun put(key: String, value: Any) {
|
||||
extraInfo[key] = value.toString()
|
||||
}
|
||||
|
||||
fun remove(key: String) {
|
||||
extraInfo.remove(key)
|
||||
}
|
||||
|
||||
fun toMarkdown(): String {
|
||||
if (extraInfo.isEmpty()) {
|
||||
return ""
|
||||
}
|
||||
val output = StringBuilder()
|
||||
output.append(
|
||||
"""
|
||||
Extra info:
|
||||
---
|
||||
<table>
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
for (key in extraInfo.keys) {
|
||||
output
|
||||
.append("<tr><td>")
|
||||
.append(key)
|
||||
.append("</td><td>")
|
||||
.append(extraInfo[key])
|
||||
.append("</td></tr>\n")
|
||||
}
|
||||
output.append("</table>\n")
|
||||
return output.toString()
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
public class GithubLogin {
|
||||
|
||||
private final String apiToken;
|
||||
|
||||
private final String password;
|
||||
|
||||
private final String username;
|
||||
|
||||
public GithubLogin(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.apiToken = null;
|
||||
}
|
||||
|
||||
public GithubLogin(String apiToken) {
|
||||
this.username = null;
|
||||
this.password = null;
|
||||
this.apiToken = apiToken;
|
||||
}
|
||||
|
||||
public String getApiToken() {
|
||||
return apiToken;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public boolean shouldUseApiToken() {
|
||||
return TextUtils.isEmpty(username) || TextUtils.isEmpty(password);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github
|
||||
|
||||
import android.text.TextUtils
|
||||
|
||||
class GithubLogin {
|
||||
val apiToken: String?
|
||||
val password: String?
|
||||
val username: String?
|
||||
|
||||
constructor(username: String?, password: String?) {
|
||||
this.username = username
|
||||
this.password = password
|
||||
apiToken = null
|
||||
}
|
||||
|
||||
constructor(apiToken: String?) {
|
||||
username = null
|
||||
password = null
|
||||
this.apiToken = apiToken
|
||||
}
|
||||
|
||||
fun shouldUseApiToken(): Boolean {
|
||||
return TextUtils.isEmpty(username) || TextUtils.isEmpty(password)
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||
|
||||
public class GithubTarget {
|
||||
|
||||
private final String repository;
|
||||
|
||||
private final String username;
|
||||
|
||||
public GithubTarget(String username, String repository) {
|
||||
this.username = username;
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public String getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github
|
||||
|
||||
class GithubTarget(val username: String, val repository: String)
|
|
@ -16,11 +16,14 @@ package code.name.monkey.retromusic.activities.saf;
|
|||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import code.name.monkey.retromusic.R;
|
||||
|
||||
import com.heinrichreimersoftware.materialintro.app.IntroActivity;
|
||||
import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
|
||||
/** Created by hemanths on 2019-07-31. */
|
||||
public class SAFGuideActivity extends IntroActivity {
|
||||
|
||||
|
|
|
@ -21,27 +21,37 @@ import android.graphics.Bitmap
|
|||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import android.widget.ImageView
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.IntentSenderRequest
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.R.drawable
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.colorButtons
|
||||
import code.name.monkey.retromusic.extensions.hideSoftKeyboard
|
||||
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
||||
import code.name.monkey.retromusic.model.ArtworkInfo
|
||||
import code.name.monkey.retromusic.model.LoadingInfo
|
||||
import code.name.monkey.retromusic.model.AudioTagInfo
|
||||
import code.name.monkey.retromusic.repository.Repository
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.SAFUtil
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.android.synthetic.main.activity_album_tag_editor.*
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jaudiotagger.audio.AudioFile
|
||||
import org.jaudiotagger.audio.AudioFileIO
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
|
@ -49,20 +59,27 @@ import org.koin.android.ext.android.inject
|
|||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||
abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
||||
abstract val editorImage: ImageView
|
||||
val repository by inject<Repository>()
|
||||
|
||||
lateinit var saveFab: MaterialButton
|
||||
protected var id: Long = 0
|
||||
private set
|
||||
private var paletteColorPrimary: Int = 0
|
||||
private var isInNoImageMode: Boolean = false
|
||||
private var songPaths: List<String>? = null
|
||||
private var savedSongPaths: List<String>? = null
|
||||
private val currentSongPath: String? = null
|
||||
private var savedTags: Map<FieldKey, String>? = null
|
||||
private var savedArtworkInfo: ArtworkInfo? = null
|
||||
protected abstract val contentViewLayout: Int
|
||||
private var _binding: VB? = null
|
||||
protected val binding: VB get() = _binding!!
|
||||
private var cacheFiles = listOf<File>()
|
||||
|
||||
abstract val bindingInflater: (LayoutInflater) -> VB
|
||||
|
||||
private lateinit var launcher: ActivityResultLauncher<IntentSenderRequest>
|
||||
|
||||
protected abstract fun loadImageFromFile(selectedFile: Uri?)
|
||||
|
||||
protected val show: AlertDialog
|
||||
|
@ -76,7 +93,9 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
2 -> deleteImage()
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.action_cancel, null)
|
||||
.show()
|
||||
.colorButtons()
|
||||
|
||||
internal val albumArtist: String?
|
||||
get() {
|
||||
|
@ -158,6 +177,15 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
protected val discNumber: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.DISC_NO)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected val lyrics: String?
|
||||
get() {
|
||||
return try {
|
||||
|
@ -187,9 +215,8 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(contentViewLayout)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
_binding = bindingInflater.invoke(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setTaskDescriptionColorAuto()
|
||||
|
||||
saveFab = findViewById(R.id.saveTags)
|
||||
|
@ -201,6 +228,11 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
finish()
|
||||
}
|
||||
setUpViews()
|
||||
launcher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
writeToFiles(getSongUris(), cacheFiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpViews() {
|
||||
|
@ -217,7 +249,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
getString(R.string.web_search),
|
||||
getString(R.string.remove_cover)
|
||||
)
|
||||
editorImage?.setOnClickListener { show }
|
||||
editorImage.setOnClickListener { show }
|
||||
}
|
||||
|
||||
private fun startImagePicker() {
|
||||
|
@ -259,6 +291,8 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
|
||||
protected abstract fun getSongPaths(): List<String>
|
||||
|
||||
protected abstract fun getSongUris(): List<Uri>
|
||||
|
||||
protected fun searchWebFor(vararg keys: String) {
|
||||
val stringBuilder = StringBuilder()
|
||||
for (key in keys) {
|
||||
|
@ -282,20 +316,6 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
protected fun setNoImageMode() {
|
||||
isInNoImageMode = true
|
||||
imageContainer?.visibility = View.GONE
|
||||
editorImage?.visibility = View.GONE
|
||||
editorImage?.isEnabled = false
|
||||
|
||||
setColors(
|
||||
intent.getIntExtra(
|
||||
EXTRA_PALETTE,
|
||||
ATHUtil.resolveColor(this, R.attr.colorPrimary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
protected fun dataChanged() {
|
||||
showFab()
|
||||
}
|
||||
|
@ -329,27 +349,57 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
fieldKeyValueMap: Map<FieldKey, String>,
|
||||
artworkInfo: ArtworkInfo?
|
||||
) {
|
||||
RetroUtil.hideSoftKeyboard(this)
|
||||
hideSoftKeyboard()
|
||||
|
||||
hideFab()
|
||||
println(fieldKeyValueMap)
|
||||
WriteTagsAsyncTask(this).execute(
|
||||
LoadingInfo(
|
||||
songPaths,
|
||||
fieldKeyValueMap,
|
||||
artworkInfo
|
||||
)
|
||||
)
|
||||
GlobalScope.launch {
|
||||
if (VersionUtils.hasR()) {
|
||||
cacheFiles = TagWriter.writeTagsToFilesR(
|
||||
this@AbsTagEditorActivity, AudioTagInfo(
|
||||
songPaths,
|
||||
fieldKeyValueMap,
|
||||
artworkInfo
|
||||
)
|
||||
)
|
||||
val pendingIntent = MediaStore.createWriteRequest(contentResolver, getSongUris())
|
||||
|
||||
launcher.launch(IntentSenderRequest.Builder(pendingIntent).build())
|
||||
} else {
|
||||
TagWriter.writeTagsToFiles(
|
||||
this@AbsTagEditorActivity, AudioTagInfo(
|
||||
songPaths,
|
||||
fieldKeyValueMap,
|
||||
artworkInfo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeTags(paths: List<String>?) {
|
||||
WriteTagsAsyncTask(this).execute(
|
||||
LoadingInfo(
|
||||
paths,
|
||||
savedTags,
|
||||
savedArtworkInfo
|
||||
)
|
||||
)
|
||||
GlobalScope.launch {
|
||||
if (VersionUtils.hasR()) {
|
||||
cacheFiles = TagWriter.writeTagsToFilesR(
|
||||
this@AbsTagEditorActivity, AudioTagInfo(
|
||||
paths,
|
||||
savedTags,
|
||||
savedArtworkInfo
|
||||
)
|
||||
)
|
||||
val pendingIntent = MediaStore.createWriteRequest(contentResolver, getSongUris())
|
||||
|
||||
launcher.launch(IntentSenderRequest.Builder(pendingIntent).build())
|
||||
} else {
|
||||
TagWriter.writeTagsToFiles(
|
||||
this@AbsTagEditorActivity, AudioTagInfo(
|
||||
paths,
|
||||
savedTags,
|
||||
savedArtworkInfo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -388,9 +438,30 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun writeToFiles(songUris: List<Uri>, cacheFiles: List<File>) {
|
||||
if (cacheFiles.size == songUris.size) {
|
||||
for (i in cacheFiles.indices) {
|
||||
contentResolver.openOutputStream(songUris[i])?.use { output ->
|
||||
cacheFiles[i].inputStream().use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
TagWriter.scan(this@AbsTagEditorActivity, getSongPaths())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
// Delete Cache Files
|
||||
cacheFiles.forEach { file ->
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val EXTRA_ID = "extra_id"
|
||||
const val EXTRA_PALETTE = "extra_palette"
|
||||
private val TAG = AbsTagEditorActivity::class.java.simpleName
|
||||
|
|
|
@ -22,33 +22,34 @@ import android.graphics.Color
|
|||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.transition.Slide
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder
|
||||
import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.model.ArtworkInfo
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import java.util.*
|
||||
import kotlinx.android.synthetic.main.activity_album_tag_editor.*
|
||||
import com.bumptech.glide.request.target.ImageViewTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import java.util.*
|
||||
|
||||
class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||
class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBinding>() {
|
||||
|
||||
override val contentViewLayout: Int
|
||||
get() = R.layout.activity_album_tag_editor
|
||||
override val bindingInflater: (LayoutInflater) -> ActivityAlbumTagEditorBinding =
|
||||
ActivityAlbumTagEditorBinding::inflate
|
||||
|
||||
private fun windowEnterTransition() {
|
||||
val slide = Slide()
|
||||
|
@ -60,54 +61,19 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
|||
window.enterTransition = slide
|
||||
}
|
||||
|
||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||
|
||||
Glide.with(this@AlbumTagEditorActivity).load(selectedFile).asBitmap()
|
||||
.transcode(BitmapPaletteTranscoder(this), BitmapPaletteWrapper::class.java)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
|
||||
.into(object : SimpleTarget<BitmapPaletteWrapper>() {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper?,
|
||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
getColor(resource?.palette, Color.TRANSPARENT)
|
||||
albumArtBitmap = resource?.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
||||
setImageBitmap(
|
||||
albumArtBitmap,
|
||||
getColor(
|
||||
resource?.palette,
|
||||
ATHUtil.resolveColor(
|
||||
this@AlbumTagEditorActivity,
|
||||
R.attr.defaultFooterColor
|
||||
)
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
dataChanged()
|
||||
setResult(Activity.RESULT_OK)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
Toast.makeText(this@AlbumTagEditorActivity, e.toString(), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private var albumArtBitmap: Bitmap? = null
|
||||
private var deleteAlbumArt: Boolean = false
|
||||
|
||||
private fun setupToolbar() {
|
||||
toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
setSupportActionBar(toolbar)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
binding.appBarLayout?.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(this)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
window.sharedElementsUseOverlay = true
|
||||
imageContainer?.transitionName = getString(R.string.transition_album_art)
|
||||
binding.imageContainer.transitionName = getString(R.string.transition_album_art)
|
||||
windowEnterTransition()
|
||||
setUpViews()
|
||||
setupToolbar()
|
||||
|
@ -116,22 +82,23 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
|||
private fun setUpViews() {
|
||||
fillViewsWithFileTags()
|
||||
|
||||
MaterialUtil.setTint(yearContainer, false)
|
||||
MaterialUtil.setTint(genreContainer, false)
|
||||
MaterialUtil.setTint(albumTitleContainer, false)
|
||||
MaterialUtil.setTint(albumArtistContainer, false)
|
||||
binding.yearContainer.setTint(false)
|
||||
binding.genreContainer.setTint(false)
|
||||
binding.albumTitleContainer.setTint(false)
|
||||
binding.albumArtistContainer.setTint(false)
|
||||
|
||||
albumText.appHandleColor().addTextChangedListener(this)
|
||||
albumArtistText.appHandleColor().addTextChangedListener(this)
|
||||
genreTitle.appHandleColor().addTextChangedListener(this)
|
||||
yearTitle.appHandleColor().addTextChangedListener(this)
|
||||
binding.albumText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.albumArtistText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.genreTitle.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.yearTitle.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
}
|
||||
|
||||
private fun fillViewsWithFileTags() {
|
||||
albumText.setText(albumTitle)
|
||||
albumArtistText.setText(albumArtistName)
|
||||
genreTitle.setText(genreName)
|
||||
yearTitle.setText(songYear)
|
||||
binding.albumText.setText(albumTitle)
|
||||
binding.albumArtistText.setText(albumArtistName)
|
||||
binding.genreTitle.setText(genreName)
|
||||
binding.yearTitle.setText(songYear)
|
||||
println(albumTitle + albumArtistName)
|
||||
}
|
||||
|
||||
override fun loadCurrentImage() {
|
||||
|
@ -140,41 +107,68 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
|||
bitmap,
|
||||
getColor(
|
||||
generatePalette(bitmap),
|
||||
ATHUtil.resolveColor(this, R.attr.defaultFooterColor)
|
||||
defaultFooterColor()
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
}
|
||||
|
||||
private fun toastLoadingFailed() {
|
||||
Toast.makeText(
|
||||
this@AlbumTagEditorActivity,
|
||||
R.string.could_not_download_album_cover,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
showToast(R.string.could_not_download_album_cover)
|
||||
}
|
||||
|
||||
override fun searchImageOnWeb() {
|
||||
searchWebFor(albumText.text.toString(), albumArtistText.text.toString())
|
||||
searchWebFor(binding.albumText.text.toString(), binding.albumArtistText.text.toString())
|
||||
}
|
||||
|
||||
override fun deleteImage() {
|
||||
setImageBitmap(
|
||||
BitmapFactory.decodeResource(resources, R.drawable.default_audio_art),
|
||||
ATHUtil.resolveColor(this, R.attr.defaultFooterColor)
|
||||
defaultFooterColor()
|
||||
)
|
||||
deleteAlbumArt = true
|
||||
dataChanged()
|
||||
}
|
||||
|
||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||
GlideApp.with(this@AlbumTagEditorActivity).asBitmapPalette().load(selectedFile)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
|
||||
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
getColor(resource.palette, Color.TRANSPARENT)
|
||||
albumArtBitmap = resource.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
||||
setImageBitmap(
|
||||
albumArtBitmap,
|
||||
getColor(
|
||||
resource.palette,
|
||||
defaultFooterColor()
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
dataChanged()
|
||||
setResult(Activity.RESULT_OK)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
showToast(R.string.error_load_failed, Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
override fun setResource(resource: BitmapPaletteWrapper?) {}
|
||||
})
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM] = binding.albumText.text.toString()
|
||||
// android seems not to recognize album_artist field so we additionally write the normal artist field
|
||||
fieldKeyValueMap[FieldKey.ARTIST] = albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.GENRE] = genreTitle.text.toString()
|
||||
fieldKeyValueMap[FieldKey.YEAR] = yearTitle.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ARTIST] = binding.albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = binding.albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.GENRE] = binding.genreTitle.text.toString()
|
||||
fieldKeyValueMap[FieldKey.YEAR] = binding.yearTitle.text.toString()
|
||||
|
||||
writeValuesToFiles(
|
||||
fieldKeyValueMap,
|
||||
|
@ -191,21 +185,29 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
|||
.map(Song::data)
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
dataChanged()
|
||||
override fun getSongUris(): List<Uri> = repository.albumById(id).songs.map {
|
||||
MusicUtil.getSongFileUri(it.id)
|
||||
}
|
||||
|
||||
override fun setColors(color: Int) {
|
||||
super.setColors(color)
|
||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this,
|
||||
color.isColorLight
|
||||
)
|
||||
).also {
|
||||
saveFab.iconTint = it
|
||||
saveFab.setTextColor(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override val editorImage: ImageView
|
||||
get() = binding.editorImage
|
||||
|
||||
companion object {
|
||||
|
||||
val TAG: String = AlbumTagEditorActivity::class.java.simpleName
|
||||
|
|
|
@ -14,110 +14,196 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.activities.tageditor
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||
import code.name.monkey.retromusic.databinding.ActivitySongTagEditorBinding
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.model.ArtworkInfo
|
||||
import code.name.monkey.retromusic.repository.SongRepository
|
||||
import kotlinx.android.synthetic.main.activity_song_tag_editor.*
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.target.ImageViewTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import org.koin.android.ext.android.inject
|
||||
import java.util.*
|
||||
|
||||
class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||
class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>() {
|
||||
|
||||
override val bindingInflater: (LayoutInflater) -> ActivitySongTagEditorBinding =
|
||||
ActivitySongTagEditorBinding::inflate
|
||||
|
||||
override val contentViewLayout: Int
|
||||
get() = R.layout.activity_song_tag_editor
|
||||
|
||||
private val songRepository by inject<SongRepository>()
|
||||
|
||||
private var albumArtBitmap: Bitmap? = null
|
||||
private var deleteAlbumArt: Boolean = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setNoImageMode()
|
||||
setUpViews()
|
||||
toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
setSupportActionBar(toolbar)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
binding.appBarLayout?.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(this)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun setUpViews() {
|
||||
fillViewsWithFileTags()
|
||||
MaterialUtil.setTint(songTextContainer, false)
|
||||
MaterialUtil.setTint(composerContainer, false)
|
||||
MaterialUtil.setTint(albumTextContainer, false)
|
||||
MaterialUtil.setTint(artistContainer, false)
|
||||
MaterialUtil.setTint(albumArtistContainer, false)
|
||||
MaterialUtil.setTint(yearContainer, false)
|
||||
MaterialUtil.setTint(genreContainer, false)
|
||||
MaterialUtil.setTint(trackNumberContainer, false)
|
||||
MaterialUtil.setTint(lyricsContainer, false)
|
||||
binding.songTextContainer.setTint(false)
|
||||
binding.composerContainer.setTint(false)
|
||||
binding.albumTextContainer.setTint(false)
|
||||
binding.artistContainer.setTint(false)
|
||||
binding.albumArtistContainer.setTint(false)
|
||||
binding.yearContainer.setTint(false)
|
||||
binding.genreContainer.setTint(false)
|
||||
binding.trackNumberContainer.setTint(false)
|
||||
binding.discNumberContainer.setTint(false)
|
||||
binding.lyricsContainer.setTint(false)
|
||||
|
||||
songText.appHandleColor().addTextChangedListener(this)
|
||||
albumText.appHandleColor().addTextChangedListener(this)
|
||||
albumArtistText.appHandleColor().addTextChangedListener(this)
|
||||
artistText.appHandleColor().addTextChangedListener(this)
|
||||
genreText.appHandleColor().addTextChangedListener(this)
|
||||
yearText.appHandleColor().addTextChangedListener(this)
|
||||
trackNumberText.appHandleColor().addTextChangedListener(this)
|
||||
lyricsText.appHandleColor().addTextChangedListener(this)
|
||||
songComposerText.appHandleColor().addTextChangedListener(this)
|
||||
binding.songText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.albumText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.albumArtistText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.artistText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.genreText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.yearText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.trackNumberText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.discNumberText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.lyricsText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
binding.songComposerText.appHandleColor().doAfterTextChanged { dataChanged() }
|
||||
}
|
||||
|
||||
private fun fillViewsWithFileTags() {
|
||||
songText.setText(songTitle)
|
||||
albumArtistText.setText(albumArtist)
|
||||
albumText.setText(albumTitle)
|
||||
artistText.setText(artistName)
|
||||
genreText.setText(genreName)
|
||||
yearText.setText(songYear)
|
||||
trackNumberText.setText(trackNumber)
|
||||
lyricsText.setText(lyrics)
|
||||
songComposerText.setText(composer)
|
||||
binding.songText.setText(songTitle)
|
||||
binding.albumArtistText.setText(albumArtist)
|
||||
binding.albumText.setText(albumTitle)
|
||||
binding.artistText.setText(artistName)
|
||||
binding.genreText.setText(genreName)
|
||||
binding.yearText.setText(songYear)
|
||||
binding.trackNumberText.setText(trackNumber)
|
||||
binding.discNumberText.setText(discNumber)
|
||||
binding.lyricsText.setText(lyrics)
|
||||
binding.songComposerText.setText(composer)
|
||||
println(songTitle + songYear)
|
||||
}
|
||||
|
||||
override fun loadCurrentImage() {
|
||||
val bitmap = albumArt
|
||||
setImageBitmap(
|
||||
bitmap,
|
||||
RetroColorUtil.getColor(
|
||||
RetroColorUtil.generatePalette(bitmap),
|
||||
defaultFooterColor()
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
}
|
||||
|
||||
override fun searchImageOnWeb() {
|
||||
searchWebFor(binding.songText.text.toString(), binding.artistText.text.toString())
|
||||
}
|
||||
|
||||
override fun deleteImage() {
|
||||
setImageBitmap(
|
||||
BitmapFactory.decodeResource(resources, R.drawable.default_audio_art),
|
||||
defaultFooterColor()
|
||||
)
|
||||
deleteAlbumArt = true
|
||||
dataChanged()
|
||||
}
|
||||
|
||||
override fun setColors(color: Int) {
|
||||
super.setColors(color)
|
||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this,
|
||||
color.isColorLight
|
||||
)
|
||||
).also {
|
||||
saveFab.iconTint = it
|
||||
saveFab.setTextColor(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
fieldKeyValueMap[FieldKey.TITLE] = songText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ARTIST] = artistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.GENRE] = genreText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.YEAR] = yearText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.TRACK] = trackNumberText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.LYRICS] = lyricsText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.COMPOSER] = songComposerText.text.toString()
|
||||
writeValuesToFiles(fieldKeyValueMap, null)
|
||||
fieldKeyValueMap[FieldKey.TITLE] = binding.songText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM] = binding.albumText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ARTIST] = binding.artistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.GENRE] = binding.genreText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.YEAR] = binding.yearText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.TRACK] = binding.trackNumberText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.DISC_NO] = binding.discNumberText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.LYRICS] = binding.lyricsText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = binding.albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.COMPOSER] = binding.songComposerText.text.toString()
|
||||
writeValuesToFiles(
|
||||
fieldKeyValueMap, when {
|
||||
deleteAlbumArt -> ArtworkInfo(id, null)
|
||||
albumArtBitmap == null -> null
|
||||
else -> ArtworkInfo(id, albumArtBitmap!!)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun getSongPaths(): List<String> = listOf(songRepository.song(id).data)
|
||||
|
||||
override fun getSongUris(): List<Uri> = listOf(MusicUtil.getSongFileUri(id))
|
||||
|
||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||
}
|
||||
GlideApp.with(this@SongTagEditorActivity).asBitmapPalette().load(selectedFile)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
|
||||
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
RetroColorUtil.getColor(resource.palette, Color.TRANSPARENT)
|
||||
albumArtBitmap = resource.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
||||
setImageBitmap(
|
||||
albumArtBitmap,
|
||||
RetroColorUtil.getColor(
|
||||
resource.palette,
|
||||
defaultFooterColor()
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
dataChanged()
|
||||
setResult(Activity.RESULT_OK)
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
}
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
showToast(R.string.error_load_failed, Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
dataChanged()
|
||||
override fun setResource(resource: BitmapPaletteWrapper?) {}
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = SongTagEditorActivity::class.java.simpleName
|
||||
}
|
||||
|
||||
override val editorImage: ImageView
|
||||
get() = binding.editorImage
|
||||
}
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.media.MediaScannerConnection
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import code.name.monkey.retromusic.extensions.showToast
|
||||
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener
|
||||
import code.name.monkey.retromusic.model.AudioTagInfo
|
||||
import code.name.monkey.retromusic.util.MusicUtil.createAlbumArtFile
|
||||
import code.name.monkey.retromusic.util.MusicUtil.deleteAlbumArt
|
||||
import code.name.monkey.retromusic.util.MusicUtil.insertAlbumArt
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jaudiotagger.audio.AudioFileIO
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException
|
||||
import org.jaudiotagger.audio.exceptions.CannotWriteException
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
|
||||
import org.jaudiotagger.tag.TagException
|
||||
import org.jaudiotagger.tag.images.AndroidArtwork
|
||||
import org.jaudiotagger.tag.images.Artwork
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
class TagWriter {
|
||||
|
||||
companion object {
|
||||
|
||||
suspend fun scan(context: Context, toBeScanned: List<String?>?) {
|
||||
if (toBeScanned == null || toBeScanned.isEmpty()) {
|
||||
Log.i("scan", "scan: Empty")
|
||||
context.showToast( "Scan file from folder")
|
||||
return
|
||||
}
|
||||
MediaScannerConnection.scanFile(
|
||||
context,
|
||||
toBeScanned.toTypedArray(),
|
||||
null,
|
||||
withContext(Dispatchers.Main) {
|
||||
if (context is Activity) UpdateToastMediaScannerCompletionListener(
|
||||
context, toBeScanned
|
||||
) else null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) {
|
||||
withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
var artwork: Artwork? = null
|
||||
var albumArtFile: File? = null
|
||||
if (info.artworkInfo?.artwork != null) {
|
||||
try {
|
||||
albumArtFile = createAlbumArtFile(context).canonicalFile
|
||||
info.artworkInfo.artwork.compress(
|
||||
Bitmap.CompressFormat.JPEG,
|
||||
100,
|
||||
albumArtFile.outputStream()
|
||||
)
|
||||
artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
var wroteArtwork = false
|
||||
var deletedArtwork = false
|
||||
for (filePath in info.filePaths!!) {
|
||||
try {
|
||||
val audioFile = AudioFileIO.read(File(filePath))
|
||||
val tag = audioFile.tagOrCreateAndSetDefault
|
||||
if (info.fieldKeyValueMap != null) {
|
||||
for ((key, value) in info.fieldKeyValueMap) {
|
||||
try {
|
||||
tag.setField(key, value)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info.artworkInfo != null) {
|
||||
if (info.artworkInfo.artwork == null) {
|
||||
tag.deleteArtworkField()
|
||||
deletedArtwork = true
|
||||
} else if (artwork != null) {
|
||||
tag.deleteArtworkField()
|
||||
tag.setField(artwork)
|
||||
wroteArtwork = true
|
||||
}
|
||||
}
|
||||
audioFile.commit()
|
||||
} catch (e: CannotReadException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: CannotWriteException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: TagException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: ReadOnlyFileException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: InvalidAudioFrameException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
if (wroteArtwork) {
|
||||
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
|
||||
} else if (deletedArtwork) {
|
||||
deleteAlbumArt(context, info.artworkInfo!!.albumId)
|
||||
}
|
||||
scan(context, info.filePaths)
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List<File> =
|
||||
withContext(Dispatchers.IO) {
|
||||
val cacheFiles = mutableListOf<File>()
|
||||
runCatching {
|
||||
var artwork: Artwork? = null
|
||||
var albumArtFile: File? = null
|
||||
if (info.artworkInfo?.artwork != null) {
|
||||
try {
|
||||
albumArtFile = createAlbumArtFile(context).canonicalFile
|
||||
info.artworkInfo.artwork.compress(
|
||||
Bitmap.CompressFormat.JPEG,
|
||||
100,
|
||||
albumArtFile.outputStream()
|
||||
)
|
||||
artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
var wroteArtwork = false
|
||||
var deletedArtwork = false
|
||||
for (filePath in info.filePaths!!) {
|
||||
try {
|
||||
val originFile = File(filePath)
|
||||
val cacheFile = File(context.cacheDir, originFile.name)
|
||||
cacheFiles.add(cacheFile)
|
||||
originFile.inputStream().use { input ->
|
||||
cacheFile.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
val audioFile = AudioFileIO.read(cacheFile)
|
||||
val tag = audioFile.tagOrCreateAndSetDefault
|
||||
if (info.fieldKeyValueMap != null) {
|
||||
for ((key, value) in info.fieldKeyValueMap) {
|
||||
try {
|
||||
tag.setField(key, value)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info.artworkInfo != null) {
|
||||
if (info.artworkInfo.artwork == null) {
|
||||
tag.deleteArtworkField()
|
||||
deletedArtwork = true
|
||||
} else if (artwork != null) {
|
||||
tag.deleteArtworkField()
|
||||
tag.setField(artwork)
|
||||
wroteArtwork = true
|
||||
}
|
||||
}
|
||||
audioFile.commit()
|
||||
} catch (e: CannotReadException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: CannotWriteException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: TagException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: ReadOnlyFileException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: InvalidAudioFrameException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
if (wroteArtwork) {
|
||||
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
|
||||
} else if (deletedArtwork) {
|
||||
deleteAlbumArt(context, info.artworkInfo!!.albumId)
|
||||
}
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
cacheFiles
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import org.jaudiotagger.audio.AudioFile;
|
||||
import org.jaudiotagger.audio.AudioFileIO;
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||
import org.jaudiotagger.audio.exceptions.CannotWriteException;
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||
import org.jaudiotagger.tag.FieldKey;
|
||||
import org.jaudiotagger.tag.Tag;
|
||||
import org.jaudiotagger.tag.TagException;
|
||||
import org.jaudiotagger.tag.images.Artwork;
|
||||
import org.jaudiotagger.tag.images.ArtworkFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.misc.DialogAsyncTask;
|
||||
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener;
|
||||
import code.name.monkey.retromusic.model.LoadingInfo;
|
||||
import code.name.monkey.retromusic.util.MusicUtil;
|
||||
|
||||
public class WriteTagsAsyncTask extends DialogAsyncTask<LoadingInfo, Integer, List<String>> {
|
||||
|
||||
public WriteTagsAsyncTask(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> doInBackground(LoadingInfo... params) {
|
||||
try {
|
||||
LoadingInfo info = params[0];
|
||||
|
||||
Artwork artwork = null;
|
||||
File albumArtFile = null;
|
||||
if (info.getArtworkInfo() != null && info.getArtworkInfo().getArtwork() != null) {
|
||||
try {
|
||||
albumArtFile = MusicUtil.INSTANCE.createAlbumArtFile().getCanonicalFile();
|
||||
info.getArtworkInfo().getArtwork().compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile));
|
||||
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
boolean wroteArtwork = false;
|
||||
boolean deletedArtwork = false;
|
||||
for (String filePath : info.getFilePaths()) {
|
||||
publishProgress(++counter, info.getFilePaths().size());
|
||||
try {
|
||||
AudioFile audioFile = AudioFileIO.read(new File(filePath));
|
||||
Tag tag = audioFile.getTagOrCreateAndSetDefault();
|
||||
|
||||
if (info.getFieldKeyValueMap() != null) {
|
||||
for (Map.Entry<FieldKey, String> entry : info.getFieldKeyValueMap().entrySet()) {
|
||||
try {
|
||||
tag.setField(entry.getKey(), entry.getValue());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info.getArtworkInfo() != null) {
|
||||
if (info.getArtworkInfo().getArtwork() == null) {
|
||||
tag.deleteArtworkField();
|
||||
deletedArtwork = true;
|
||||
} else if (artwork != null) {
|
||||
tag.deleteArtworkField();
|
||||
tag.setField(artwork);
|
||||
wroteArtwork = true;
|
||||
}
|
||||
}
|
||||
|
||||
audioFile.commit();
|
||||
} catch (@NonNull CannotReadException | IOException | CannotWriteException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Context context = getContext();
|
||||
if (context != null) {
|
||||
if (wroteArtwork) {
|
||||
MusicUtil.INSTANCE.
|
||||
insertAlbumArt(context, info.getArtworkInfo().getAlbumId(), albumArtFile.getPath());
|
||||
} else if (deletedArtwork) {
|
||||
MusicUtil.INSTANCE.deleteAlbumArt(context, info.getArtworkInfo().getAlbumId());
|
||||
}
|
||||
}
|
||||
|
||||
return info.getFilePaths();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<String> toBeScanned) {
|
||||
super.onPostExecute(toBeScanned);
|
||||
scan(toBeScanned);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(List<String> toBeScanned) {
|
||||
super.onCancelled(toBeScanned);
|
||||
scan(toBeScanned);
|
||||
}
|
||||
|
||||
private void scan(List<String> toBeScanned) {
|
||||
Context context = getContext();
|
||||
if (toBeScanned == null || toBeScanned.isEmpty()) {
|
||||
Log.i("scan", "scan: Empty");
|
||||
Toast.makeText(context, "Scan file from folder", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
MediaScannerConnection.scanFile(context, toBeScanned.toArray(new String[0]), null, context instanceof Activity ? new UpdateToastMediaScannerCompletionListener((Activity) context, toBeScanned) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog createDialog(@NonNull Context context) {
|
||||
return new MaterialDialog.Builder(context)
|
||||
.title(R.string.saving_changes)
|
||||
.cancelable(false)
|
||||
.progress(false, 0)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(@NonNull Dialog dialog, Integer... values) {
|
||||
super.onProgressUpdate(dialog, values);
|
||||
((MaterialDialog) dialog).setMaxProgress(values[1]);
|
||||
((MaterialDialog) dialog).setProgress(values[0]);
|
||||
}
|
||||
}
|
|
@ -1,140 +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.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import code.name.monkey.appthemehelper.ThemeStore;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.model.CategoryInfo;
|
||||
import code.name.monkey.retromusic.util.SwipeAndDragHelper;
|
||||
import com.google.android.material.checkbox.MaterialCheckBox;
|
||||
import java.util.List;
|
||||
|
||||
public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapter.ViewHolder>
|
||||
implements SwipeAndDragHelper.ActionCompletionContract {
|
||||
|
||||
private List<CategoryInfo> categoryInfos;
|
||||
private ItemTouchHelper touchHelper;
|
||||
|
||||
public CategoryInfoAdapter() {
|
||||
SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(this);
|
||||
touchHelper = new ItemTouchHelper(swipeAndDragHelper);
|
||||
}
|
||||
|
||||
public void attachToRecyclerView(RecyclerView recyclerView) {
|
||||
touchHelper.attachToRecyclerView(recyclerView);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<CategoryInfo> getCategoryInfos() {
|
||||
return categoryInfos;
|
||||
}
|
||||
|
||||
public void setCategoryInfos(@NonNull List<CategoryInfo> categoryInfos) {
|
||||
this.categoryInfos = categoryInfos;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return categoryInfos.size();
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull CategoryInfoAdapter.ViewHolder holder, int position) {
|
||||
CategoryInfo categoryInfo = categoryInfos.get(position);
|
||||
|
||||
holder.checkBox.setChecked(categoryInfo.isVisible());
|
||||
holder.title.setText(
|
||||
holder.title.getResources().getString(categoryInfo.getCategory().getStringRes()));
|
||||
|
||||
holder.itemView.setOnClickListener(
|
||||
v -> {
|
||||
if (!(categoryInfo.isVisible() && isLastCheckedCategory(categoryInfo))) {
|
||||
categoryInfo.setVisible(!categoryInfo.isVisible());
|
||||
holder.checkBox.setChecked(categoryInfo.isVisible());
|
||||
} else {
|
||||
Toast.makeText(
|
||||
holder.itemView.getContext(),
|
||||
R.string.you_have_to_select_at_least_one_category,
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
|
||||
holder.dragView.setOnTouchListener(
|
||||
(view, event) -> {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
touchHelper.startDrag(holder);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CategoryInfoAdapter.ViewHolder onCreateViewHolder(
|
||||
@NonNull ViewGroup parent, int viewType) {
|
||||
View view =
|
||||
LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.preference_dialog_library_categories_listitem, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewMoved(int oldPosition, int newPosition) {
|
||||
CategoryInfo categoryInfo = categoryInfos.get(oldPosition);
|
||||
categoryInfos.remove(oldPosition);
|
||||
categoryInfos.add(newPosition, categoryInfo);
|
||||
notifyItemMoved(oldPosition, newPosition);
|
||||
}
|
||||
|
||||
private boolean isLastCheckedCategory(CategoryInfo categoryInfo) {
|
||||
if (categoryInfo.isVisible()) {
|
||||
for (CategoryInfo c : categoryInfos) {
|
||||
if (c != categoryInfo && c.isVisible()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private MaterialCheckBox checkBox;
|
||||
private View dragView;
|
||||
private TextView title;
|
||||
|
||||
ViewHolder(View view) {
|
||||
super(view);
|
||||
checkBox = view.findViewById(R.id.checkbox);
|
||||
checkBox.setButtonTintList(
|
||||
ColorStateList.valueOf(ThemeStore.Companion.accentColor(checkBox.getContext())));
|
||||
title = view.findViewById(R.id.title);
|
||||
dragView = view.findViewById(R.id.drag_view);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.PreferenceDialogLibraryCategoriesListitemBinding
|
||||
import code.name.monkey.retromusic.extensions.showToast
|
||||
import code.name.monkey.retromusic.model.CategoryInfo
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.SwipeAndDragHelper
|
||||
import code.name.monkey.retromusic.util.SwipeAndDragHelper.ActionCompletionContract
|
||||
|
||||
class CategoryInfoAdapter : RecyclerView.Adapter<CategoryInfoAdapter.ViewHolder>(),
|
||||
ActionCompletionContract {
|
||||
var categoryInfos: MutableList<CategoryInfo> =
|
||||
PreferenceUtil.libraryCategory.toMutableList()
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
set(value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
private val touchHelper: ItemTouchHelper
|
||||
fun attachToRecyclerView(recyclerView: RecyclerView?) {
|
||||
touchHelper.attachToRecyclerView(recyclerView)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return categoryInfos.size
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val categoryInfo = categoryInfos[position]
|
||||
holder.binding.checkbox.isChecked = categoryInfo.visible
|
||||
holder.binding.title.text =
|
||||
holder.binding.title.resources.getString(categoryInfo.category.stringRes)
|
||||
holder.itemView.setOnClickListener {
|
||||
if (!(categoryInfo.visible && isLastCheckedCategory(categoryInfo))) {
|
||||
categoryInfo.visible = !categoryInfo.visible
|
||||
holder.binding.checkbox.isChecked = categoryInfo.visible
|
||||
} else {
|
||||
holder.itemView.context.showToast(R.string.you_have_to_select_at_least_one_category)
|
||||
}
|
||||
}
|
||||
holder.binding.dragView.setOnTouchListener { _: View?, event: MotionEvent ->
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
touchHelper.startDrag(holder)
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup, viewType: Int
|
||||
): ViewHolder {
|
||||
return ViewHolder(
|
||||
PreferenceDialogLibraryCategoriesListitemBinding.inflate(
|
||||
LayoutInflater.from(
|
||||
parent.context
|
||||
), parent, false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onViewMoved(oldPosition: Int, newPosition: Int) {
|
||||
val categoryInfo = categoryInfos[oldPosition]
|
||||
categoryInfos.removeAt(oldPosition)
|
||||
categoryInfos.add(newPosition, categoryInfo)
|
||||
notifyItemMoved(oldPosition, newPosition)
|
||||
}
|
||||
|
||||
private fun isLastCheckedCategory(categoryInfo: CategoryInfo): Boolean {
|
||||
if (categoryInfo.visible) {
|
||||
for (c in categoryInfos) {
|
||||
if (c !== categoryInfo && c.visible) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
class ViewHolder(val binding: PreferenceDialogLibraryCategoriesListitemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
init {
|
||||
binding.checkbox.buttonTintList =
|
||||
ColorStateList.valueOf(accentColor(binding.checkbox.context))
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val swipeAndDragHelper = SwipeAndDragHelper(this)
|
||||
touchHelper = ItemTouchHelper(swipeAndDragHelper)
|
||||
}
|
||||
}
|
|
@ -14,15 +14,14 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.adapter
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.openUrl
|
||||
import code.name.monkey.retromusic.model.Contributor
|
||||
import code.name.monkey.retromusic.util.RetroUtil.openUrl
|
||||
import code.name.monkey.retromusic.views.RetroShapeableImageView
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
|
@ -65,7 +64,7 @@ class ContributorAdapter(
|
|||
val contributor = contributors[position]
|
||||
holder.bindData(contributor)
|
||||
holder.itemView.setOnClickListener {
|
||||
openUrl(it?.context as Activity, contributors[position].link)
|
||||
it?.context?.openUrl(contributors[position].link)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,16 +14,22 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewCompat
|
||||
import android.view.ViewOutlineProvider
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||
import code.name.monkey.retromusic.databinding.ItemGenreBinding
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.interfaces.IGenreClickListener
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
|
@ -33,37 +39,72 @@ import java.util.*
|
|||
class GenreAdapter(
|
||||
private val activity: FragmentActivity,
|
||||
var dataSet: List<Genre>,
|
||||
private val mItemLayoutRes: Int,
|
||||
private val listener: IGenreClickListener
|
||||
) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() {
|
||||
|
||||
init {
|
||||
this.setHasStableIds(true)
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return dataSet[position].id
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(LayoutInflater.from(activity).inflate(mItemLayoutRes, parent, false))
|
||||
return ViewHolder(ItemGenreBinding.inflate(LayoutInflater.from(activity), parent, false))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val genre = dataSet[position]
|
||||
holder.title?.text = genre.name
|
||||
holder.text?.text = String.format(
|
||||
holder.binding.title.text = genre.name
|
||||
holder.binding.text.text = String.format(
|
||||
Locale.getDefault(),
|
||||
"%d %s",
|
||||
genre.songCount,
|
||||
if (genre.songCount > 1) activity.getString(R.string.songs) else activity.getString(R.string.song)
|
||||
)
|
||||
loadGenreImage(genre, holder)
|
||||
}
|
||||
|
||||
private fun loadGenreImage(genre: Genre, holder: GenreAdapter.ViewHolder) {
|
||||
val genreSong = MusicUtil.songByGenre(genre.id)
|
||||
GlideApp.with(activity)
|
||||
.asBitmapPalette()
|
||||
.load(RetroGlideExtension.getSongModel(genreSong))
|
||||
.songCoverOptions(genreSong)
|
||||
.into(object : RetroMusicColoredTarget(holder.binding.image) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(holder, colors)
|
||||
}
|
||||
})
|
||||
// Just for a bit of shadow around image
|
||||
holder.binding.image.outlineProvider = ViewOutlineProvider.BOUNDS
|
||||
}
|
||||
|
||||
private fun setColors(holder: ViewHolder, color: MediaNotificationProcessor) {
|
||||
holder.binding.imageContainerCard.setCardBackgroundColor(color.backgroundColor)
|
||||
holder.binding.title.setTextColor(color.primaryTextColor)
|
||||
holder.binding.text.setTextColor(color.secondaryTextColor)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun swapDataSet(list: List<Genre>) {
|
||||
dataSet = list
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||
inner class ViewHolder(val binding: ItemGenreBinding) : RecyclerView.ViewHolder(binding.root),
|
||||
View.OnClickListener {
|
||||
override fun onClick(v: View?) {
|
||||
ViewCompat.setTransitionName(itemView, "genre")
|
||||
listener.onClickGenre(dataSet[layoutPosition], itemView)
|
||||
}
|
||||
|
||||
init {
|
||||
itemView.setOnClickListener(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,35 +14,30 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.findFragment
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
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.hide
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.fragments.home.HomeFragment
|
||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
||||
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
||||
import code.name.monkey.retromusic.interfaces.IGenreClickListener
|
||||
import code.name.monkey.retromusic.model.*
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
|
||||
class HomeAdapter(
|
||||
private val activity: AppCompatActivity
|
||||
|
@ -60,17 +55,10 @@ class HomeAdapter(
|
|||
LayoutInflater.from(activity).inflate(R.layout.section_recycler_view, parent, false)
|
||||
return when (viewType) {
|
||||
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
|
||||
GENRES -> GenreViewHolder(layout)
|
||||
FAVOURITES -> PlaylistViewHolder(layout)
|
||||
TOP_ALBUMS, RECENT_ALBUMS -> AlbumViewHolder(layout)
|
||||
else -> {
|
||||
SuggestionsViewHolder(
|
||||
LayoutInflater.from(activity).inflate(
|
||||
R.layout.item_suggestions,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
ArtistViewHolder(layout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +70,7 @@ class HomeAdapter(
|
|||
val viewHolder = holder as AlbumViewHolder
|
||||
viewHolder.bindView(home)
|
||||
viewHolder.clickableArea.setOnClickListener {
|
||||
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
||||
activity.findNavController(R.id.fragment_container).navigate(
|
||||
R.id.detailListFragment,
|
||||
bundleOf("type" to RECENT_ALBUMS)
|
||||
|
@ -92,6 +81,7 @@ class HomeAdapter(
|
|||
val viewHolder = holder as AlbumViewHolder
|
||||
viewHolder.bindView(home)
|
||||
viewHolder.clickableArea.setOnClickListener {
|
||||
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
||||
activity.findNavController(R.id.fragment_container).navigate(
|
||||
R.id.detailListFragment,
|
||||
bundleOf("type" to TOP_ALBUMS)
|
||||
|
@ -102,6 +92,7 @@ class HomeAdapter(
|
|||
val viewHolder = holder as ArtistViewHolder
|
||||
viewHolder.bindView(home)
|
||||
viewHolder.clickableArea.setOnClickListener {
|
||||
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
||||
activity.findNavController(R.id.fragment_container).navigate(
|
||||
R.id.detailListFragment,
|
||||
bundleOf("type" to RECENT_ARTISTS)
|
||||
|
@ -112,32 +103,24 @@ class HomeAdapter(
|
|||
val viewHolder = holder as ArtistViewHolder
|
||||
viewHolder.bindView(home)
|
||||
viewHolder.clickableArea.setOnClickListener {
|
||||
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
||||
activity.findNavController(R.id.fragment_container).navigate(
|
||||
R.id.detailListFragment,
|
||||
bundleOf("type" to TOP_ARTISTS)
|
||||
)
|
||||
}
|
||||
}
|
||||
SUGGESTIONS -> {
|
||||
val viewHolder = holder as SuggestionsViewHolder
|
||||
viewHolder.bindView(home)
|
||||
}
|
||||
FAVOURITES -> {
|
||||
val viewHolder = holder as PlaylistViewHolder
|
||||
viewHolder.bindView(home)
|
||||
viewHolder.clickableArea.setOnClickListener {
|
||||
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
||||
activity.findNavController(R.id.fragment_container).navigate(
|
||||
R.id.detailListFragment,
|
||||
bundleOf("type" to FAVOURITES)
|
||||
)
|
||||
}
|
||||
}
|
||||
GENRES -> {
|
||||
val viewHolder = holder as GenreViewHolder
|
||||
viewHolder.bind(home)
|
||||
}
|
||||
PLAYLISTS -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,6 +128,7 @@ class HomeAdapter(
|
|||
return list.size
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun swapData(sections: List<Home>) {
|
||||
list = sections
|
||||
notifyDataSetChanged()
|
||||
|
@ -170,36 +154,6 @@ class HomeAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
private inner class SuggestionsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val images = listOf(
|
||||
R.id.image1,
|
||||
R.id.image2,
|
||||
R.id.image3,
|
||||
R.id.image4,
|
||||
R.id.image5,
|
||||
R.id.image6,
|
||||
R.id.image7,
|
||||
R.id.image8
|
||||
)
|
||||
|
||||
fun bindView(home: Home) {
|
||||
val color = ThemeStore.accentColor(activity)
|
||||
itemView.findViewById<TextView>(R.id.message).setTextColor(color)
|
||||
itemView.findViewById<MaterialCardView>(R.id.card6).apply {
|
||||
setCardBackgroundColor(ColorUtil.withAlpha(color, 0.12f))
|
||||
}
|
||||
images.forEachIndexed { index, id ->
|
||||
itemView.findViewById<View>(id).setOnClickListener {
|
||||
MusicPlayerRemote.playNext(home.arrayList[index] as Song)
|
||||
}
|
||||
SongGlideRequest.Builder.from(Glide.with(activity), home.arrayList[index] as Song)
|
||||
.asBitmap()
|
||||
.build()
|
||||
.into(itemView.findViewById(id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) {
|
||||
fun bindView(home: Home) {
|
||||
title.setText(home.titleRes)
|
||||
|
@ -207,7 +161,7 @@ class HomeAdapter(
|
|||
val songAdapter = SongAdapter(
|
||||
activity,
|
||||
home.arrayList as MutableList<Song>,
|
||||
R.layout.item_album_card, null
|
||||
R.layout.item_favourite_card, null
|
||||
)
|
||||
layoutManager = linearLayoutManager()
|
||||
adapter = songAdapter
|
||||
|
@ -215,23 +169,6 @@ class HomeAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
private inner class GenreViewHolder(itemView: View) : AbsHomeViewItem(itemView) {
|
||||
fun bind(home: Home) {
|
||||
arrow.hide()
|
||||
title.setText(home.titleRes)
|
||||
val genreAdapter = GenreAdapter(
|
||||
activity,
|
||||
home.arrayList as List<Genre>,
|
||||
R.layout.item_grid_genre,
|
||||
this@HomeAdapter
|
||||
)
|
||||
recyclerView.apply {
|
||||
layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.HORIZONTAL, false)
|
||||
adapter = genreAdapter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView)
|
||||
val title: AppCompatTextView = itemView.findViewById(R.id.title)
|
||||
|
@ -257,7 +194,7 @@ class HomeAdapter(
|
|||
bundleOf(EXTRA_ARTIST_ID to artistId),
|
||||
null,
|
||||
FragmentNavigatorExtras(
|
||||
view to "artist"
|
||||
view to artistId.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -268,7 +205,7 @@ class HomeAdapter(
|
|||
bundleOf(EXTRA_ALBUM_ID to albumId),
|
||||
null,
|
||||
FragmentNavigatorExtras(
|
||||
view to "album"
|
||||
view to albumId.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -27,17 +28,16 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.retromusic.*
|
||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
||||
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
||||
import code.name.monkey.retromusic.model.*
|
||||
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
|
||||
import code.name.monkey.retromusic.repository.PlaylistSongsLoader
|
||||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import java.util.*
|
||||
|
||||
class SearchAdapter(
|
||||
|
@ -45,6 +45,7 @@ class SearchAdapter(
|
|||
private var dataSet: List<Any>
|
||||
) : RecyclerView.Adapter<SearchAdapter.ViewHolder>() {
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun swapDataSet(dataSet: List<Any>) {
|
||||
this.dataSet = dataSet
|
||||
notifyDataSetChanged()
|
||||
|
@ -52,9 +53,9 @@ 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 Artist) return if ((dataSet[position] as Artist).isAlbumArtist) ALBUM_ARTIST else ARTIST
|
||||
if (dataSet[position] is Genre) return GENRE
|
||||
if (dataSet[position] is PlaylistEntity) return PLAYLIST
|
||||
if (dataSet[position] is PlaylistWithSongs) return PLAYLIST
|
||||
return if (dataSet[position] is Song) SONG else HEADER
|
||||
}
|
||||
|
||||
|
@ -66,6 +67,14 @@ class SearchAdapter(
|
|||
false
|
||||
), viewType
|
||||
)
|
||||
else if (viewType == ALBUM || viewType == ARTIST || viewType== ALBUM_ARTIST)
|
||||
ViewHolder(
|
||||
LayoutInflater.from(activity).inflate(
|
||||
R.layout.item_list_big,
|
||||
parent,
|
||||
false
|
||||
), viewType
|
||||
)
|
||||
else
|
||||
ViewHolder(
|
||||
LayoutInflater.from(activity).inflate(R.layout.item_list, parent, false),
|
||||
|
@ -80,21 +89,23 @@ class SearchAdapter(
|
|||
val album = dataSet[position] as Album
|
||||
holder.title?.text = album.title
|
||||
holder.text?.text = album.artistName
|
||||
AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
||||
.checkIgnoreMediaStore().build().into(holder.image)
|
||||
GlideApp.with(activity).asDrawable().albumCoverOptions(album.safeGetFirstSong()).load(RetroGlideExtension.getSongModel(album.safeGetFirstSong()))
|
||||
.into(holder.image!!)
|
||||
}
|
||||
ARTIST -> {
|
||||
holder.imageTextContainer?.isVisible = true
|
||||
val artist = dataSet[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)
|
||||
GlideApp.with(activity).asDrawable().artistImageOptions(artist).load(
|
||||
RetroGlideExtension.getArtistModel(artist)).into(holder.image!!)
|
||||
}
|
||||
SONG -> {
|
||||
holder.imageTextContainer?.isVisible = true
|
||||
val song = dataSet[position] as Song
|
||||
holder.title?.text = song.title
|
||||
holder.text?.text = song.albumName
|
||||
GlideApp.with(activity).asDrawable().songCoverOptions(song).load(RetroGlideExtension.getSongModel(song)).into(holder.image!!)
|
||||
}
|
||||
GENRE -> {
|
||||
val genre = dataSet[position] as Genre
|
||||
|
@ -109,10 +120,18 @@ class SearchAdapter(
|
|||
)
|
||||
}
|
||||
PLAYLIST -> {
|
||||
val playlist = dataSet[position] as PlaylistEntity
|
||||
holder.title?.text = playlist.playlistName
|
||||
val playlist = dataSet[position] as PlaylistWithSongs
|
||||
holder.title?.text = playlist.playlistEntity.playlistName
|
||||
//holder.text?.text = MusicUtil.playlistInfoString(activity, playlist.songs)
|
||||
}
|
||||
ALBUM_ARTIST -> {
|
||||
holder.imageTextContainer?.isVisible = true
|
||||
val artist = dataSet[position] as Artist
|
||||
holder.title?.text = artist.name
|
||||
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
|
||||
GlideApp.with(activity).asDrawable().artistImageOptions(artist).load(
|
||||
RetroGlideExtension.getArtistModel(artist)).into(holder.image!!)
|
||||
}
|
||||
else -> {
|
||||
holder.title?.text = dataSet[position].toString()
|
||||
holder.title?.setTextColor(ThemeStore.accentColor(activity))
|
||||
|
@ -120,16 +139,6 @@ class SearchAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getSongs(playlist: Playlist): List<Song> {
|
||||
val songs = mutableListOf<Song>()
|
||||
if (playlist is AbsSmartPlaylist) {
|
||||
songs.addAll(playlist.getSongs())
|
||||
} else {
|
||||
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
|
||||
}
|
||||
return songs
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
@ -140,13 +149,13 @@ class SearchAdapter(
|
|||
imageTextContainer?.isInvisible = true
|
||||
if (itemViewType == SONG) {
|
||||
imageTextContainer?.isGone = true
|
||||
menu?.visibility = View.VISIBLE
|
||||
menu?.isVisible = true
|
||||
menu?.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) {
|
||||
override val song: Song
|
||||
get() = dataSet[layoutPosition] as Song
|
||||
})
|
||||
} else {
|
||||
menu?.visibility = View.GONE
|
||||
menu?.isVisible = false
|
||||
}
|
||||
|
||||
when (itemViewType) {
|
||||
|
@ -154,7 +163,7 @@ class SearchAdapter(
|
|||
ARTIST -> setImageTransitionName(activity.getString(R.string.transition_artist_image))
|
||||
else -> {
|
||||
val container = itemView.findViewById<View>(R.id.imageContainer)
|
||||
container?.visibility = View.GONE
|
||||
container?.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -174,6 +183,12 @@ class SearchAdapter(
|
|||
bundleOf(EXTRA_ARTIST_ID to (item as Artist).id)
|
||||
)
|
||||
}
|
||||
ALBUM_ARTIST ->{
|
||||
activity.findNavController(R.id.fragment_container).navigate(
|
||||
R.id.albumArtistDetailsFragment,
|
||||
bundleOf(EXTRA_ARTIST_NAME to (item as Artist).name)
|
||||
)
|
||||
}
|
||||
GENRE -> {
|
||||
activity.findNavController(R.id.fragment_container).navigate(
|
||||
R.id.genreDetailsFragment,
|
||||
|
@ -187,9 +202,8 @@ class SearchAdapter(
|
|||
)
|
||||
}
|
||||
SONG -> {
|
||||
val playList = mutableListOf<Song>()
|
||||
playList.add(item as Song)
|
||||
MusicPlayerRemote.openQueue(playList, 0, true)
|
||||
MusicPlayerRemote.playNext(item as Song)
|
||||
MusicPlayerRemote.playNextSong()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,5 +216,6 @@ class SearchAdapter(
|
|||
private const val SONG = 3
|
||||
private const val GENRE = 4
|
||||
private const val PLAYLIST = 5
|
||||
private const val ALBUM_ARTIST = 6
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,30 +20,32 @@ import android.view.MenuItem
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
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.getTintedDrawable
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.interfaces.ICallbacks
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.signature.MediaStoreSignature
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
import java.io.File
|
||||
import java.text.DecimalFormat
|
||||
import kotlin.math.log10
|
||||
import kotlin.math.pow
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
|
||||
class SongFileAdapter(
|
||||
private val activity: AppCompatActivity,
|
||||
override val activity: AppCompatActivity,
|
||||
private var dataSet: List<File>,
|
||||
private val itemLayoutRes: Int,
|
||||
private val iCallbacks: ICallbacks?,
|
||||
iCabHolder: ICabHolder?
|
||||
iCabHolder: ICabHolder?,
|
||||
) : AbsMultiSelectAdapter<SongFileAdapter.ViewHolder, File>(
|
||||
activity, iCabHolder, R.menu.menu_media_selection
|
||||
), PopupTextProvider {
|
||||
|
@ -77,7 +79,7 @@ class SongFileAdapter(
|
|||
if (holder.itemViewType == FILE) {
|
||||
holder.text?.text = getFileText(file)
|
||||
} else {
|
||||
holder.text?.visibility = View.GONE
|
||||
holder.text?.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,17 +110,15 @@ class SongFileAdapter(
|
|||
)
|
||||
)
|
||||
} else {
|
||||
val error = RetroUtil.getTintedVectorDrawable(
|
||||
activity, R.drawable.ic_file_music, iconColor
|
||||
)
|
||||
Glide.with(activity)
|
||||
val error = activity.getTintedDrawable(R.drawable.ic_file_music, iconColor)
|
||||
GlideApp.with(activity)
|
||||
.load(AudioFileCover(file.path))
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.error(error)
|
||||
.placeholder(error)
|
||||
.animate(android.R.anim.fade_in)
|
||||
.transition(RetroGlideExtension.getDefaultTransition())
|
||||
.signature(MediaStoreSignature("", file.lastModified(), 0))
|
||||
.into(holder.image)
|
||||
.into(holder.image!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,12 +126,12 @@ class SongFileAdapter(
|
|||
return dataSet.size
|
||||
}
|
||||
|
||||
override fun getIdentifier(position: Int): File? {
|
||||
override fun getIdentifier(position: Int): File {
|
||||
return dataSet[position]
|
||||
}
|
||||
|
||||
override fun getName(`object`: File): String {
|
||||
return getFileTitle(`object`)
|
||||
override fun getName(model: File): String {
|
||||
return getFileTitle(model)
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<File>) {
|
||||
|
@ -140,7 +140,7 @@ class SongFileAdapter(
|
|||
}
|
||||
|
||||
override fun getPopupText(position: Int): String {
|
||||
return getSectionName(position)
|
||||
return if (position >= dataSet.lastIndex) "" else getSectionName(position)
|
||||
}
|
||||
|
||||
private fun getSectionName(position: Int): String {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package code.name.monkey.retromusic.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.R
|
||||
import java.io.File
|
||||
|
||||
class StorageAdapter(
|
||||
val storageList: List<Storage>,
|
||||
val storageClickListener: StorageClickListener
|
||||
) :
|
||||
RecyclerView.Adapter<StorageAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(
|
||||
R.layout.item_storage,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bindData(storageList[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return storageList.size
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val title: TextView = itemView.findViewById(R.id.title)
|
||||
|
||||
fun bindData(storage: Storage) {
|
||||
title.text = storage.title
|
||||
}
|
||||
|
||||
init {
|
||||
itemView.setOnClickListener { storageClickListener.onStorageClicked(storageList[bindingAdapterPosition]) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface StorageClickListener {
|
||||
fun onStorageClicked(storage: Storage)
|
||||
}
|
||||
|
||||
class Storage {
|
||||
lateinit var title: String
|
||||
lateinit var file: File
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hemanth Savarla.
|
||||
*
|
||||
* 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.adapter
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.model.Contributor
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.views.RetroShapeableImageView
|
||||
|
||||
class TranslatorsAdapter(
|
||||
private var contributors: List<Contributor>
|
||||
) : RecyclerView.Adapter<TranslatorsAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(
|
||||
R.layout.item_contributor,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val contributor = contributors[position]
|
||||
holder.bindData(contributor)
|
||||
holder.itemView.setOnClickListener {
|
||||
RetroUtil.openUrl(it?.context as Activity, contributors[position].link)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return contributors.size
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val title: TextView = itemView.findViewById(R.id.title)
|
||||
val text: TextView = itemView.findViewById(R.id.text)
|
||||
val image: RetroShapeableImageView = itemView.findViewById(R.id.icon)
|
||||
|
||||
internal fun bindData(contributor: Contributor) {
|
||||
title.text = contributor.name
|
||||
text.text = contributor.summary
|
||||
image.hide()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,12 +19,13 @@ import android.view.LayoutInflater
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
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.glide.AlbumGlideRequest
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.helper.SortOrder
|
||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||
|
@ -35,11 +36,10 @@ import code.name.monkey.retromusic.model.Song
|
|||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
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(
|
||||
val activity: FragmentActivity,
|
||||
override val activity: FragmentActivity,
|
||||
var dataSet: List<Album>,
|
||||
var itemLayoutRes: Int,
|
||||
iCabHolder: ICabHolder?,
|
||||
|
@ -68,12 +68,18 @@ open class AlbumAdapter(
|
|||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
private fun getAlbumTitle(album: Album): String? {
|
||||
private fun getAlbumTitle(album: Album): String {
|
||||
return album.title
|
||||
}
|
||||
|
||||
protected open fun getAlbumText(album: Album): String? {
|
||||
return album.artistName
|
||||
return album.albumArtist.let {
|
||||
if (it.isNullOrEmpty()) {
|
||||
album.artistName
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
|
@ -82,6 +88,13 @@ open class AlbumAdapter(
|
|||
holder.itemView.isActivated = isChecked
|
||||
holder.title?.text = getAlbumTitle(album)
|
||||
holder.text?.text = getAlbumText(album)
|
||||
// Check if imageContainer exists so we can have a smooth transition without
|
||||
// CardView clipping, if it doesn't exist in current layout set transition name to image instead.
|
||||
if (holder.imageContainer != null) {
|
||||
holder.imageContainer?.transitionName = album.id.toString()
|
||||
} else {
|
||||
holder.image?.transitionName = album.id.toString()
|
||||
}
|
||||
loadAlbumCover(album, holder)
|
||||
}
|
||||
|
||||
|
@ -92,17 +105,17 @@ open class AlbumAdapter(
|
|||
holder.paletteColorContainer?.setBackgroundColor(color.backgroundColor)
|
||||
}
|
||||
holder.mask?.backgroundTintList = ColorStateList.valueOf(color.primaryTextColor)
|
||||
holder.imageContainerCard?.setCardBackgroundColor(color.backgroundColor) }
|
||||
holder.imageContainerCard?.setCardBackgroundColor(color.backgroundColor)
|
||||
}
|
||||
|
||||
protected open fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
||||
if (holder.image == null) {
|
||||
return
|
||||
}
|
||||
|
||||
AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
||||
.checkIgnoreMediaStore()
|
||||
.generatePalette(activity)
|
||||
.build()
|
||||
val song = album.safeGetFirstSong()
|
||||
GlideApp.with(activity).asBitmapPalette().albumCoverOptions(song)
|
||||
//.checkIgnoreMediaStore()
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors, holder)
|
||||
|
@ -122,8 +135,8 @@ open class AlbumAdapter(
|
|||
return dataSet[position]
|
||||
}
|
||||
|
||||
override fun getName(album: Album): String {
|
||||
return album.title!!
|
||||
override fun getName(model: Album): String {
|
||||
return model.title
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(
|
||||
|
@ -150,7 +163,7 @@ open class AlbumAdapter(
|
|||
when (PreferenceUtil.albumSortOrder) {
|
||||
SortOrder.AlbumSortOrder.ALBUM_A_Z, SortOrder.AlbumSortOrder.ALBUM_Z_A -> sectionName =
|
||||
dataSet[position].title
|
||||
SortOrder.AlbumSortOrder.ALBUM_ARTIST -> sectionName = dataSet[position].artistName
|
||||
SortOrder.AlbumSortOrder.ALBUM_ARTIST -> sectionName = dataSet[position].albumArtist
|
||||
SortOrder.AlbumSortOrder.ALBUM_YEAR -> return MusicUtil.getYearString(
|
||||
dataSet[position].year
|
||||
)
|
||||
|
@ -161,8 +174,7 @@ open class AlbumAdapter(
|
|||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||
|
||||
init {
|
||||
setImageTransitionName("Album")
|
||||
menu?.visibility = View.GONE
|
||||
menu?.isVisible = false
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
|
@ -171,16 +183,13 @@ open class AlbumAdapter(
|
|||
toggleChecked(layoutPosition)
|
||||
} else {
|
||||
image?.let {
|
||||
ViewCompat.setTransitionName(it, "album")
|
||||
listener?.onAlbumClick(dataSet[layoutPosition].id, it)
|
||||
listener?.onAlbumClick(dataSet[layoutPosition].id, imageContainer ?: it)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View?): Boolean {
|
||||
toggleChecked(layoutPosition)
|
||||
return super.onLongClick(v)
|
||||
return toggleChecked(layoutPosition)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,22 +19,24 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.fragments.AlbumCoverStyle
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||
import code.name.monkey.retromusic.fragments.base.goToLyrics
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.misc.CustomFragmentStatePagerAdapter
|
||||
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 com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -83,12 +85,12 @@ class AlbumCoverPagerAdapter(
|
|||
|
||||
class AlbumCoverFragment : Fragment() {
|
||||
|
||||
private lateinit var albumCover: ImageView
|
||||
private var isColorReady: Boolean = false
|
||||
private lateinit var color: MediaNotificationProcessor
|
||||
private lateinit var song: Song
|
||||
private var colorReceiver: ColorReceiver? = null
|
||||
private var request: Int = 0
|
||||
private val mainActivity get() = activity as MainActivity
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -103,10 +105,10 @@ class AlbumCoverPagerAdapter(
|
|||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(getLayoutWithPlayerTheme(), container, false)
|
||||
ViewCompat.setTransitionName(view, "lyrics")
|
||||
albumCover = view.findViewById(R.id.player_image)
|
||||
view.setOnClickListener {
|
||||
showLyricsDialog()
|
||||
if (mainActivity.getBottomSheetBehavior().state == STATE_EXPANDED) {
|
||||
showLyricsDialog()
|
||||
}
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
@ -122,7 +124,7 @@ class AlbumCoverPagerAdapter(
|
|||
setTitle(song.title)
|
||||
setMessage(if (data.isNullOrEmpty()) "No lyrics found" else data)
|
||||
setNegativeButton(R.string.synced_lyrics) { _, _ ->
|
||||
NavigationUtil.goToLyrics(requireActivity())
|
||||
goToLyrics(requireActivity())
|
||||
}
|
||||
show()
|
||||
}
|
||||
|
@ -133,6 +135,7 @@ class AlbumCoverPagerAdapter(
|
|||
private fun getLayoutWithPlayerTheme(): Int {
|
||||
return when (PreferenceUtil.nowPlayingScreen) {
|
||||
Card, Fit, Tiny, Classic, Gradient, Full -> R.layout.fragment_album_full_cover
|
||||
Peek -> R.layout.fragment_peek_album_cover
|
||||
else -> {
|
||||
if (PreferenceUtil.isCarouselEffect) {
|
||||
R.layout.fragment_album_carousel_cover
|
||||
|
@ -142,7 +145,6 @@ class AlbumCoverPagerAdapter(
|
|||
AlbumCoverStyle.Flat -> R.layout.fragment_album_flat_cover
|
||||
AlbumCoverStyle.Circle -> R.layout.fragment_album_circle_cover
|
||||
AlbumCoverStyle.Card -> R.layout.fragment_album_card_cover
|
||||
AlbumCoverStyle.Material -> R.layout.fragment_album_material_cover
|
||||
AlbumCoverStyle.Full -> R.layout.fragment_album_full_cover
|
||||
AlbumCoverStyle.FullCard -> R.layout.fragment_album_full_card_cover
|
||||
}
|
||||
|
@ -153,7 +155,7 @@ class AlbumCoverPagerAdapter(
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
loadAlbumCover()
|
||||
loadAlbumCover(albumCover = view.findViewById(R.id.player_image))
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@ -161,10 +163,11 @@ class AlbumCoverPagerAdapter(
|
|||
colorReceiver = null
|
||||
}
|
||||
|
||||
private fun loadAlbumCover() {
|
||||
SongGlideRequest.Builder.from(Glide.with(requireContext()), song)
|
||||
.checkIgnoreMediaStore(requireContext())
|
||||
.generatePalette(requireContext()).build()
|
||||
private fun loadAlbumCover(albumCover: ImageView) {
|
||||
GlideApp.with(this).asBitmapPalette().songCoverOptions(song)
|
||||
//.checkIgnoreMediaStore()
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.dontAnimate()
|
||||
.into(object : RetroMusicColoredTarget(albumCover) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColor(colors)
|
||||
|
@ -200,9 +203,7 @@ class AlbumCoverPagerAdapter(
|
|||
|
||||
fun newInstance(song: Song): AlbumCoverFragment {
|
||||
val frag = AlbumCoverFragment()
|
||||
val args = Bundle()
|
||||
args.putParcelable(SONG_ARG, song)
|
||||
frag.arguments = args
|
||||
frag.arguments = bundleOf(SONG_ARG to song)
|
||||
return frag
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ package code.name.monkey.retromusic.adapter.album
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.helper.HorizontalAdapterHelper
|
||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
||||
|
@ -25,7 +26,6 @@ import code.name.monkey.retromusic.interfaces.ICabHolder
|
|||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
class HorizontalAlbumAdapter(
|
||||
activity: FragmentActivity,
|
||||
|
@ -49,10 +49,8 @@ class HorizontalAlbumAdapter(
|
|||
|
||||
override fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
||||
if (holder.image == null) return
|
||||
AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
||||
.checkIgnoreMediaStore()
|
||||
.generatePalette(activity)
|
||||
.build()
|
||||
GlideApp.with(activity).asBitmapPalette().albumCoverOptions(album.safeGetFirstSong())
|
||||
.load(RetroGlideExtension.getSongModel(album.safeGetFirstSong()))
|
||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors, holder)
|
||||
|
@ -60,7 +58,7 @@ class HorizontalAlbumAdapter(
|
|||
})
|
||||
}
|
||||
|
||||
override fun getAlbumText(album: Album): String? {
|
||||
override fun getAlbumText(album: Album): String {
|
||||
return MusicUtil.getYearString(album.year)
|
||||
}
|
||||
|
||||
|
|
|
@ -14,48 +14,55 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.adapter.artist
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
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.core.view.isVisible
|
||||
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.glide.ArtistGlideRequest
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.IAlbumArtistClickListener
|
||||
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
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.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import java.util.*
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
|
||||
class ArtistAdapter(
|
||||
val activity: FragmentActivity,
|
||||
override val activity: FragmentActivity,
|
||||
var dataSet: List<Artist>,
|
||||
var itemLayoutRes: Int,
|
||||
val ICabHolder: ICabHolder?,
|
||||
val IArtistClickListener: IArtistClickListener
|
||||
val IArtistClickListener: IArtistClickListener,
|
||||
val IAlbumArtistClickListener: IAlbumArtistClickListener? = null
|
||||
) : AbsMultiSelectAdapter<ArtistAdapter.ViewHolder, Artist>(
|
||||
activity, ICabHolder, R.menu.menu_media_selection
|
||||
), PopupTextProvider {
|
||||
|
||||
var albumArtistsOnly = false
|
||||
|
||||
init {
|
||||
this.setHasStableIds(true)
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun swapDataSet(dataSet: List<Artist>) {
|
||||
this.dataSet = dataSet
|
||||
notifyDataSetChanged()
|
||||
albumArtistsOnly = PreferenceUtil.albumArtistsOnly
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
|
@ -82,6 +89,13 @@ class ArtistAdapter(
|
|||
holder.itemView.isActivated = isChecked
|
||||
holder.title?.text = artist.name
|
||||
holder.text?.hide()
|
||||
val transitionName =
|
||||
if (albumArtistsOnly) artist.name else artist.id.toString()
|
||||
if (holder.imageContainer != null) {
|
||||
holder.imageContainer?.transitionName = transitionName
|
||||
} else {
|
||||
holder.image?.transitionName = transitionName
|
||||
}
|
||||
loadArtistImage(artist, holder)
|
||||
}
|
||||
|
||||
|
@ -98,9 +112,11 @@ class ArtistAdapter(
|
|||
if (holder.image == null) {
|
||||
return
|
||||
}
|
||||
ArtistGlideRequest.Builder.from(Glide.with(activity), artist)
|
||||
.generatePalette(activity)
|
||||
.build()
|
||||
GlideApp.with(activity)
|
||||
.asBitmapPalette()
|
||||
.load(RetroGlideExtension.getArtistModel(artist))
|
||||
.artistImageOptions(artist)
|
||||
.transition(RetroGlideExtension.getDefaultTransition())
|
||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors, holder)
|
||||
|
@ -112,12 +128,12 @@ class ArtistAdapter(
|
|||
return dataSet.size
|
||||
}
|
||||
|
||||
override fun getIdentifier(position: Int): Artist? {
|
||||
override fun getIdentifier(position: Int): Artist {
|
||||
return dataSet[position]
|
||||
}
|
||||
|
||||
override fun getName(artist: Artist): String {
|
||||
return artist.name
|
||||
override fun getName(model: Artist): String {
|
||||
return model.name
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(
|
||||
|
@ -146,7 +162,7 @@ class ArtistAdapter(
|
|||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||
|
||||
init {
|
||||
menu?.visibility = View.GONE
|
||||
menu?.isVisible = false
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
|
@ -154,16 +170,19 @@ class ArtistAdapter(
|
|||
if (isInQuickSelectMode) {
|
||||
toggleChecked(layoutPosition)
|
||||
} else {
|
||||
val artist = dataSet[layoutPosition]
|
||||
image?.let {
|
||||
ViewCompat.setTransitionName(it, "artist")
|
||||
IArtistClickListener.onArtist(dataSet[layoutPosition].id, it)
|
||||
if (albumArtistsOnly && IAlbumArtistClickListener != null) {
|
||||
IAlbumArtistClickListener.onAlbumArtist(artist.name, imageContainer ?: it)
|
||||
} else {
|
||||
IArtistClickListener.onArtist(artist.id, imageContainer ?: it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View?): Boolean {
|
||||
toggleChecked(layoutPosition)
|
||||
return super.onLongClick(v)
|
||||
return toggleChecked(layoutPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package code.name.monkey.retromusic.adapter.backup
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.ItemListBackupBinding
|
||||
import java.io.File
|
||||
|
||||
|
||||
class BackupAdapter(
|
||||
val activity: FragmentActivity,
|
||||
var dataSet: MutableList<File>,
|
||||
val backupClickedListener: BackupClickedListener
|
||||
) : RecyclerView.Adapter<BackupAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
ItemListBackupBinding.inflate(LayoutInflater.from(activity), parent, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.binding.title.text = dataSet[position].nameWithoutExtension
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = dataSet.size
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun swapDataset(dataSet: List<File>) {
|
||||
this.dataSet = ArrayList(dataSet)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
inner class ViewHolder(val binding: ItemListBackupBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
init {
|
||||
binding.menu.setOnClickListener { view ->
|
||||
val popupMenu = PopupMenu(activity, view)
|
||||
popupMenu.inflate(R.menu.menu_backup)
|
||||
popupMenu.setOnMenuItemClickListener { menuItem ->
|
||||
return@setOnMenuItemClickListener backupClickedListener.onBackupMenuClicked(
|
||||
dataSet[bindingAdapterPosition],
|
||||
menuItem
|
||||
)
|
||||
}
|
||||
popupMenu.show()
|
||||
}
|
||||
itemView.setOnClickListener {
|
||||
backupClickedListener.onBackupClicked(dataSet[bindingAdapterPosition])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface BackupClickedListener {
|
||||
fun onBackupClicked(file: File)
|
||||
|
||||
fun onBackupMenuClicked(file: File, menuItem: MenuItem): Boolean
|
||||
}
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.base;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import androidx.annotation.MenuRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder;
|
||||
import com.afollestad.materialcab.MaterialCab;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I>
|
||||
extends RecyclerView.Adapter<V> implements MaterialCab.Callback {
|
||||
|
||||
@Nullable private final ICabHolder ICabHolder;
|
||||
private final Context context;
|
||||
private MaterialCab cab;
|
||||
private List<I> checked;
|
||||
private int menuRes;
|
||||
|
||||
public AbsMultiSelectAdapter(
|
||||
@NonNull Context context, @Nullable ICabHolder ICabHolder, @MenuRes int menuRes) {
|
||||
this.ICabHolder = ICabHolder;
|
||||
checked = new ArrayList<>();
|
||||
this.menuRes = menuRes;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCabCreated(MaterialCab materialCab, Menu menu) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCabFinished(MaterialCab materialCab) {
|
||||
clearChecked();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCabItemClicked(MenuItem menuItem) {
|
||||
if (menuItem.getItemId() == R.id.action_multi_select_adapter_check_all) {
|
||||
checkAll();
|
||||
} else {
|
||||
onMultipleItemAction(menuItem, new ArrayList<>(checked));
|
||||
cab.finish();
|
||||
clearChecked();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void checkAll() {
|
||||
if (ICabHolder != null) {
|
||||
checked.clear();
|
||||
for (int i = 0; i < getItemCount(); i++) {
|
||||
I identifier = getIdentifier(i);
|
||||
if (identifier != null) {
|
||||
checked.add(identifier);
|
||||
}
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
updateCab();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract I getIdentifier(int position);
|
||||
|
||||
protected String getName(I object) {
|
||||
return object.toString();
|
||||
}
|
||||
|
||||
protected boolean isChecked(I identifier) {
|
||||
return checked.contains(identifier);
|
||||
}
|
||||
|
||||
protected boolean isInQuickSelectMode() {
|
||||
return cab != null && cab.isActive();
|
||||
}
|
||||
|
||||
protected abstract void onMultipleItemAction(MenuItem menuItem, List<I> selection);
|
||||
|
||||
protected void setMultiSelectMenuRes(@MenuRes int menuRes) {
|
||||
this.menuRes = menuRes;
|
||||
}
|
||||
|
||||
protected boolean toggleChecked(final int position) {
|
||||
if (ICabHolder != null) {
|
||||
I identifier = getIdentifier(position);
|
||||
if (identifier == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checked.remove(identifier)) {
|
||||
checked.add(identifier);
|
||||
}
|
||||
|
||||
notifyItemChanged(position);
|
||||
updateCab();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void clearChecked() {
|
||||
checked.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void updateCab() {
|
||||
if (ICabHolder != null) {
|
||||
if (cab == null || !cab.isActive()) {
|
||||
cab = ICabHolder.openCab(menuRes, this);
|
||||
}
|
||||
final int size = checked.size();
|
||||
if (size <= 0) {
|
||||
cab.finish();
|
||||
} else if (size == 1) {
|
||||
cab.setTitle(getName(checked.get(0)));
|
||||
} else {
|
||||
cab.setTitle(context.getString(R.string.x_selected, size));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package code.name.monkey.retromusic.adapter.base
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Color
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.annotation.MenuRes
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.interfaces.ICabCallback
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import com.afollestad.materialcab.attached.AttachedCab
|
||||
import com.afollestad.materialcab.attached.destroy
|
||||
import com.afollestad.materialcab.attached.isActive
|
||||
|
||||
abstract class AbsMultiSelectAdapter<V : RecyclerView.ViewHolder?, I>(
|
||||
open val activity: FragmentActivity, private val ICabHolder: ICabHolder?, @MenuRes menuRes: Int
|
||||
) : RecyclerView.Adapter<V>(), ICabCallback {
|
||||
private var cab: AttachedCab? = null
|
||||
private val checked: MutableList<I>
|
||||
private var menuRes: Int
|
||||
override fun onCabCreated(cab: AttachedCab, menu: Menu): Boolean {
|
||||
activity.window.statusBarColor =
|
||||
RetroColorUtil.shiftBackgroundColor(activity.surfaceColor())
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onCabFinished(cab: AttachedCab): Boolean {
|
||||
clearChecked()
|
||||
activity.window.statusBarColor = when {
|
||||
VersionUtils.hasMarshmallow() -> Color.TRANSPARENT
|
||||
else -> Color.BLACK
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onCabItemClicked(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.action_multi_select_adapter_check_all) {
|
||||
checkAll()
|
||||
} else {
|
||||
onMultipleItemAction(item, ArrayList(checked))
|
||||
cab?.destroy()
|
||||
clearChecked()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun checkAll() {
|
||||
if (ICabHolder != null) {
|
||||
checked.clear()
|
||||
for (i in 0 until itemCount) {
|
||||
val identifier = getIdentifier(i)
|
||||
if (identifier != null) {
|
||||
checked.add(identifier)
|
||||
}
|
||||
}
|
||||
notifyDataSetChanged()
|
||||
updateCab()
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun getIdentifier(position: Int): I?
|
||||
|
||||
protected abstract fun getName(model: I): String?
|
||||
|
||||
protected fun isChecked(identifier: I): Boolean {
|
||||
return checked.contains(identifier)
|
||||
}
|
||||
|
||||
protected val isInQuickSelectMode: Boolean
|
||||
get() = cab != null && cab!!.isActive()
|
||||
|
||||
protected abstract fun onMultipleItemAction(menuItem: MenuItem, selection: List<I>)
|
||||
protected fun setMultiSelectMenuRes(@MenuRes menuRes: Int) {
|
||||
this.menuRes = menuRes
|
||||
}
|
||||
|
||||
protected fun toggleChecked(position: Int): Boolean {
|
||||
if (ICabHolder != null) {
|
||||
val identifier = getIdentifier(position) ?: return false
|
||||
if (!checked.remove(identifier)) {
|
||||
checked.add(identifier)
|
||||
}
|
||||
notifyItemChanged(position)
|
||||
updateCab()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun clearChecked() {
|
||||
checked.clear()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
@SuppressLint("StringFormatInvalid", "StringFormatMatches")
|
||||
private fun updateCab() {
|
||||
if (ICabHolder != null) {
|
||||
if (cab == null || !cab!!.isActive()) {
|
||||
cab = ICabHolder.openCab(menuRes, this)
|
||||
}
|
||||
val size = checked.size
|
||||
when {
|
||||
size <= 0 -> {
|
||||
cab?.destroy()
|
||||
}
|
||||
size == 1 -> {
|
||||
cab?.title(literal = getName(checked[0]))
|
||||
}
|
||||
else -> {
|
||||
cab?.title(literal = activity.getString(R.string.x_selected, size))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
checked = ArrayList()
|
||||
this.menuRes = menuRes
|
||||
}
|
||||
}
|
|
@ -16,19 +16,19 @@ package code.name.monkey.retromusic.adapter.base;
|
|||
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableSwipeableItemViewHolder;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
|
||||
public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHolder
|
||||
implements View.OnLongClickListener, View.OnClickListener {
|
||||
|
||||
|
@ -41,15 +41,12 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
|
|||
@Nullable
|
||||
public ImageView image;
|
||||
|
||||
@Nullable
|
||||
public ImageView artistImage;
|
||||
|
||||
@Nullable
|
||||
public ImageView playerImage;
|
||||
|
||||
@Nullable
|
||||
public MaterialCardView imageContainerCard;
|
||||
|
||||
@Nullable
|
||||
public FrameLayout imageContainer;
|
||||
|
||||
@Nullable
|
||||
public TextView imageText;
|
||||
|
||||
|
@ -65,10 +62,6 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
|
|||
@Nullable
|
||||
public View paletteColorContainer;
|
||||
|
||||
|
||||
@Nullable
|
||||
public RecyclerView recyclerView;
|
||||
|
||||
@Nullable
|
||||
public TextView text;
|
||||
|
||||
|
@ -88,18 +81,16 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
|
|||
text2 = itemView.findViewById(R.id.text2);
|
||||
|
||||
image = itemView.findViewById(R.id.image);
|
||||
artistImage = itemView.findViewById(R.id.artistImage);
|
||||
playerImage = itemView.findViewById(R.id.player_image);
|
||||
time = itemView.findViewById(R.id.time);
|
||||
|
||||
imageText = itemView.findViewById(R.id.imageText);
|
||||
imageTextContainer = itemView.findViewById(R.id.imageTextContainer);
|
||||
imageContainerCard = itemView.findViewById(R.id.imageContainerCard);
|
||||
imageContainer = itemView.findViewById(R.id.imageContainer);
|
||||
|
||||
menu = itemView.findViewById(R.id.menu);
|
||||
dragView = itemView.findViewById(R.id.drag_view);
|
||||
paletteColorContainer = itemView.findViewById(R.id.paletteColorContainer);
|
||||
recyclerView = itemView.findViewById(R.id.recycler_view);
|
||||
mask = itemView.findViewById(R.id.mask);
|
||||
dummyContainer = itemView.findViewById(R.id.dummy_view);
|
||||
|
||||
|
|
|
@ -15,42 +15,36 @@
|
|||
package code.name.monkey.retromusic.adapter.playlist
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.setPadding
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
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.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||
import code.name.monkey.retromusic.db.SongEntity
|
||||
import code.name.monkey.retromusic.db.toSongs
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.extensions.dipToPix
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.playlistPreview.PlaylistPreview
|
||||
import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder
|
||||
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
|
||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.interfaces.IPlaylistClickListener
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.AutoGeneratedPlaylistBitmap
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
|
||||
class PlaylistAdapter(
|
||||
private val activity: FragmentActivity,
|
||||
private var dataSet: List<PlaylistWithSongs>,
|
||||
override val activity: FragmentActivity,
|
||||
var dataSet: List<PlaylistWithSongs>,
|
||||
private var itemLayoutRes: Int,
|
||||
ICabHolder: ICabHolder?,
|
||||
private val listener: IPlaylistClickListener
|
||||
|
@ -58,7 +52,7 @@ class PlaylistAdapter(
|
|||
activity,
|
||||
ICabHolder,
|
||||
R.menu.menu_playlists_selection
|
||||
) {
|
||||
), PopupTextProvider {
|
||||
|
||||
init {
|
||||
setHasStableIds(true)
|
||||
|
@ -83,44 +77,51 @@ class PlaylistAdapter(
|
|||
}
|
||||
|
||||
private fun getPlaylistTitle(playlist: PlaylistEntity): String {
|
||||
return if (TextUtils.isEmpty(playlist.playlistName)) "-" else playlist.playlistName
|
||||
return playlist.playlistName.ifEmpty { "-" }
|
||||
}
|
||||
|
||||
private fun getPlaylistText(playlist: PlaylistWithSongs): String {
|
||||
return MusicUtil.getPlaylistInfoString(activity, playlist.songs.toSongs())
|
||||
}
|
||||
|
||||
override fun getPopupText(position: Int): String {
|
||||
val sectionName: String = when (PreferenceUtil.playlistSortOrder) {
|
||||
PlaylistSortOrder.PLAYLIST_A_Z, PlaylistSortOrder.PLAYLIST_Z_A -> dataSet[position].playlistEntity.playlistName
|
||||
PlaylistSortOrder.PLAYLIST_SONG_COUNT, PlaylistSortOrder.PLAYLIST_SONG_COUNT_DESC -> dataSet[position].songs.size.toString()
|
||||
else -> {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return MusicUtil.getSectionName(sectionName)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val playlist = dataSet[position]
|
||||
holder.itemView.isActivated = isChecked(playlist)
|
||||
holder.title?.text = getPlaylistTitle(playlist.playlistEntity)
|
||||
holder.text?.text = getPlaylistText(playlist)
|
||||
holder.image?.setImageDrawable(getIconRes())
|
||||
val isChecked = isChecked(playlist)
|
||||
if (isChecked) {
|
||||
holder.menu?.hide()
|
||||
} else {
|
||||
holder.menu?.show()
|
||||
}
|
||||
//playlistBitmapLoader(activity, holder, playlist)
|
||||
holder.menu?.isGone = isChecked(playlist)
|
||||
GlideApp.with(activity)
|
||||
.load(
|
||||
if (itemLayoutRes == R.layout.item_list) {
|
||||
holder.image?.setPadding(activity.dipToPix(8F).toInt())
|
||||
R.drawable.ic_playlist_play
|
||||
} else PlaylistPreview(playlist)
|
||||
)
|
||||
.playlistOptions()
|
||||
.into(holder.image!!)
|
||||
}
|
||||
|
||||
private fun getIconRes(): Drawable = TintHelper.createTintedDrawable(
|
||||
activity,
|
||||
R.drawable.ic_playlist_play,
|
||||
ATHUtil.resolveColor(activity, R.attr.colorControlNormal)
|
||||
)
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
||||
override fun getIdentifier(position: Int): PlaylistWithSongs? {
|
||||
override fun getIdentifier(position: Int): PlaylistWithSongs {
|
||||
return dataSet[position]
|
||||
}
|
||||
|
||||
override fun getName(playlist: PlaylistWithSongs): String {
|
||||
return playlist.playlistEntity.playlistName
|
||||
override fun getName(model: PlaylistWithSongs): String {
|
||||
return model.playlistEntity.playlistName
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<PlaylistWithSongs>) {
|
||||
|
@ -141,18 +142,8 @@ class PlaylistAdapter(
|
|||
return songs
|
||||
}
|
||||
|
||||
private fun getSongs(playlist: PlaylistWithSongs): List<SongEntity> =
|
||||
mutableListOf<SongEntity>().apply {
|
||||
addAll(playlist.songs)
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||
init {
|
||||
image?.apply {
|
||||
val iconPadding =
|
||||
activity.resources.getDimensionPixelSize(R.dimen.list_item_image_icon_padding)
|
||||
setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
|
||||
}
|
||||
menu?.setOnClickListener { view ->
|
||||
val popupMenu = PopupMenu(activity, view)
|
||||
popupMenu.inflate(R.menu.menu_item_playlist)
|
||||
|
@ -172,7 +163,7 @@ class PlaylistAdapter(
|
|||
if (isInQuickSelectMode) {
|
||||
toggleChecked(layoutPosition)
|
||||
} else {
|
||||
ViewCompat.setTransitionName(itemView, "playlist")
|
||||
itemView.transitionName = "playlist"
|
||||
listener.onPlaylistClick(dataSet[layoutPosition], itemView)
|
||||
}
|
||||
}
|
||||
|
@ -183,37 +174,6 @@ class PlaylistAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun playlistBitmapLoader(
|
||||
activity: FragmentActivity,
|
||||
viewHolder: ViewHolder,
|
||||
playlist: PlaylistWithSongs
|
||||
) {
|
||||
|
||||
activity.lifecycleScope.launch(IO) {
|
||||
val songs = playlist.songs.toSongs()
|
||||
val bitmap = AutoGeneratedPlaylistBitmap.getBitmap(activity, songs, false, false)
|
||||
withContext(Main) { viewHolder.image?.setImageBitmap(bitmap) }
|
||||
}
|
||||
|
||||
/*
|
||||
override fun doInBackground(vararg params: Void?): Bitmap {
|
||||
val songs = playlist.songs.toSongs()
|
||||
return AutoGeneratedPlaylistBitmap.getBitmap(activity, songs, false, false)
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: Bitmap?) {
|
||||
super.onPostExecute(result)
|
||||
viewHolder.image?.setImageBitmap(result)
|
||||
val color = RetroColorUtil.getColor(
|
||||
RetroColorUtil.generatePalette(
|
||||
result
|
||||
),
|
||||
ATHUtil.resolveColor(activity, R.attr.colorSurface)
|
||||
)
|
||||
viewHolder.paletteColorContainer?.setBackgroundColor(color)
|
||||
}*/
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = PlaylistAdapter::class.java.simpleName
|
||||
}
|
||||
|
|
|
@ -16,106 +16,96 @@ package code.name.monkey.retromusic.adapter.song
|
|||
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.R.menu
|
||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.toSongEntity
|
||||
import code.name.monkey.retromusic.db.toSongs
|
||||
import code.name.monkey.retromusic.db.toSongsEntity
|
||||
import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.accentOutlineColor
|
||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.model.PlaylistSong
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.ViewUtil
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemViewHolder
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
class OrderablePlaylistSongAdapter(
|
||||
private val playlist: PlaylistEntity,
|
||||
activity: FragmentActivity,
|
||||
dataSet: ArrayList<Song>,
|
||||
dataSet: MutableList<Song>,
|
||||
itemLayoutRes: Int,
|
||||
ICabHolder: ICabHolder?,
|
||||
private val onMoveItemListener: OnMoveItemListener?
|
||||
) : SongAdapter(
|
||||
activity,
|
||||
dataSet,
|
||||
itemLayoutRes,
|
||||
ICabHolder
|
||||
), DraggableItemAdapter<OrderablePlaylistSongAdapter.ViewHolder> {
|
||||
) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, ICabHolder),
|
||||
DraggableItemAdapter<OrderablePlaylistSongAdapter.ViewHolder> {
|
||||
|
||||
val libraryViewModel: LibraryViewModel by activity.viewModel()
|
||||
|
||||
init {
|
||||
setMultiSelectMenuRes(menu.menu_playlists_songs_selection)
|
||||
this.setHasStableIds(true)
|
||||
this.setMultiSelectMenuRes(R.menu.menu_playlists_songs_selection)
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
// requires static value, it means need to keep the same value
|
||||
// even if the item position has been changed.
|
||||
return if (position != 0) {
|
||||
dataSet[position - 1].id
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
var positionFinal = position
|
||||
positionFinal--
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (position == 0) OFFSET_ITEM else SONG
|
||||
}
|
||||
|
||||
var long: Long = 0
|
||||
if (positionFinal < 0) {
|
||||
long = -2
|
||||
} else {
|
||||
if (dataSet[positionFinal] is PlaylistSong) {
|
||||
long = (dataSet[positionFinal] as PlaylistSong).idInPlayList.toLong()
|
||||
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
|
||||
if (holder.itemViewType == OFFSET_ITEM) {
|
||||
val viewHolder = holder as ViewHolder
|
||||
viewHolder.playAction?.let {
|
||||
it.setOnClickListener {
|
||||
MusicPlayerRemote.openQueue(dataSet, 0, true)
|
||||
}
|
||||
it.accentOutlineColor()
|
||||
}
|
||||
viewHolder.shuffleAction?.let {
|
||||
it.setOnClickListener {
|
||||
MusicPlayerRemote.openAndShuffleQueue(dataSet, true)
|
||||
}
|
||||
it.accentColor()
|
||||
}
|
||||
} else {
|
||||
super.onBindViewHolder(holder, position - 1)
|
||||
}
|
||||
return long
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<Song>) {
|
||||
when (menuItem.itemId) {
|
||||
R.id.action_remove_from_playlist -> {
|
||||
RemoveSongFromPlaylistDialog.create(selection.toSongs(playlist.playListId))
|
||||
.show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
|
||||
return
|
||||
}
|
||||
}
|
||||
super.onMultipleItemAction(menuItem, selection)
|
||||
}
|
||||
|
||||
override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean {
|
||||
return onMoveItemListener != null && position > 0 && (ViewUtil.hitTest(
|
||||
holder.dragView!!, x, y
|
||||
) || ViewUtil.hitTest(holder.image!!, x, y))
|
||||
}
|
||||
|
||||
override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange {
|
||||
return ItemDraggableRange(1, dataSet.size)
|
||||
}
|
||||
|
||||
override fun onMoveItem(fromPosition: Int, toPosition: Int) {
|
||||
if (onMoveItemListener != null && fromPosition != toPosition) {
|
||||
onMoveItemListener.onMoveItem(fromPosition - 1, toPosition - 1)
|
||||
R.id.action_remove_from_playlist -> RemoveSongFromPlaylistDialog.create(
|
||||
selection.toSongsEntity(
|
||||
playlist
|
||||
)
|
||||
)
|
||||
.show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
|
||||
else -> super.onMultipleItemAction(menuItem, selection)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean {
|
||||
return dropPosition > 0
|
||||
}
|
||||
|
||||
override fun onItemDragStarted(position: Int) {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onItemDragFinished(fromPosition: Int, toPosition: Int, result: Boolean) {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
interface OnMoveItemListener {
|
||||
fun onMoveItem(fromPosition: Int, toPosition: Int)
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : SongAdapter.ViewHolder(itemView),
|
||||
DraggableItemViewHolder {
|
||||
@DraggableItemStateFlags
|
||||
private var mDragStateFlags: Int = 0
|
||||
inner class ViewHolder(itemView: View) : AbsOffsetSongAdapter.ViewHolder(itemView) {
|
||||
val playAction: MaterialButton? = itemView.findViewById(R.id.playAction)
|
||||
val shuffleAction: MaterialButton? = itemView.findViewById(R.id.shuffleAction)
|
||||
|
||||
override var songMenuRes: Int
|
||||
get() = R.menu.menu_item_playlist_song
|
||||
|
@ -123,16 +113,6 @@ class OrderablePlaylistSongAdapter(
|
|||
super.songMenuRes = value
|
||||
}
|
||||
|
||||
init {
|
||||
if (dragView != null) {
|
||||
if (onMoveItemListener != null) {
|
||||
dragView?.visibility = View.VISIBLE
|
||||
} else {
|
||||
dragView?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSongMenuItemClick(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_remove_from_playlist -> {
|
||||
|
@ -144,13 +124,49 @@ class OrderablePlaylistSongAdapter(
|
|||
return super.onSongMenuItemClick(item)
|
||||
}
|
||||
|
||||
@DraggableItemStateFlags
|
||||
override fun getDragStateFlags(): Int {
|
||||
return mDragStateFlags
|
||||
init {
|
||||
dragView?.isVisible = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun setDragStateFlags(@DraggableItemStateFlags flags: Int) {
|
||||
mDragStateFlags = flags
|
||||
override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean {
|
||||
if (dataSet.size == 0 or 1 || isInQuickSelectMode) {
|
||||
return false
|
||||
}
|
||||
val dragHandle = holder.dragView ?: return false
|
||||
|
||||
val handleWidth = dragHandle.width
|
||||
val handleHeight = dragHandle.height
|
||||
val handleLeft = dragHandle.left
|
||||
val handleTop = dragHandle.top
|
||||
|
||||
return (x >= handleLeft && x < handleLeft + handleWidth &&
|
||||
y >= handleTop && y < handleTop + handleHeight) && position != 0
|
||||
}
|
||||
|
||||
override fun onMoveItem(fromPosition: Int, toPosition: Int) {
|
||||
dataSet.add(toPosition - 1, dataSet.removeAt(fromPosition - 1))
|
||||
}
|
||||
|
||||
override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange {
|
||||
return ItemDraggableRange(1, itemCount - 1)
|
||||
}
|
||||
|
||||
override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onItemDragStarted(position: Int) {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onItemDragFinished(fromPosition: Int, toPosition: Int, result: Boolean) {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun saveSongs(playlistEntity: PlaylistEntity) {
|
||||
activity.lifecycleScope.launch(Dispatchers.IO) {
|
||||
libraryViewModel.insertSongs(dataSet.toSongsEntity(playlistEntity))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@ package code.name.monkey.retromusic.adapter.song
|
|||
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.isPlaying
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.playNextSong
|
||||
|
@ -28,8 +28,6 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote.removeFromQueue
|
|||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.ViewUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags
|
||||
|
@ -41,7 +39,7 @@ import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultAct
|
|||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
|
||||
class PlayingQueueAdapter(
|
||||
activity: AppCompatActivity,
|
||||
activity: FragmentActivity,
|
||||
dataSet: MutableList<Song>,
|
||||
private var current: Int,
|
||||
itemLayoutRes: Int
|
||||
|
@ -79,14 +77,10 @@ class PlayingQueueAdapter(
|
|||
if (holder.image == null) {
|
||||
return
|
||||
}
|
||||
SongGlideRequest.Builder.from(Glide.with(activity), song)
|
||||
.checkIgnoreMediaStore(activity)
|
||||
.generatePalette(activity).build()
|
||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
//setColors(colors, holder)
|
||||
}
|
||||
})
|
||||
GlideApp.with(activity)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.songCoverOptions(song)
|
||||
.into(holder.image!!)
|
||||
}
|
||||
|
||||
fun swapDataSet(dataSet: List<Song>, position: Int) {
|
||||
|
@ -156,7 +150,7 @@ class PlayingQueueAdapter(
|
|||
}
|
||||
|
||||
init {
|
||||
dragView?.visibility = View.VISIBLE
|
||||
dragView?.isVisible = true
|
||||
}
|
||||
|
||||
override fun onSongMenuItemClick(item: MenuItem): Boolean {
|
||||
|
@ -190,7 +184,7 @@ class PlayingQueueAdapter(
|
|||
private const val UP_NEXT = 2
|
||||
}
|
||||
|
||||
override fun onSwipeItem(holder: ViewHolder, position: Int, result: Int): SwipeResultAction? {
|
||||
override fun onSwipeItem(holder: ViewHolder, position: Int, result: Int): SwipeResultAction {
|
||||
return if (result == SwipeableItemConstants.RESULT_CANCELED) {
|
||||
SwipeResultActionDefault()
|
||||
} else {
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hemanth Savarla.
|
||||
*
|
||||
* 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.adapter.song
|
||||
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.toSongEntity
|
||||
import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
|
||||
class PlaylistSongAdapter(
|
||||
private val playlist: PlaylistEntity,
|
||||
activity: FragmentActivity,
|
||||
dataSet: MutableList<Song>,
|
||||
itemLayoutRes: Int,
|
||||
iCabHolder: ICabHolder?
|
||||
) : SongAdapter(activity, dataSet, itemLayoutRes, iCabHolder) {
|
||||
|
||||
init {
|
||||
this.setMultiSelectMenuRes(R.menu.menu_cannot_delete_single_songs_playlist_songs_selection)
|
||||
}
|
||||
|
||||
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
open inner class ViewHolder(itemView: View) : SongAdapter.ViewHolder(itemView) {
|
||||
|
||||
override var songMenuRes: Int
|
||||
get() = R.menu.menu_item_playlist_song
|
||||
set(value) {
|
||||
super.songMenuRes = value
|
||||
}
|
||||
|
||||
override fun onSongMenuItemClick(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_remove_from_playlist -> {
|
||||
RemoveSongFromPlaylistDialog.create(song.toSongEntity(playlist.playListId))
|
||||
.show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onSongMenuItemClick(item)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,14 +15,16 @@
|
|||
package code.name.monkey.retromusic.adapter.song
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.applyColor
|
||||
import code.name.monkey.retromusic.extensions.applyOutlineColor
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.accentOutlineColor
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.google.android.material.button.MaterialButton
|
||||
|
||||
class ShuffleButtonSongAdapter(
|
||||
|
@ -32,28 +34,36 @@ class ShuffleButtonSongAdapter(
|
|||
ICabHolder: ICabHolder?
|
||||
) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, ICabHolder) {
|
||||
|
||||
|
||||
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (position == 0) OFFSET_ITEM else SONG
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
|
||||
if (holder.itemViewType == OFFSET_ITEM) {
|
||||
val color = ThemeStore.accentColor(activity)
|
||||
val viewHolder = holder as ViewHolder
|
||||
viewHolder.playAction?.let {
|
||||
it.setOnClickListener {
|
||||
MusicPlayerRemote.openQueue(dataSet, 0, true)
|
||||
}
|
||||
it.applyOutlineColor(color)
|
||||
it.accentOutlineColor()
|
||||
}
|
||||
viewHolder.shuffleAction?.let {
|
||||
it.setOnClickListener {
|
||||
MusicPlayerRemote.openAndShuffleQueue(dataSet, true)
|
||||
}
|
||||
it.applyColor(color)
|
||||
it.accentColor()
|
||||
}
|
||||
} else {
|
||||
super.onBindViewHolder(holder, position - 1)
|
||||
val landscape = RetroUtil.isLandscape
|
||||
if ((PreferenceUtil.songGridSize > 2 && !landscape) || (PreferenceUtil.songGridSizeLand > 5 && landscape)) {
|
||||
holder.menu?.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,4 +79,5 @@ class ShuffleButtonSongAdapter(
|
|||
super.onClick(v)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import androidx.fragment.app.FragmentActivity
|
|||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import java.util.*
|
||||
|
||||
class SimpleSongAdapter(
|
||||
context: FragmentActivity,
|
||||
|
|
|
@ -21,6 +21,7 @@ import android.view.MenuItem
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.navigation.findNavController
|
||||
|
@ -28,21 +29,20 @@ 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
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.SortOrder
|
||||
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.ICabCallback
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.bumptech.glide.Glide
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
|
||||
/**
|
||||
|
@ -50,7 +50,7 @@ import me.zhanghai.android.fastscroll.PopupTextProvider
|
|||
*/
|
||||
|
||||
open class SongAdapter(
|
||||
protected val activity: FragmentActivity,
|
||||
override val activity: FragmentActivity,
|
||||
var dataSet: MutableList<Song>,
|
||||
protected var itemLayoutRes: Int,
|
||||
ICabHolder: ICabHolder?,
|
||||
|
@ -59,7 +59,7 @@ open class SongAdapter(
|
|||
activity,
|
||||
ICabHolder,
|
||||
R.menu.menu_media_selection
|
||||
), MaterialCab.Callback, PopupTextProvider {
|
||||
), ICabCallback, PopupTextProvider {
|
||||
|
||||
private var showSectionName = true
|
||||
|
||||
|
@ -95,15 +95,15 @@ open class SongAdapter(
|
|||
val song = dataSet[position]
|
||||
val isChecked = isChecked(song)
|
||||
holder.itemView.isActivated = isChecked
|
||||
if (isChecked) {
|
||||
holder.menu?.hide()
|
||||
} else {
|
||||
holder.menu?.show()
|
||||
}
|
||||
holder.menu?.isGone = isChecked
|
||||
holder.title?.text = getSongTitle(song)
|
||||
holder.text?.text = getSongText(song)
|
||||
holder.text2?.text = getSongText(song)
|
||||
loadAlbumCover(song, holder)
|
||||
val landscape = RetroUtil.isLandscape
|
||||
if ((PreferenceUtil.songGridSize > 2 && !landscape) || (PreferenceUtil.songGridSizeLand > 5 && landscape)) {
|
||||
holder.menu?.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) {
|
||||
|
@ -111,7 +111,7 @@ open class SongAdapter(
|
|||
holder.title?.setTextColor(color.primaryTextColor)
|
||||
holder.text?.setTextColor(color.secondaryTextColor)
|
||||
holder.paletteColorContainer?.setBackgroundColor(color.backgroundColor)
|
||||
holder.menu?.imageTintList= ColorStateList.valueOf(color.primaryTextColor)
|
||||
holder.menu?.imageTintList = ColorStateList.valueOf(color.primaryTextColor)
|
||||
}
|
||||
holder.mask?.backgroundTintList = ColorStateList.valueOf(color.primaryTextColor)
|
||||
}
|
||||
|
@ -120,9 +120,8 @@ open class SongAdapter(
|
|||
if (holder.image == null) {
|
||||
return
|
||||
}
|
||||
SongGlideRequest.Builder.from(Glide.with(activity), song)
|
||||
.checkIgnoreMediaStore(activity)
|
||||
.generatePalette(activity).build()
|
||||
GlideApp.with(activity).asBitmapPalette().songCoverOptions(song)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors, holder)
|
||||
|
@ -130,15 +129,15 @@ open class SongAdapter(
|
|||
})
|
||||
}
|
||||
|
||||
private fun getSongTitle(song: Song): String? {
|
||||
private fun getSongTitle(song: Song): String {
|
||||
return song.title
|
||||
}
|
||||
|
||||
private fun getSongText(song: Song): String? {
|
||||
private fun getSongText(song: Song): String {
|
||||
return song.artistName
|
||||
}
|
||||
|
||||
private fun getSongText2(song: Song): String? {
|
||||
private fun getSongText2(song: Song): String {
|
||||
return song.albumName
|
||||
}
|
||||
|
||||
|
@ -150,8 +149,8 @@ open class SongAdapter(
|
|||
return dataSet[position]
|
||||
}
|
||||
|
||||
override fun getName(song: Song): String {
|
||||
return song.title
|
||||
override fun getName(model: Song): String {
|
||||
return model.title
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<Song>) {
|
||||
|
@ -165,6 +164,7 @@ open class SongAdapter(
|
|||
SortOrder.SongSortOrder.SONG_ARTIST -> dataSet[position].artistName
|
||||
SortOrder.SongSortOrder.SONG_YEAR -> return MusicUtil.getYearString(dataSet[position].year)
|
||||
SortOrder.SongSortOrder.COMPOSER -> dataSet[position].composer
|
||||
SortOrder.SongSortOrder.SONG_ALBUM_ARTIST -> dataSet[position].albumArtist
|
||||
else -> {
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -15,18 +15,16 @@
|
|||
package code.name.monkey.retromusic.appshortcuts
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Build
|
||||
import android.util.TypedValue
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.getTintedDrawable
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N_MR1)
|
||||
object AppShortcutIconGenerator {
|
||||
|
@ -63,28 +61,17 @@ object AppShortcutIconGenerator {
|
|||
context: Context,
|
||||
iconId: Int,
|
||||
foregroundColor: Int,
|
||||
backgroundColor: Int
|
||||
backgroundColor: Int,
|
||||
): Icon {
|
||||
// Get and tint foreground and background drawables
|
||||
val vectorDrawable = RetroUtil.getTintedVectorDrawable(context, iconId, foregroundColor)
|
||||
val backgroundDrawable = RetroUtil.getTintedVectorDrawable(
|
||||
context, R.drawable.ic_app_shortcut_background, backgroundColor
|
||||
)
|
||||
val vectorDrawable = context.getTintedDrawable(iconId, foregroundColor)
|
||||
val backgroundDrawable =
|
||||
context.getTintedDrawable(R.drawable.ic_app_shortcut_background, backgroundColor)
|
||||
|
||||
// Squash the two drawables together
|
||||
val layerDrawable = LayerDrawable(arrayOf(backgroundDrawable, vectorDrawable))
|
||||
|
||||
// Return as an Icon
|
||||
return Icon.createWithBitmap(drawableToBitmap(layerDrawable))
|
||||
}
|
||||
|
||||
private fun drawableToBitmap(drawable: Drawable): Bitmap {
|
||||
val bitmap = Bitmap.createBitmap(
|
||||
drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
|
||||
)
|
||||
val canvas = Canvas(bitmap)
|
||||
drawable.setBounds(0, 0, canvas.width, canvas.height)
|
||||
drawable.draw(canvas)
|
||||
return bitmap
|
||||
return Icon.createWithBitmap(layerDrawable.toBitmap())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package code.name.monkey.retromusic.appshortcuts
|
|||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.core.os.bundleOf
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType
|
||||
|
@ -26,7 +27,11 @@ import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist
|
|||
import code.name.monkey.retromusic.model.smartplaylist.ShuffleAllPlaylist
|
||||
import code.name.monkey.retromusic.model.smartplaylist.TopTracksPlaylist
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_PLAY_PLAYLIST
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.INTENT_EXTRA_PLAYLIST
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.INTENT_EXTRA_SHUFFLE_MODE
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.SHUFFLE_MODE_NONE
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.SHUFFLE_MODE_SHUFFLE
|
||||
|
||||
class AppShortcutLauncherActivity : Activity() {
|
||||
|
||||
|
@ -59,9 +64,10 @@ class AppShortcutLauncherActivity : Activity() {
|
|||
val intent = Intent(this, MusicService::class.java)
|
||||
intent.action = ACTION_PLAY_PLAYLIST
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(INTENT_EXTRA_PLAYLIST, playlist)
|
||||
bundle.putInt(INTENT_EXTRA_SHUFFLE_MODE, shuffleMode)
|
||||
val bundle = bundleOf(
|
||||
INTENT_EXTRA_PLAYLIST to playlist,
|
||||
INTENT_EXTRA_SHUFFLE_MODE to shuffleMode
|
||||
)
|
||||
|
||||
intent.putExtras(bundle)
|
||||
|
||||
|
|
|
@ -21,14 +21,15 @@ import android.content.pm.ShortcutInfo
|
|||
import android.content.pm.ShortcutManager
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Build
|
||||
import androidx.core.content.getSystemService
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||
class DynamicShortcutManager(private val context: Context) {
|
||||
private val shortcutManager: ShortcutManager =
|
||||
this.context.getSystemService(ShortcutManager::class.java)
|
||||
private val shortcutManager: ShortcutManager? =
|
||||
this.context.getSystemService()
|
||||
|
||||
private val defaultShortcuts: List<ShortcutInfo>
|
||||
get() = listOf(
|
||||
|
@ -39,12 +40,12 @@ class DynamicShortcutManager(private val context: Context) {
|
|||
|
||||
fun initDynamicShortcuts() {
|
||||
// if (shortcutManager.dynamicShortcuts.size == 0) {
|
||||
shortcutManager.dynamicShortcuts = defaultShortcuts
|
||||
shortcutManager?.dynamicShortcuts = defaultShortcuts
|
||||
// }
|
||||
}
|
||||
|
||||
fun updateDynamicShortcuts() {
|
||||
shortcutManager.updateShortcuts(defaultShortcuts)
|
||||
shortcutManager?.updateShortcuts(defaultShortcuts)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -66,7 +67,7 @@ class DynamicShortcutManager(private val context: Context) {
|
|||
}
|
||||
|
||||
fun reportShortcutUsed(context: Context, shortcutId: String) {
|
||||
context.getSystemService(ShortcutManager::class.java).reportShortcutUsed(shortcutId)
|
||||
context.getSystemService<ShortcutManager>()?.reportShortcutUsed(shortcutId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.core.os.bundleOf
|
||||
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||
|
@ -36,8 +36,7 @@ abstract class BaseShortcutType(internal var context: Context) {
|
|||
internal fun getPlaySongsIntent(shortcutType: Long): Intent {
|
||||
val intent = Intent(context, AppShortcutLauncherActivity::class.java)
|
||||
intent.action = Intent.ACTION_VIEW
|
||||
val b = Bundle()
|
||||
b.putLong(AppShortcutLauncherActivity.KEY_SHORTCUT_TYPE, shortcutType)
|
||||
val b = bundleOf(AppShortcutLauncherActivity.KEY_SHORTCUT_TYPE to shortcutType)
|
||||
intent.putExtras(b)
|
||||
return intent
|
||||
}
|
||||
|
|
|
@ -42,6 +42,6 @@ class LastAddedShortcutType(context: Context) : BaseShortcutType(context) {
|
|||
companion object {
|
||||
|
||||
val id: String
|
||||
get() = BaseShortcutType.ID_PREFIX + "last_added"
|
||||
get() = ID_PREFIX + "last_added"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,6 @@ class TopTracksShortcutType(context: Context) : BaseShortcutType(context) {
|
|||
companion object {
|
||||
|
||||
val id: String
|
||||
get() = BaseShortcutType.ID_PREFIX + "top_tracks"
|
||||
get() = ID_PREFIX + "top_tracks"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,22 +20,27 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.extensions.getTintedDrawable
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_REWIND
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_SKIP
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_TOGGLE_PAUSE
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
|
||||
class AppWidgetBig : BaseAppWidget() {
|
||||
private var target: Target<Bitmap>? = null // for cancellation
|
||||
|
@ -55,31 +60,24 @@ class AppWidgetBig : BaseAppWidget() {
|
|||
)
|
||||
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_next,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, false)
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_next, context.getTintedDrawable(
|
||||
R.drawable.ic_skip_next,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, false)
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_previous,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, false)
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_prev,
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_skip_previous,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, false)
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, BaseAppWidget.Companion.createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_play_arrow_white_32dp,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, false)
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_toggle_play_pause,
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_play_arrow_white_32dp,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, false)
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
linkButtons(context, appWidgetView)
|
||||
|
@ -98,7 +96,7 @@ class AppWidgetBig : BaseAppWidget() {
|
|||
val song = service.currentSong
|
||||
|
||||
// Set the titles and artwork
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||
if (song.title.isEmpty() && song.artistName.isEmpty()) {
|
||||
appWidgetView.setViewVisibility(
|
||||
R.id.media_titles,
|
||||
View.INVISIBLE
|
||||
|
@ -120,33 +118,27 @@ class AppWidgetBig : BaseAppWidget() {
|
|||
val playPauseRes =
|
||||
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play_arrow_white_32dp
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
playPauseRes,
|
||||
primaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_toggle_play_pause,
|
||||
service.getTintedDrawable(
|
||||
playPauseRes,
|
||||
primaryColor
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_next,
|
||||
primaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_next,
|
||||
service.getTintedDrawable(
|
||||
R.drawable.ic_skip_next,
|
||||
primaryColor
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_previous,
|
||||
primaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_prev,
|
||||
service.getTintedDrawable(
|
||||
R.drawable.ic_skip_previous,
|
||||
primaryColor
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
// Link actions buttons to intents
|
||||
|
@ -154,27 +146,31 @@ class AppWidgetBig : BaseAppWidget() {
|
|||
|
||||
// Load the album cover async and push the update on completion
|
||||
val p = RetroUtil.getScreenSize(service)
|
||||
val widgetImageSize = Math.min(p.x, p.y)
|
||||
val widgetImageSize = p.x.coerceAtMost(p.y)
|
||||
val appContext = service.applicationContext
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.clear(target)
|
||||
Glide.with(service).clear(target)
|
||||
}
|
||||
target = SongGlideRequest.Builder.from(Glide.with(appContext), song)
|
||||
.checkIgnoreMediaStore(appContext).asBitmap().build()
|
||||
.into(object : SimpleTarget<Bitmap>(widgetImageSize, widgetImageSize) {
|
||||
target = GlideApp.with(appContext)
|
||||
.asBitmap()
|
||||
//.checkIgnoreMediaStore()
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.into(object : CustomTarget<Bitmap>(widgetImageSize, widgetImageSize) {
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap,
|
||||
glideAnimation: GlideAnimation<in Bitmap>
|
||||
transition: Transition<in Bitmap>?,
|
||||
) {
|
||||
update(resource)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
update(null)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {}
|
||||
|
||||
private fun update(bitmap: Bitmap?) {
|
||||
if (bitmap == null) {
|
||||
appWidgetView.setImageViewResource(
|
||||
|
@ -199,13 +195,17 @@ class AppWidgetBig : BaseAppWidget() {
|
|||
MainActivity.EXPAND_PANEL,
|
||||
PreferenceUtil.isExpandPanel
|
||||
)
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
|
||||
var pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
context, 0, action, if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.clickable_area, pendingIntent)
|
||||
|
||||
// Previous track
|
||||
|
|
|
@ -20,24 +20,27 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.extensions.getTintedDrawable
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_REWIND
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_SKIP
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_TOGGLE_PAUSE
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
|
||||
class AppWidgetCard : BaseAppWidget() {
|
||||
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
||||
|
@ -53,31 +56,25 @@ class AppWidgetCard : BaseAppWidget() {
|
|||
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
|
||||
val secondaryColor = MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_next,
|
||||
secondaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_next,
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_skip_next,
|
||||
secondaryColor
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_previous,
|
||||
secondaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_prev,
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_skip_previous,
|
||||
secondaryColor
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_play_arrow_white_32dp,
|
||||
secondaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_toggle_play_pause,
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_play_arrow_white_32dp,
|
||||
secondaryColor
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
linkButtons(context, appWidgetView)
|
||||
|
@ -94,7 +91,7 @@ class AppWidgetCard : BaseAppWidget() {
|
|||
val song = service.currentSong
|
||||
|
||||
// Set the titles and artwork
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||
if (song.title.isEmpty() && song.artistName.isEmpty()) {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
} else {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
|
||||
|
@ -106,33 +103,27 @@ class AppWidgetCard : BaseAppWidget() {
|
|||
val playPauseRes =
|
||||
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play_arrow_white_32dp
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
playPauseRes,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_toggle_play_pause,
|
||||
service.getTintedDrawable(
|
||||
playPauseRes,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_next,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_next,
|
||||
service.getTintedDrawable(
|
||||
R.drawable.ic_skip_next,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_previous,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_prev,
|
||||
service.getTintedDrawable(
|
||||
R.drawable.ic_skip_previous,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
// Link actions buttons to intents
|
||||
|
@ -150,14 +141,15 @@ class AppWidgetCard : BaseAppWidget() {
|
|||
// Load the album cover async and push the update on completion
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.clear(target)
|
||||
Glide.with(service).clear(target)
|
||||
}
|
||||
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
||||
.checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop()
|
||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.centerCrop()
|
||||
.into(object : CustomTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
|
||||
transition: Transition<in BitmapPaletteWrapper>?,
|
||||
) {
|
||||
val palette = resource.palette
|
||||
update(
|
||||
|
@ -171,38 +163,31 @@ class AppWidgetCard : BaseAppWidget() {
|
|||
)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {}
|
||||
|
||||
private fun update(bitmap: Bitmap?, color: Int) {
|
||||
// Set correct drawable for pause state
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, ImageUtil.createBitmap(
|
||||
ImageUtil.getTintedVectorDrawable(
|
||||
service, playPauseRes, color
|
||||
)
|
||||
)
|
||||
R.id.button_toggle_play_pause,
|
||||
service.getTintedDrawable(playPauseRes, color).toBitmap()
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, ImageUtil.createBitmap(
|
||||
ImageUtil.getTintedVectorDrawable(
|
||||
service, R.drawable.ic_skip_next, color
|
||||
)
|
||||
)
|
||||
R.id.button_next,
|
||||
service.getTintedDrawable(R.drawable.ic_skip_next, color).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, ImageUtil.createBitmap(
|
||||
ImageUtil.getTintedVectorDrawable(
|
||||
service, R.drawable.ic_skip_previous, color
|
||||
)
|
||||
)
|
||||
R.id.button_prev,
|
||||
service.getTintedDrawable(R.drawable.ic_skip_previous, color).toBitmap()
|
||||
)
|
||||
|
||||
val image = getAlbumArtDrawable(service.resources, bitmap)
|
||||
val image = getAlbumArtDrawable(service, bitmap)
|
||||
val roundedBitmap = createRoundedBitmap(
|
||||
image, imageSize, imageSize, cardRadius, 0F, cardRadius, 0F
|
||||
)
|
||||
|
@ -223,13 +208,17 @@ class AppWidgetCard : BaseAppWidget() {
|
|||
MainActivity.EXPAND_PANEL,
|
||||
PreferenceUtil.isExpandPanel
|
||||
)
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
|
||||
var pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
context, 0, action, if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.image, pendingIntent)
|
||||
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
|
||||
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hemanth Savarla.
|
||||
*
|
||||
* 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.appwidgets
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.extensions.getTintedDrawable
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_TOGGLE_PAUSE
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.TOGGLE_FAVORITE
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
class AppWidgetCircle : BaseAppWidget() {
|
||||
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
||||
|
||||
/**
|
||||
* Initialize given widgets to default state, where we launch Music on default click and hide
|
||||
* actions if service not running.
|
||||
*/
|
||||
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
|
||||
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_circle)
|
||||
|
||||
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
|
||||
val secondaryColor = MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause,
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_play_arrow,
|
||||
secondaryColor
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
linkButtons(context, appWidgetView)
|
||||
pushUpdate(context, appWidgetIds, appWidgetView)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all active widget instances by pushing changes
|
||||
*/
|
||||
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
|
||||
val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_circle)
|
||||
|
||||
val isPlaying = service.isPlaying
|
||||
val song = service.currentSong
|
||||
|
||||
// Set correct drawable for pause state
|
||||
val playPauseRes =
|
||||
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play_arrow
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause,
|
||||
service.getTintedDrawable(
|
||||
playPauseRes,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
).toBitmap()
|
||||
)
|
||||
val isFavorite = runBlocking(Dispatchers.IO) {
|
||||
return@runBlocking MusicUtil.repository.isSongFavorite(song.id)
|
||||
}
|
||||
val favoriteRes =
|
||||
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_favorite,
|
||||
service.getTintedDrawable(
|
||||
favoriteRes,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
// Link actions buttons to intents
|
||||
linkButtons(service, appWidgetView)
|
||||
|
||||
if (imageSize == 0) {
|
||||
val p = RetroUtil.getScreenSize(service)
|
||||
imageSize = p.x.coerceAtMost(p.y)
|
||||
}
|
||||
|
||||
// Load the album cover async and push the update on completion
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.with(service).clear(target)
|
||||
}
|
||||
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.apply(RequestOptions.circleCropTransform())
|
||||
.into(object : CustomTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?,
|
||||
) {
|
||||
val palette = resource.palette
|
||||
update(
|
||||
resource.bitmap, palette.getVibrantColor(
|
||||
palette.getMutedColor(
|
||||
MaterialValueHelper.getSecondaryTextColor(
|
||||
service, true
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
|
||||
}
|
||||
|
||||
private fun update(bitmap: Bitmap?, color: Int) {
|
||||
// Set correct drawable for pause state
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause,
|
||||
service.getTintedDrawable(
|
||||
playPauseRes, color
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
// Set favorite button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_favorite,
|
||||
service.getTintedDrawable(
|
||||
favoriteRes, color
|
||||
).toBitmap()
|
||||
)
|
||||
if (bitmap != null) {
|
||||
appWidgetView.setImageViewBitmap(R.id.image, bitmap)
|
||||
}
|
||||
|
||||
pushUpdate(service, appWidgetIds, appWidgetView)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link up various button actions using [PendingIntent].
|
||||
*/
|
||||
private fun linkButtons(context: Context, views: RemoteViews) {
|
||||
val action = Intent(context, MainActivity::class.java)
|
||||
.putExtra(
|
||||
MainActivity.EXPAND_PANEL,
|
||||
PreferenceUtil.isExpandPanel
|
||||
)
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
var pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
context, 0, action, if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.image, pendingIntent)
|
||||
// Favorite track
|
||||
pendingIntent = buildPendingIntent(context, TOGGLE_FAVORITE, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_toggle_favorite, pendingIntent)
|
||||
|
||||
// Play and pause
|
||||
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val NAME = "app_widget_circle"
|
||||
|
||||
private var mInstance: AppWidgetCircle? = null
|
||||
private var imageSize = 0
|
||||
|
||||
val instance: AppWidgetCircle
|
||||
@Synchronized get() {
|
||||
if (mInstance == null) {
|
||||
mInstance = AppWidgetCircle()
|
||||
}
|
||||
return mInstance!!
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,24 +21,27 @@ import android.content.Intent
|
|||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.extensions.getTintedDrawable
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_REWIND
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_SKIP
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_TOGGLE_PAUSE
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
|
||||
class AppWidgetClassic : BaseAppWidget() {
|
||||
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
||||
|
@ -54,33 +57,27 @@ class AppWidgetClassic : BaseAppWidget() {
|
|||
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_next,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
)!!, 1f
|
||||
)
|
||||
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_skip_next,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_previous,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
)!!, 1f
|
||||
)
|
||||
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_skip_previous,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_play_arrow_white_32dp,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
)!!, 1f
|
||||
)
|
||||
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_play_arrow_white_32dp,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
linkButtons(context, appWidgetView)
|
||||
|
@ -97,7 +94,7 @@ class AppWidgetClassic : BaseAppWidget() {
|
|||
val song = service.currentSong
|
||||
|
||||
// Set the titles and artwork
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||
if (song.title.isEmpty() && song.artistName.isEmpty()) {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
} else {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
|
||||
|
@ -120,14 +117,16 @@ class AppWidgetClassic : BaseAppWidget() {
|
|||
val appContext = service.applicationContext
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.clear(target)
|
||||
Glide.with(service).clear(target)
|
||||
}
|
||||
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
||||
.checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop()
|
||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
//.checkIgnoreMediaStore()
|
||||
.centerCrop()
|
||||
.into(object : CustomTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
|
||||
transition: Transition<in BitmapPaletteWrapper>?,
|
||||
) {
|
||||
val palette = resource.palette
|
||||
update(
|
||||
|
@ -143,49 +142,42 @@ class AppWidgetClassic : BaseAppWidget() {
|
|||
)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
update(null, Color.WHITE)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {}
|
||||
|
||||
private fun update(bitmap: Bitmap?, color: Int) {
|
||||
// Set correct drawable for pause state
|
||||
val playPauseRes =
|
||||
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play_arrow
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause,
|
||||
ImageUtil.createBitmap(
|
||||
ImageUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
playPauseRes,
|
||||
color
|
||||
)
|
||||
)
|
||||
service.getTintedDrawable(
|
||||
playPauseRes,
|
||||
color
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next,
|
||||
ImageUtil.createBitmap(
|
||||
ImageUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_next,
|
||||
color
|
||||
)
|
||||
)
|
||||
service.getTintedDrawable(
|
||||
R.drawable.ic_skip_next,
|
||||
color
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev,
|
||||
ImageUtil.createBitmap(
|
||||
ImageUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_previous,
|
||||
color
|
||||
)
|
||||
)
|
||||
service.getTintedDrawable(
|
||||
R.drawable.ic_skip_previous,
|
||||
color
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
val image = getAlbumArtDrawable(service.resources, bitmap)
|
||||
val image = getAlbumArtDrawable(service, bitmap)
|
||||
val roundedBitmap =
|
||||
createRoundedBitmap(
|
||||
image,
|
||||
|
@ -213,13 +205,16 @@ class AppWidgetClassic : BaseAppWidget() {
|
|||
MainActivity.EXPAND_PANEL,
|
||||
PreferenceUtil.isExpandPanel
|
||||
)
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
|
||||
var pendingIntent = PendingIntent.getActivity(
|
||||
context, 0, action, if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.image, pendingIntent)
|
||||
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
|
||||
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hemanth Savarla.
|
||||
*
|
||||
* 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.appwidgets
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.extensions.getTintedDrawable
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_REWIND
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_SKIP
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_TOGGLE_PAUSE
|
||||
import code.name.monkey.retromusic.util.DensityUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
|
||||
class AppWidgetMD3 : BaseAppWidget() {
|
||||
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
||||
|
||||
/**
|
||||
* Initialize given widgets to default state, where we launch Music on default click and hide
|
||||
* actions if service not running.
|
||||
*/
|
||||
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
|
||||
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_md3)
|
||||
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
|
||||
val secondaryColor = MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next,
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_skip_next,
|
||||
secondaryColor
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev,
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_skip_previous,
|
||||
secondaryColor
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause,
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_play_arrow_white_32dp,
|
||||
secondaryColor
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
linkButtons(context, appWidgetView)
|
||||
pushUpdate(context, appWidgetIds, appWidgetView)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all active widget instances by pushing changes
|
||||
*/
|
||||
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
|
||||
val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_md3)
|
||||
|
||||
val isPlaying = service.isPlaying
|
||||
val song = service.currentSong
|
||||
|
||||
// Set the titles and artwork
|
||||
if (song.title.isEmpty() && song.artistName.isEmpty()) {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
} else {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
|
||||
appWidgetView.setTextViewText(R.id.title, song.title)
|
||||
appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song))
|
||||
}
|
||||
|
||||
// Set correct drawable for pause state
|
||||
val playPauseRes =
|
||||
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play_arrow_white_32dp
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause,
|
||||
service.getTintedDrawable(
|
||||
playPauseRes,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next,
|
||||
service.getTintedDrawable(
|
||||
R.drawable.ic_skip_next,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev,
|
||||
service.getTintedDrawable(
|
||||
R.drawable.ic_skip_previous,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
// Link actions buttons to intents
|
||||
linkButtons(service, appWidgetView)
|
||||
|
||||
if (imageSize == 0) {
|
||||
imageSize =
|
||||
service.resources.getDimensionPixelSize(R.dimen.app_widget_card_image_size)
|
||||
}
|
||||
if (cardRadius == 0f) {
|
||||
cardRadius =
|
||||
DensityUtil.dip2px(service, 8F).toFloat()
|
||||
}
|
||||
|
||||
// Load the album cover async and push the update on completion
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.with(service).clear(target)
|
||||
}
|
||||
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.centerCrop()
|
||||
.into(object : CustomTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?,
|
||||
) {
|
||||
val palette = resource.palette
|
||||
update(
|
||||
resource.bitmap, palette.getVibrantColor(
|
||||
palette.getMutedColor(
|
||||
MaterialValueHelper.getSecondaryTextColor(
|
||||
service, true
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {}
|
||||
|
||||
private fun update(bitmap: Bitmap?, color: Int) {
|
||||
// Set correct drawable for pause state
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause,
|
||||
service.getTintedDrawable(playPauseRes, color).toBitmap()
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next,
|
||||
service.getTintedDrawable(R.drawable.ic_skip_next, color).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev,
|
||||
service.getTintedDrawable(R.drawable.ic_skip_previous, color).toBitmap()
|
||||
)
|
||||
|
||||
val image = getAlbumArtDrawable(service, bitmap)
|
||||
val roundedBitmap = createRoundedBitmap(
|
||||
image,
|
||||
imageSize,
|
||||
imageSize,
|
||||
cardRadius,
|
||||
cardRadius,
|
||||
cardRadius,
|
||||
cardRadius
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
|
||||
|
||||
pushUpdate(service, appWidgetIds, appWidgetView)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link up various button actions using [PendingIntent].
|
||||
*/
|
||||
private fun linkButtons(context: Context, views: RemoteViews) {
|
||||
val action = Intent(context, MainActivity::class.java)
|
||||
.putExtra(
|
||||
MainActivity.EXPAND_PANEL,
|
||||
PreferenceUtil.isExpandPanel
|
||||
)
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
var pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
context, 0, action, if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.image, pendingIntent)
|
||||
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
|
||||
|
||||
// Previous track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
|
||||
|
||||
// Play and pause
|
||||
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
|
||||
|
||||
// Next track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val NAME = "app_widget_md3"
|
||||
|
||||
private var mInstance: AppWidgetMD3? = null
|
||||
private var imageSize = 0
|
||||
private var cardRadius = 0F
|
||||
|
||||
val instance: AppWidgetMD3
|
||||
@Synchronized get() {
|
||||
if (mInstance == null) {
|
||||
mInstance = AppWidgetMD3()
|
||||
}
|
||||
return mInstance!!
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,23 +20,27 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.extensions.getTintedDrawable
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_REWIND
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_SKIP
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_TOGGLE_PAUSE
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
|
||||
class AppWidgetSmall : BaseAppWidget() {
|
||||
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
||||
|
@ -52,33 +56,26 @@ class AppWidgetSmall : BaseAppWidget() {
|
|||
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_next,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
)!!, 1f
|
||||
)
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_skip_next,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_previous,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
)!!, 1f
|
||||
)
|
||||
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_skip_previous,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_play_arrow_white_32dp,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
)!!, 1f
|
||||
)
|
||||
|
||||
context.getTintedDrawable(
|
||||
R.drawable.ic_play_arrow_white_32dp,
|
||||
MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
linkButtons(context, appWidgetView)
|
||||
|
@ -95,10 +92,10 @@ class AppWidgetSmall : BaseAppWidget() {
|
|||
val song = service.currentSong
|
||||
|
||||
// Set the titles and artwork
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||
if (song.title.isEmpty() && song.artistName.isEmpty()) {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
} else {
|
||||
if (TextUtils.isEmpty(song.title) || TextUtils.isEmpty(song.artistName)) {
|
||||
if (song.title.isEmpty() || song.artistName.isEmpty()) {
|
||||
appWidgetView.setTextViewText(R.id.text_separator, "")
|
||||
} else {
|
||||
appWidgetView.setTextViewText(R.id.text_separator, "•")
|
||||
|
@ -123,14 +120,16 @@ class AppWidgetSmall : BaseAppWidget() {
|
|||
val appContext = service.applicationContext
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.clear(target)
|
||||
Glide.with(service).clear(target)
|
||||
}
|
||||
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
||||
.checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop()
|
||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
|
||||
//.checkIgnoreMediaStore()
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.centerCrop()
|
||||
.into(object : CustomTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
|
||||
transition: Transition<in BitmapPaletteWrapper>?,
|
||||
) {
|
||||
val palette = resource.palette
|
||||
update(
|
||||
|
@ -144,8 +143,12 @@ class AppWidgetSmall : BaseAppWidget() {
|
|||
)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
|
||||
}
|
||||
|
||||
|
@ -154,30 +157,21 @@ class AppWidgetSmall : BaseAppWidget() {
|
|||
val playPauseRes = if (isPlaying) R.drawable.ic_pause
|
||||
else R.drawable.ic_play_arrow_white_32dp
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service, playPauseRes, color
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_toggle_play_pause,
|
||||
service.getTintedDrawable(playPauseRes, color).toBitmap()
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service, R.drawable.ic_skip_next, color
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_next,
|
||||
service.getTintedDrawable(R.drawable.ic_skip_next, color).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service, R.drawable.ic_skip_previous, color
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_prev,
|
||||
service.getTintedDrawable(R.drawable.ic_skip_previous, color).toBitmap()
|
||||
)
|
||||
|
||||
val image = getAlbumArtDrawable(service.resources, bitmap)
|
||||
val image = getAlbumArtDrawable(service, bitmap)
|
||||
val roundedBitmap = createRoundedBitmap(
|
||||
image, imageSize, imageSize, cardRadius, 0f, 0f, 0f
|
||||
)
|
||||
|
@ -198,13 +192,17 @@ class AppWidgetSmall : BaseAppWidget() {
|
|||
MainActivity.EXPAND_PANEL,
|
||||
PreferenceUtil.isExpandPanel
|
||||
)
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
|
||||
var pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
context, 0, action, if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.image, pendingIntent)
|
||||
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
|
||||
|
||||
|
|
|
@ -18,49 +18,44 @@ import android.app.PendingIntent
|
|||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.content.ContextCompat
|
||||
import code.name.monkey.retromusic.App
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.extensions.getTintedDrawable
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_REWIND
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_SKIP
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_TOGGLE_PAUSE
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
|
||||
class AppWidgetText : BaseAppWidget() {
|
||||
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
|
||||
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_text)
|
||||
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context, R.drawable.ic_skip_next, ContextCompat.getColor(
|
||||
context, R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_next,
|
||||
context.getTintedDrawable(R.drawable.ic_skip_next, ContextCompat.getColor(
|
||||
context, R.color.md_white_1000
|
||||
)).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context, R.drawable.ic_skip_previous, ContextCompat.getColor(
|
||||
context, R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
R.id.button_prev,
|
||||
context.getTintedDrawable(R.drawable.ic_skip_previous, ContextCompat.getColor(
|
||||
context, R.color.md_white_1000
|
||||
)
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context, R.drawable.ic_play_arrow_white_32dp, ContextCompat.getColor(
|
||||
context, R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
R.id.button_toggle_play_pause,
|
||||
context.getTintedDrawable(R.drawable.ic_play_arrow_white_32dp, ContextCompat.getColor(
|
||||
context, R.color.md_white_1000
|
||||
)
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
appWidgetView.setTextColor(
|
||||
|
@ -83,13 +78,16 @@ class AppWidgetText : BaseAppWidget() {
|
|||
MainActivity.EXPAND_PANEL,
|
||||
PreferenceUtil.isExpandPanel
|
||||
)
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
|
||||
var pendingIntent = PendingIntent.getActivity(
|
||||
context, 0, action, if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.image, pendingIntent)
|
||||
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
|
||||
|
||||
|
@ -113,7 +111,7 @@ class AppWidgetText : BaseAppWidget() {
|
|||
val song = service.currentSong
|
||||
|
||||
// Set the titles and artwork
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||
if (song.title.isEmpty() && song.artistName.isEmpty()) {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
} else {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
|
||||
|
@ -127,35 +125,29 @@ class AppWidgetText : BaseAppWidget() {
|
|||
val playPauseRes = if (isPlaying) R.drawable.ic_pause
|
||||
else R.drawable.ic_play_arrow_white_32dp
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
App.getContext(), playPauseRes, ContextCompat.getColor(
|
||||
App.getContext(), R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_toggle_play_pause,
|
||||
service.getTintedDrawable(playPauseRes, ContextCompat.getColor(
|
||||
service, R.color.md_white_1000)
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
App.getContext(),
|
||||
R.drawable.ic_skip_next,
|
||||
ContextCompat.getColor(
|
||||
App.getContext(), R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_next,
|
||||
service.getTintedDrawable(
|
||||
R.drawable.ic_skip_next,
|
||||
ContextCompat.getColor(
|
||||
service,
|
||||
R.color.md_white_1000
|
||||
)
|
||||
).toBitmap()
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
App.getContext(),
|
||||
R.drawable.ic_skip_previous,
|
||||
ContextCompat.getColor(
|
||||
App.getContext(), R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
)
|
||||
R.id.button_prev,
|
||||
service.getTintedDrawable(
|
||||
R.drawable.ic_skip_previous,
|
||||
ContextCompat.getColor(
|
||||
service, R.color.md_white_1000
|
||||
)
|
||||
).toBitmap()
|
||||
)
|
||||
|
||||
pushUpdate(service.applicationContext, appWidgetIds, appWidgetView)
|
||||
|
|
|
@ -20,19 +20,20 @@ import android.appwidget.AppWidgetProvider
|
|||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.content.ContextCompat
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.APP_WIDGET_UPDATE
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.EXTRA_APP_WIDGET_NAME
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.FAVORITE_STATE_CHANGED
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.META_CHANGED
|
||||
import code.name.monkey.retromusic.service.MusicService.Companion.PLAY_STATE_CHANGED
|
||||
|
||||
abstract class BaseAppWidget : AppWidgetProvider() {
|
||||
|
||||
|
@ -57,7 +58,7 @@ abstract class BaseAppWidget : AppWidgetProvider() {
|
|||
*/
|
||||
fun notifyChange(service: MusicService, what: String) {
|
||||
if (hasInstances(service)) {
|
||||
if (META_CHANGED == what || PLAY_STATE_CHANGED == what) {
|
||||
if (META_CHANGED == what || PLAY_STATE_CHANGED == what || FAVORITE_STATE_CHANGED == what) {
|
||||
performUpdate(service, null)
|
||||
}
|
||||
}
|
||||
|
@ -96,10 +97,14 @@ abstract class BaseAppWidget : AppWidgetProvider() {
|
|||
): PendingIntent {
|
||||
val intent = Intent(action)
|
||||
intent.component = serviceName
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
PendingIntent.getForegroundService(context, 0, intent, 0)
|
||||
return if (VersionUtils.hasOreo()) {
|
||||
PendingIntent.getForegroundService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||
} else {
|
||||
PendingIntent.getService(context, 0, intent, 0)
|
||||
PendingIntent.getService(
|
||||
context, 0, intent, if (VersionUtils.hasMarshmallow())
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,18 +112,18 @@ abstract class BaseAppWidget : AppWidgetProvider() {
|
|||
|
||||
abstract fun performUpdate(service: MusicService, appWidgetIds: IntArray?)
|
||||
|
||||
protected fun getAlbumArtDrawable(resources: Resources, bitmap: Bitmap?): Drawable {
|
||||
protected fun getAlbumArtDrawable(context: Context, bitmap: Bitmap?): Drawable {
|
||||
return if (bitmap == null) {
|
||||
ContextCompat.getDrawable(App.getContext(), R.drawable.default_audio_art)!!
|
||||
ContextCompat.getDrawable(context, R.drawable.default_audio_art)!!
|
||||
} else {
|
||||
BitmapDrawable(resources, bitmap)
|
||||
BitmapDrawable(context.resources, bitmap)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun getSongArtistAndAlbum(song: Song): String {
|
||||
val builder = StringBuilder()
|
||||
builder.append(song.artistName)
|
||||
if (!TextUtils.isEmpty(song.artistName) && !TextUtils.isEmpty(song.albumName)) {
|
||||
if (song.artistName.isNotEmpty() && song.albumName.isNotEmpty()) {
|
||||
builder.append(" • ")
|
||||
}
|
||||
builder.append(song.albumName)
|
||||
|
@ -162,18 +167,6 @@ abstract class BaseAppWidget : AppWidgetProvider() {
|
|||
return rounded
|
||||
}
|
||||
|
||||
fun createBitmap(drawable: Drawable, sizeMultiplier: Float): Bitmap {
|
||||
val bitmap = Bitmap.createBitmap(
|
||||
(drawable.intrinsicWidth * sizeMultiplier).toInt(),
|
||||
(drawable.intrinsicHeight * sizeMultiplier).toInt(),
|
||||
Bitmap.Config.ARGB_8888
|
||||
)
|
||||
val c = Canvas(bitmap)
|
||||
drawable.setBounds(0, 0, c.width, c.height)
|
||||
drawable.draw(c)
|
||||
return bitmap
|
||||
}
|
||||
|
||||
protected fun composeRoundedRectPath(
|
||||
rect: RectF,
|
||||
tl: Float,
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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.auto;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Created by Beesham Sarendranauth (Beesham)
|
||||
*/
|
||||
public class AutoMediaIDHelper {
|
||||
|
||||
// Media IDs used on browseable items of MediaBrowser
|
||||
public static final String MEDIA_ID_EMPTY_ROOT = "__EMPTY_ROOT__";
|
||||
public static final String MEDIA_ID_ROOT = "__ROOT__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_SEARCH = "__BY_SEARCH__"; // TODO
|
||||
public static final String MEDIA_ID_MUSICS_BY_HISTORY = "__BY_HISTORY__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_TOP_TRACKS = "__BY_TOP_TRACKS__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_SUGGESTIONS = "__BY_SUGGESTIONS__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_PLAYLIST = "__BY_PLAYLIST__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_ALBUM = "__BY_ALBUM__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_ARTIST = "__BY_ARTIST__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_ALBUM_ARTIST = "__BY_ALBUM_ARTIST__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_GENRE = "__BY_GENRE__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_SHUFFLE = "__BY_SHUFFLE__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_QUEUE = "__BY_QUEUE__";
|
||||
public static final String RECENT_ROOT = "__RECENT__";
|
||||
|
||||
private static final String CATEGORY_SEPARATOR = "__/__";
|
||||
private static final String LEAF_SEPARATOR = "__|__";
|
||||
|
||||
/**
|
||||
* Create a String value that represents a playable or a browsable media.
|
||||
* <p/>
|
||||
* Encode the media browseable categories, if any, and the unique music ID, if any,
|
||||
* into a single String mediaID.
|
||||
* <p/>
|
||||
* MediaIDs are of the form <categoryType>__/__<categoryValue>__|__<musicUniqueId>, to make it
|
||||
* easy to find the category (like genre) that a music was selected from, so we
|
||||
* can correctly build the playing queue. This is specially useful when
|
||||
* one music can appear in more than one list, like "by genre -> genre_1"
|
||||
* and "by artist -> artist_1".
|
||||
*
|
||||
* @param mediaID Unique ID for playable items, or null for browseable items.
|
||||
* @param categories Hierarchy of categories representing this item's browsing parents.
|
||||
* @return A hierarchy-aware media ID.
|
||||
*/
|
||||
public static String createMediaID(String mediaID, String... categories) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (categories != null) {
|
||||
for (int i = 0; i < categories.length; i++) {
|
||||
if (!isValidCategory(categories[i])) {
|
||||
throw new IllegalArgumentException("Invalid category: " + categories[i]);
|
||||
}
|
||||
sb.append(categories[i]);
|
||||
if (i < categories.length - 1) {
|
||||
sb.append(CATEGORY_SEPARATOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mediaID != null) {
|
||||
sb.append(LEAF_SEPARATOR).append(mediaID);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String extractCategory(@NonNull String mediaID) {
|
||||
int pos = mediaID.indexOf(LEAF_SEPARATOR);
|
||||
if (pos >= 0) {
|
||||
return mediaID.substring(0, pos);
|
||||
}
|
||||
return mediaID;
|
||||
}
|
||||
|
||||
public static String extractMusicID(@NonNull String mediaID) {
|
||||
int pos = mediaID.indexOf(LEAF_SEPARATOR);
|
||||
if (pos >= 0) {
|
||||
return mediaID.substring(pos + LEAF_SEPARATOR.length());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isBrowseable(@NonNull String mediaID) {
|
||||
return !mediaID.contains(LEAF_SEPARATOR);
|
||||
}
|
||||
|
||||
private static boolean isValidCategory(String category) {
|
||||
return category == null ||
|
||||
(!category.contains(CATEGORY_SEPARATOR) && !category.contains(LEAF_SEPARATOR));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* 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.auto
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.support.v4.media.MediaBrowserCompat
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.CategoryInfo
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.repository.*
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
|
||||
/**
|
||||
* Created by Beesham Sarendranauth (Beesham)
|
||||
*/
|
||||
class AutoMusicProvider(
|
||||
val mContext: Context,
|
||||
private val songsRepository: SongRepository,
|
||||
private val albumsRepository: AlbumRepository,
|
||||
private val artistsRepository: ArtistRepository,
|
||||
private val genresRepository: GenreRepository,
|
||||
private val playlistsRepository: PlaylistRepository,
|
||||
private val topPlayedRepository: TopPlayedRepository
|
||||
) {
|
||||
private var mMusicService: WeakReference<MusicService>? = null
|
||||
|
||||
fun setMusicService(service: MusicService) {
|
||||
mMusicService = WeakReference(service)
|
||||
}
|
||||
|
||||
fun getChildren(mediaId: String?, resources: Resources): List<MediaBrowserCompat.MediaItem> {
|
||||
val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = ArrayList()
|
||||
when (mediaId) {
|
||||
AutoMediaIDHelper.MEDIA_ID_ROOT -> {
|
||||
mediaItems.addAll(getRootChildren(resources))
|
||||
}
|
||||
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST -> for (playlist in playlistsRepository.playlists()) {
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST, playlist.id)
|
||||
.icon(R.drawable.ic_playlist_play)
|
||||
.title(playlist.name)
|
||||
.subTitle(playlist.getInfoString(mContext))
|
||||
.asPlayable()
|
||||
.build()
|
||||
)
|
||||
}
|
||||
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM -> for (album in albumsRepository.albums()) {
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.path(mediaId, album.id)
|
||||
.title(album.title)
|
||||
.subTitle(album.albumArtist ?: album.artistName)
|
||||
.icon(MusicUtil.getMediaStoreAlbumCoverUri(album.id))
|
||||
.asPlayable()
|
||||
.build()
|
||||
)
|
||||
}
|
||||
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST -> for (artist in artistsRepository.artists()) {
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asPlayable()
|
||||
.path(mediaId, artist.id)
|
||||
.title(artist.name)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM_ARTIST -> for (artist in artistsRepository.albumArtists()) {
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asPlayable()
|
||||
// we just pass album id here as we don't have album artist id's
|
||||
.path(mediaId, artist.safeGetFirstAlbum().id)
|
||||
.title(artist.name)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE -> for (genre in genresRepository.genres()) {
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asPlayable()
|
||||
.path(mediaId, genre.id)
|
||||
.title(genre.name)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE ->
|
||||
mMusicService?.get()?.playingQueue
|
||||
?.let {
|
||||
for (song in it) {
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asPlayable()
|
||||
.path(mediaId, song.id)
|
||||
.title(song.title)
|
||||
.subTitle(song.artistName)
|
||||
.icon(MusicUtil.getMediaStoreAlbumCoverUri(song.albumId))
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
getPlaylistChildren(mediaId, mediaItems)
|
||||
}
|
||||
}
|
||||
return mediaItems
|
||||
}
|
||||
|
||||
private fun getPlaylistChildren(
|
||||
mediaId: String?,
|
||||
mediaItems: MutableList<MediaBrowserCompat.MediaItem>
|
||||
) {
|
||||
val songs = when (mediaId) {
|
||||
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS -> {
|
||||
topPlayedRepository.topTracks()
|
||||
}
|
||||
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY -> {
|
||||
topPlayedRepository.recentlyPlayedTracks()
|
||||
}
|
||||
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SUGGESTIONS -> {
|
||||
topPlayedRepository.notRecentlyPlayedTracks().take(8)
|
||||
}
|
||||
else -> {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
songs.forEach { song ->
|
||||
mediaItems.add(
|
||||
getPlayableSong(mediaId, song)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRootChildren(resources: Resources): List<MediaBrowserCompat.MediaItem> {
|
||||
val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = ArrayList()
|
||||
val libraryCategories = PreferenceUtil.libraryCategory
|
||||
libraryCategories.forEach {
|
||||
if (it.visible) {
|
||||
when (it.category) {
|
||||
CategoryInfo.Category.Albums -> {
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asBrowsable()
|
||||
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM)
|
||||
.gridLayout(true)
|
||||
.icon(R.drawable.ic_album)
|
||||
.title(resources.getString(R.string.albums)).build()
|
||||
)
|
||||
}
|
||||
CategoryInfo.Category.Artists -> {
|
||||
if (PreferenceUtil.albumArtistsOnly) {
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asBrowsable()
|
||||
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM_ARTIST)
|
||||
.icon(R.drawable.ic_album_artist)
|
||||
.title(resources.getString(R.string.album_artist)).build()
|
||||
)
|
||||
} else {
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asBrowsable()
|
||||
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST)
|
||||
.icon(R.drawable.ic_artist)
|
||||
.title(resources.getString(R.string.artists)).build()
|
||||
)
|
||||
}
|
||||
}
|
||||
CategoryInfo.Category.Genres -> {
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asBrowsable()
|
||||
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE)
|
||||
.icon(R.drawable.ic_guitar)
|
||||
.title(resources.getString(R.string.genres)).build()
|
||||
)
|
||||
}
|
||||
CategoryInfo.Category.Playlists -> {
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asBrowsable()
|
||||
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST)
|
||||
.icon(R.drawable.ic_playlist_play)
|
||||
.title(resources.getString(R.string.playlists)).build()
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asPlayable()
|
||||
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SHUFFLE)
|
||||
.icon(R.drawable.ic_shuffle)
|
||||
.title(resources.getString(R.string.action_shuffle_all))
|
||||
.subTitle(MusicUtil.getPlaylistInfoString(mContext, songsRepository.songs()))
|
||||
.build()
|
||||
)
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asBrowsable()
|
||||
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE)
|
||||
.icon(R.drawable.ic_queue_music)
|
||||
.title(resources.getString(R.string.queue))
|
||||
.subTitle(MusicUtil.getPlaylistInfoString(mContext, MusicPlayerRemote.playingQueue))
|
||||
.asBrowsable().build()
|
||||
)
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asBrowsable()
|
||||
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS)
|
||||
.icon(R.drawable.ic_trending_up)
|
||||
.title(resources.getString(R.string.my_top_tracks))
|
||||
.subTitle(
|
||||
MusicUtil.getPlaylistInfoString(
|
||||
mContext,
|
||||
topPlayedRepository.topTracks()
|
||||
)
|
||||
)
|
||||
.asBrowsable().build()
|
||||
)
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asBrowsable()
|
||||
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SUGGESTIONS)
|
||||
.icon(R.drawable.ic_face)
|
||||
.title(resources.getString(R.string.suggestion_songs))
|
||||
.subTitle(
|
||||
MusicUtil.getPlaylistInfoString(
|
||||
mContext,
|
||||
topPlayedRepository.notRecentlyPlayedTracks().takeIf {
|
||||
it.size > 9
|
||||
} ?: emptyList()
|
||||
)
|
||||
)
|
||||
.asBrowsable().build()
|
||||
)
|
||||
mediaItems.add(
|
||||
AutoMediaItem.with(mContext)
|
||||
.asBrowsable()
|
||||
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY)
|
||||
.icon(R.drawable.ic_history)
|
||||
.title(resources.getString(R.string.history))
|
||||
.subTitle(
|
||||
MusicUtil.getPlaylistInfoString(
|
||||
mContext,
|
||||
topPlayedRepository.recentlyPlayedTracks()
|
||||
)
|
||||
)
|
||||
.asBrowsable().build()
|
||||
)
|
||||
return mediaItems
|
||||
}
|
||||
|
||||
private fun getPlayableSong(mediaId: String?, song: Song): MediaBrowserCompat.MediaItem {
|
||||
return AutoMediaItem.with(mContext)
|
||||
.asPlayable()
|
||||
.path(mediaId, song.id)
|
||||
.title(song.title)
|
||||
.subTitle(song.artistName)
|
||||
.icon(MusicUtil.getMediaStoreAlbumCoverUri(song.albumId))
|
||||
.build()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package code.name.monkey.retromusic.auto
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.support.v4.media.MediaBrowserCompat
|
||||
import android.support.v4.media.MediaDescriptionCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.os.bundleOf
|
||||
|
||||
|
||||
internal object AutoMediaItem {
|
||||
fun with(context: Context): Builder {
|
||||
return Builder(context)
|
||||
}
|
||||
|
||||
internal class Builder(private val mContext: Context) {
|
||||
private var mBuilder: MediaDescriptionCompat.Builder?
|
||||
private var mFlags = 0
|
||||
fun path(fullPath: String): Builder {
|
||||
mBuilder?.setMediaId(fullPath)
|
||||
return this
|
||||
}
|
||||
|
||||
fun path(path: String?, id: Long): Builder {
|
||||
return path(AutoMediaIDHelper.createMediaID(id.toString(), path))
|
||||
}
|
||||
|
||||
fun title(title: String): Builder {
|
||||
mBuilder?.setTitle(title)
|
||||
return this
|
||||
}
|
||||
|
||||
fun subTitle(subTitle: String): Builder {
|
||||
mBuilder?.setSubtitle(subTitle)
|
||||
return this
|
||||
}
|
||||
|
||||
fun icon(uri: Uri?): Builder {
|
||||
mBuilder?.setIconUri(uri)
|
||||
return this
|
||||
}
|
||||
|
||||
fun icon(iconDrawableId: Int): Builder {
|
||||
mBuilder?.setIconBitmap(
|
||||
ResourcesCompat.getDrawable(
|
||||
mContext.resources,
|
||||
iconDrawableId,
|
||||
mContext.theme
|
||||
)?.toBitmap()
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun gridLayout(isGrid: Boolean): Builder {
|
||||
|
||||
val hints = bundleOf(
|
||||
CONTENT_STYLE_SUPPORTED to true,
|
||||
CONTENT_STYLE_BROWSABLE_HINT to
|
||||
if (isGrid) CONTENT_STYLE_GRID_ITEM_HINT_VALUE
|
||||
else CONTENT_STYLE_LIST_ITEM_HINT_VALUE,
|
||||
CONTENT_STYLE_PLAYABLE_HINT to
|
||||
if (isGrid) CONTENT_STYLE_GRID_ITEM_HINT_VALUE
|
||||
else CONTENT_STYLE_LIST_ITEM_HINT_VALUE
|
||||
)
|
||||
mBuilder?.setExtras(hints)
|
||||
return this
|
||||
}
|
||||
|
||||
fun asBrowsable(): Builder {
|
||||
mFlags = mFlags or MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
|
||||
return this
|
||||
}
|
||||
|
||||
fun asPlayable(): Builder {
|
||||
mFlags = mFlags or MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
|
||||
return this
|
||||
}
|
||||
|
||||
fun build(): MediaBrowserCompat.MediaItem {
|
||||
val result = MediaBrowserCompat.MediaItem(mBuilder!!.build(), mFlags)
|
||||
mBuilder = null
|
||||
mFlags = 0
|
||||
return result
|
||||
}
|
||||
|
||||
init {
|
||||
mBuilder = MediaDescriptionCompat.Builder()
|
||||
}
|
||||
companion object{
|
||||
// Hints - see https://developer.android.com/training/cars/media#default-content-style
|
||||
const val CONTENT_STYLE_SUPPORTED = "android.media.browse.CONTENT_STYLE_SUPPORTED"
|
||||
const val CONTENT_STYLE_BROWSABLE_HINT = "android.media.browse.CONTENT_STYLE_BROWSABLE_HINT"
|
||||
const val CONTENT_STYLE_PLAYABLE_HINT = "android.media.browse.CONTENT_STYLE_PLAYABLE_HINT"
|
||||
const val CONTENT_STYLE_LIST_ITEM_HINT_VALUE = 1
|
||||
const val CONTENT_STYLE_GRID_ITEM_HINT_VALUE = 2
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue