(...);
-}
--keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
- **[] $VALUES;
- public *;
-}
--keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
- *** rewind();
+-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
+ **[] $VALUES;
+ public *;
}
-# OkHttp
--keepattributes Signature
--keepattributes *Annotation*
--keep interface com.squareup.okhttp3.** { *; }
--dontwarn com.squareup.okhttp3.**
+-keep class !android.support.v7.internal.view.menu.**,** {*;}
-#-dontwarn
-#-ignorewarnings
+-dontwarn
+-ignorewarnings
-#Jaudiotagger
--dontwarn org.jaudiotagger.**
--dontwarn org.jcodec.**
--keep class org.jaudiotagger.** { *; }
--keep class org.jcodec.** { *; }
+-keep public class android.support.design.widget.BottomNavigationView { *; }
+-keep public class android.support.design.internal.BottomNavigationMenuView { *; }
+-keep public class android.support.design.internal.BottomNavigationPresenter { *; }
+-keep public class android.support.design.internal.BottomNavigationItemView { *; }
--keepclassmembers enum * { *; }
--keepattributes *Annotation*, Signature, Exception
--keepnames class androidx.navigation.fragment.NavHostFragment
--keep class * extends androidx.fragment.app.Fragment{}
--keepnames class * extends android.os.Parcelable
--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.** { *; }
\ No newline at end of file
+#-dontwarn android.support.v8.renderscript.*
+#-keepclassmembers class android.support.v8.renderscript.RenderScript {
+# native *** rsn*(...);
+# native *** n*(...);
+#}
+
+#-keep class org.jaudiotagger.** { *; }
+
+#For cast
+-keep class code.name.monkey.retromusic.cast.CastOptionsProvider { *; }
+-keep class android.support.** { *; }
+-keep class com.google.** { *; }
+-keep class java.nio.file.** { *; }
+
+-obfuscationdictionary build/obfuscation-dictionary.txt
+-classobfuscationdictionary build/class-dictionary.txt
+-packageobfuscationdictionary build/package-dictionary.txt
diff --git a/app/src/debug/res/font/bold.ttf b/app/src/debug/res/font/bold.ttf
deleted file mode 100644
index 96619df92..000000000
Binary files a/app/src/debug/res/font/bold.ttf and /dev/null differ
diff --git a/app/src/debug/res/font/google_sans_bold.ttf b/app/src/debug/res/font/google_sans_bold.ttf
deleted file mode 100644
index 80497666e..000000000
Binary files a/app/src/debug/res/font/google_sans_bold.ttf and /dev/null differ
diff --git a/app/src/debug/res/font/google_sans_medium.ttf b/app/src/debug/res/font/google_sans_medium.ttf
deleted file mode 100644
index 1543660da..000000000
Binary files a/app/src/debug/res/font/google_sans_medium.ttf and /dev/null differ
diff --git a/app/src/debug/res/font/google_sans_regular.ttf b/app/src/debug/res/font/google_sans_regular.ttf
deleted file mode 100644
index ab605f9e2..000000000
Binary files a/app/src/debug/res/font/google_sans_regular.ttf and /dev/null differ
diff --git a/app/src/debug/res/font/medium.ttf b/app/src/debug/res/font/medium.ttf
deleted file mode 100644
index fd818d6f5..000000000
Binary files a/app/src/debug/res/font/medium.ttf and /dev/null differ
diff --git a/app/src/debug/res/font/regular.ttf b/app/src/debug/res/font/regular.ttf
deleted file mode 100644
index e2c69c3fb..000000000
Binary files a/app/src/debug/res/font/regular.ttf and /dev/null differ
diff --git a/app/src/debug/res/font/sans.xml b/app/src/debug/res/font/sans.xml
deleted file mode 100644
index 7bbc8513b..000000000
--- a/app/src/debug/res/font/sans.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/debug/res/values/bools.xml b/app/src/debug/res/values/bools.xml
deleted file mode 100644
index 7d3f0ee62..000000000
--- a/app/src/debug/res/values/bools.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- true
- false
-
\ No newline at end of file
diff --git a/app/src/debug/res/values/donottranslate.xml b/app/src/debug/res/values/donottranslate.xml
deleted file mode 100644
index 79e559838..000000000
--- a/app/src/debug/res/values/donottranslate.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- Metro Debug
-
\ No newline at end of file
diff --git a/app/src/debug/res/values/styles.xml b/app/src/debug/res/values/styles.xml
deleted file mode 100644
index f3761b219..000000000
--- a/app/src/debug/res/values/styles.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 14fbaaab7..1ff053167 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,57 +1,40 @@
+ package="code.name.monkey.retromusic">
-
-
-
-
+
-
-
-
-
+
+
+
+
-
-
+
+
+ android:usesCleartextTraffic="false"
+ tools:ignore="AllowBackup,GoogleAppIndexingWarning"
+ tools:targetApi="m">
+ android:label="@string/app_name"
+ android:theme="@style/SplashTheme">
@@ -62,6 +45,7 @@
+
@@ -121,54 +105,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
+
-
+
@@ -216,7 +177,7 @@
@@ -229,7 +190,7 @@
@@ -241,7 +202,7 @@
@@ -253,7 +214,7 @@
@@ -265,7 +226,7 @@
@@ -275,36 +236,13 @@
android:name="android.appwidget.provider"
android:resource="@xml/app_widget_card_info" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:label="@string/app_name"
+ tools:ignore="ExportedService">
@@ -318,33 +256,15 @@
android:name="com.lge.support.SPLIT_WINDOW"
android:value="true" />
-
-
-
+ android:name="code.name.monkey.retromusic.glide.RetroMusicGlideModule"
+ android:value="GlideModule" />
-
-
-
-
+
+
-
-
-
-
-
diff --git a/app/src/main/assets/contributors.json b/app/src/main/assets/contributors.json
new file mode 100644
index 000000000..639b75535
--- /dev/null
+++ b/app/src/main/assets/contributors.json
@@ -0,0 +1,26 @@
+[
+ {
+ "name": "Hemanth Savarala",
+ "summary": "Lead Developer & Designer",
+ "link": "https://github.com/h4h13",
+ "profile_image": "https://i.imgur.com/AoVs9oj.jpg"
+ },
+ {
+ "name": "Lennart Glamann",
+ "summary": "Designer",
+ "link": "https://t.me/FlixbusLennart",
+ "profile_image": "https://i.imgur.com/Q5Nsx1R.jpg"
+ },
+ {
+ "name": "GameSpeck",
+ "summary": "Telegram group maintainer",
+ "link": "https://t.me/GameSpeck",
+ "profile_image": "https://imgur.com/B15Kb9a.jpg"
+ },
+ {
+ "name": "Milind Goel",
+ "summary": "Github & Telegram maintainer",
+ "link": "https://t.me/MilindGoel15",
+ "profile_image": "https://i.imgur.com/Bz4De21_d.jpg"
+ }
+]
\ No newline at end of file
diff --git a/app/src/main/assets/images/daksh.png b/app/src/main/assets/images/daksh.png
deleted file mode 100644
index 737f9066c..000000000
Binary files a/app/src/main/assets/images/daksh.png and /dev/null differ
diff --git a/app/src/main/assets/images/haythem.jpg b/app/src/main/assets/images/haythem.jpg
deleted file mode 100644
index c74527f1d..000000000
Binary files a/app/src/main/assets/images/haythem.jpg and /dev/null differ
diff --git a/app/src/main/assets/images/hemanth.jpg b/app/src/main/assets/images/hemanth.jpg
deleted file mode 100644
index b8f5e7805..000000000
Binary files a/app/src/main/assets/images/hemanth.jpg and /dev/null differ
diff --git a/app/src/main/assets/images/lenny.jpg b/app/src/main/assets/images/lenny.jpg
deleted file mode 100644
index 7f8025f08..000000000
Binary files a/app/src/main/assets/images/lenny.jpg and /dev/null differ
diff --git a/app/src/main/assets/images/milind.png b/app/src/main/assets/images/milind.png
deleted file mode 100644
index bbf9b8278..000000000
Binary files a/app/src/main/assets/images/milind.png and /dev/null differ
diff --git a/app/src/main/assets/images/pratham.jpg b/app/src/main/assets/images/pratham.jpg
deleted file mode 100644
index 7f05ad9d5..000000000
Binary files a/app/src/main/assets/images/pratham.jpg and /dev/null differ
diff --git a/app/src/main/assets/license.html b/app/src/main/assets/license.html
deleted file mode 100644
index 2fd8ba5ed..000000000
--- a/app/src/main/assets/license.html
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
-
-
-
-
-
- Phonograph by
- Karim Abou Zeid
-AOSP Support Libraries by AOSP contributors
- Glide by Sam Judd
- Retrofit by Square team
-
- OkHttp by Square team
-Koin by Arnaud Giuliani
- Material Dialogs and Cab
- by Aidan Michael Follestad
-
- Material Contextual Action Bar by Aidan Michael Follestad
- Advanced RecyclerView by Haruki Hasegawa
-Custom Activity on Crash by Eduard Ereza Martínez
-
-NanoHttpd by NanoHttpd Team
-Circular Seekbar by Tankery
-jAudioTagger by Kanedias
-Android Fast Scroll by Zhang Hai
-Image Picker by Dhaval Patel
-Material Intro by Jan Heinrich Reimer
-Slidr by Drew Heavner
-FadingEdgeLayout by bosphere
-KeyboardVisibilityEvent by Yasuhiro SHIMIZU
-android-snowfall by Jetradar Mobile
-Insetter by Chris Banes
- Icons by Austin Andrews
- Material Design City Wallpaper
-
-
-
diff --git a/app/src/main/assets/oldindex.html b/app/src/main/assets/oldindex.html
new file mode 100644
index 000000000..037d52fad
--- /dev/null
+++ b/app/src/main/assets/oldindex.html
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+ Phonograph by
+ Karim Abou Zeid
+
+ VinylMusicPlayer by Adrien Poupa
+ RxJava by RxJava
+ authors
+ Material Dialogs and Cab
+ by Aidan Michael Follestad
+
+ Android Sliding Up Panel by The Umano Team
+ AOSP Support Libraries by AOSP contributors
+ Glide by Sam Judd
+ Retrofit by Square team
+
+
+ Material Contextual Action Bar by Aidan Michael Follestad
+ OkHttp by Square team
+
+ CircleImageView by Henning Dodenhof
+
+ MaterialProgressBar by Zhang Hai
+ Android In-App Billing v3 Library by
+ Henning Dodenhof
+ Advanced RecyclerView by Haruki Hasegawa
+ Android-ObservableScrollView by Soichiro
+ Kashima
+ Font
+ used(CIRCULAR STD BOOK FONT) by NIELSON CAETANO
+ Icons by Austin Andrews
+ Material Design City Wallpaper
+
+
+
+
diff --git a/app/src/main/assets/retro-changelog.html b/app/src/main/assets/retro-changelog.html
index b6b1c411a..4c86d4d49 100644
--- a/app/src/main/assets/retro-changelog.html
+++ b/app/src/main/assets/retro-changelog.html
@@ -6,23 +6,22 @@
word-wrap: break-word;
}
- div {
- margin: 20px 10px;
- padding: 10px;
- border-radius: 10px;
- box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
+ body {
+ padding-left: 1rem;
+ padding-right: 1rem;
}
h2 {
margin-block-end: 0rem;
margin-block-start: 0rem;
- display: inline-block;
- vertical-align: center;
}
li {
font-size: 0.85rem;
- padding: 0.5rem 0;
+ padding-top: 0.5rem;
+ padding-left: 0;
+ padding-right: 0;
+ color: rgba(0, 0, 0, 0.8);
}
ul {
@@ -37,6 +36,7 @@
span {
font-size: 0.7rem;
+ line-height: 0.7rem;
}
h5 {
@@ -44,423 +44,36 @@
margin-block-end: 0.5rem;
}
- h3 {
- margin: 10px 0px;
+ h3 span {
+ border-radius: 0.2rem;
+ padding-left: 0.5rem;
+ padding-right: 0.5rem;
+ padding-top: 0.3rem;
+ padding-bottom: 0.3rem;
font-size: 1rem;
}
-
- .tag {
- border-radius: 5px;
- margin-left: 10px;
- padding: 5px;
- display: inline-block;
- vertical-align: top;
- }
-
{style-placeholder}
-
-
-
March 30, 2023
-
v6.1.0
-
What's New
-
- App now targets Android 13, support for Granular media permissions, Photo picker, Per-app language preferences & Predictive back gesture
-
-
Fixed
-
- Fixed playlist reordering crash
- Other minor bugs fixes and improvements
-
-
-
-
March 13, 2023
-
v6.0.4
-
What's New
-
- Minor redesign in Playlist details screen
- Updated translations
-
-
Fixed
-
- Fixed file popup menu actions in Folders tab
- Fixed playlist image loading
- Fixed blurry album art in Android 13
- Minor bug fixes and improvements
-
-
-
-
July 10, 2022
-
v6.0.3Beta
-
Fixed
-
- Migrated icons to Material symbols
-
-
-
-
June 21, 2022
-
v6.0.2Beta
-
Fixed
-
- Minor bug fixes and improvements
-
-
-
-
June 13, 2022
-
v6.0.1Beta
-
Fixed
-
- Fixed ChromeCast crash
- Fixed Slider crashes
- Fixed storage related crashes on Android 10
- Fixed CrossFade not working Fade Audio is not working
-
-
-
-
June 7, 2022
-
v6.0.0Beta
-
What's New
-
- Better Cast
- Mini player in settings screen
- Changed Seekbar with Sliders
- Added NavigationRailView for Landscape
- Show remaining time in Sleep timer dialog
-
-
Fixed
-
- Fixed Top/Recent Artists/Albums not updating (Wrong sort order)
- Fixed all Blacklist related crashes
- Fix restart button not working in crash activity
-
-
-
-
May 25, 2022
-
v5.8.5
-
What's New
-
- Display song images along in the artist and album details pages
- Removed the Internet permissions and all the associated features
-
-
Fixed
-
- Fixed crashing occurs during changing screen orientation
-
-
-
-
May 13, 2022
-
v5.8.4
-
What's New
-
- Added a toggle to enable/disable swipe down to dismiss mini player
-
-
Fixed
-
- Fixed Playback speed and pitch not working when CrossFade is enabled
- Fixed crash when adding folders to blacklist
- Fix bugs in MD3 theme
-
-
-
-
May 07, 2022
-
v5.8.3
-
What's New
-
- Added a new MD3 now playing theme
- Swipe down to dismiss Mini player
- Add support for Just Black with Material You
-
-
Fixed
-
- Fixed sharing of files from SD Card
- Fix ChromeCast crash and bugs
- Fix Audio Crossfade
- Tried to fix incorrect song data in notification
-
-
-
-
April 8, 2022
-
v5.8.0
-
What's New
-
-
Fixed
-
- Fixed Classic Notification crash
- Fixed crash when clicking on Playlist in the Search Tab
- Fixed settings change not reflecting immediately
- Fixed shuffle
- Fixed Song duration not visible in Card & Blur card themes
- Fixed some Album skip styles
- Minor bug fixes & UI improvements
-
-
-
-
March 13, 2022
-
v5.7.3
-
What's New
-
- Added adaptive color in Material now playing theme
- Added an option to share crash report
- Added options to clear, pause history
-
-
Fixed
-
- Adapt Wallpaper accent for better readability
- Optimized search
- Made sleep timer precise
-
-
-
-
February 13, 2022
-
v5.7.2Beta
-
What's New
-
- Animated splash screen on Android 12 devices
-
-
Fixed
-
- Fixed crash when removing song from Playing queue
- Fixed lyrics editing crash
- Fixed shuffle button not working
- Fixed crash on song deletion
-
-
-
-
February 1, 2022
-
v5.7.1Beta
-
What's New
-
- Added option to disable changing song by swiping anywhere on the now playing screen
-
-
Fixed
-
- Fixed Playlist save on A11+
- Fixed Just Black theme
- Fixed some UI issues
-
-
Improved
-
- Improvements to search
-
-
-
-
January 25, 2022
-
v5.7.0Beta
-
What's New
-
- Added Android Auto
- Added accent color extraction on Android 8.1+ devices
- Added new Circle widget
- Added Collapsing appbar to library tabs with an option to switch back to simple appbar
-
- Added Search tab
- Added option to use circular play button
- Added lyrics editing on A11+ devices again
- Added Long Press to forward, rewind current song
- Added ability to set Playback speed and pitch
- Added option to show lyrics over Cover
- Added option to keep screen on when showing lyrics
- Added option to switch to Manrope font
-
-
Fixed
-
- Fixed Gapless Playback
- Fixed Shuffle FAB going behind Mini Player
- Fixed crashes on Pre-marshmallow devices
- Blacklisted songs can't be played after opening from outside the app
- Fixed various small bugs and some minor improvements
-
-
-
-
January 1, 2021
-
v5.6.1
-
Fixed
-
- Fixed artist covers not updating and showing album cover images.
- Fixed FAB's not visible (Shuffle, Save, etc.)
- Fixed a crash when a Song is deleted in Artist Details
- Fixed Snowfall effect
- Fixed empty notification when queue is cleared
-
-
-
-
December 25, 2021
-
v5.6.0Beta
-
What's New
-
- Added Artwork editing for songs
- Circle theme has album art now
- Upgraded tag editor library, we should support reading tags of more formats now e.g.
- opus.
-
- Added Snowfall effect
- Crossfade effect for background when Song is changed for Blur, Blur card themes
- Album art is hidden when Show lyrics is enabled
- Added fastscroll in Playlists tab
- Added Chooser to choose what to restore
- Hide BottomNavigation when only one tab is selected in Library Categories(This was
- already there but when changing screens it was getting visible)
-
-
-
Fixed
-
- Fixed Ringtone crash
- Fixed blank album cover bug
- Fixed bottom navigation visible in Playing Queue
- Fixed lockscreen dragging glitch
- Fixed incorrect colors when no cover art is available
- Fixed favorite not updating when song is changed
- Fixed playlist not getting created & playlist creation crash with same name
- Fixed a bug in "Plain" Now playing theme where onClick event is consumed by the views
- behind the bottom sheet
-
-
-
-
-
December 6, 2021
-
v5.4.2Beta
-
Fixed
-
-
-
-
December 1, 2021
-
v5.4.1Beta
-
What's New
-
- Added file selection from system file picker for restore
- Added Grid size selection for Playlists
-
-
Improved
-
- Enable Material You by default on Android 12
- Reworked Grid Style saving
- Improved Playlist preview images
- UI improvements
-
-
Improved
-
- Fixed File deletion on Android 10
- Fixed Sleep Timer crash
- Fixed Bottom Toolbar not clickable in now playing when Shuffle is clicked
- Fixed Album Artist sort order
- Fixed a crash when clicking the "Clear All" button in the Blacklist folder selection
-
- Fixed Continuous crashes on A12 when the song ends
- Fixed New Music Mix multiple clicks crash
-
-
-
-
November 22, 2021
-
v5.4.0Beta
-
What's New
-
- Material You
- Going Edge-to-Edge
- Added Backup & restore
-
-
Improved
-
- Improved Animations
- Improved Crossfade
-
-
-
-
September 06, 2021
-
v5.0.0
-
What's New
-
- Added ability to Remember last tab
- Added Whitelisting songs
- You can now browse SDCard from Folders Tab
-
-
-
-
August 22, 2021
-
v4.4.0Beta
-
What's New
-
- Added Crossfade
- Multi-select in Album and Artist Details
- Albums now show Album Artists instead of artist of first song
-
-
Fixed
-
- Fixed Playlist Preview Images
-
-
-
-
August 11, 2021
-
v4.2.30Beta
-
What's New
-
- Revamped Playlist Tab
- Revamped Genres Tab
- Added support for embedded Synced Lyrics
- Added animated icons
-
-
Fixed
-
- Fixed Language Switching
- Fixed some reported bugs
-
-
-
-
July 18, 2021
-
v4.2.020Beta
-
What's New
-
- Added ChromeCast Support
- Added Lyrics Editor for Normal and Synced Lyrics
- Added Ripple Animation for Color Theme
- Added Drag to seek in Tiny Theme
- Added Playlist Order
- Added Search Filters
- Added Audio Fade
- Synced Lyrics in all Themes
- Swipe anywhere to change song
-
-
Improved
-
- Fixed Navigate by Album Artist
- Changed New Music Mix Actions
- Improved Animations
- And some minor bug fixes and improvements
-
-
-
-
October 12, 2020
-
v4.0.10
-
What's New
-
- Re-built from scratch using MVVM Architecture and JetPack Components
- New Material Design icon
- Implemented a custom database for playlists
- Added new Material Design motions
- Bug fixes & performance improvements
- Revamped Home tab UI
- Android 11 support
- And more!
-
-
-
April 30, 2020
-
v3.5.110Beta
-
What's New
+
v3.5.110
+
Beta version
+
Changed profile form image to icon
New what's new screen
- Added In-App language changer, where you can select language
+ Added In-App language changer, where you can select language
-
Improved
+
- Improved loading of Songs, Albums, Artists, Genres, Playlists
+ Improved loading of Songs, Albums, Artists, Genres, Playlists
-
+
+ *If you face any UI related issues you clear app data and cache, if itsnot working try to uninstall and install
+ again.
\ No newline at end of file
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
index aa6e4ae9a..991844b5e 100644
Binary files a/app/src/main/ic_launcher-playstore.png and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png
index 7e7647db5..ff0f8b9ff 100644
Binary files a/app/src/main/ic_launcher-web.png and b/app/src/main/ic_launcher-web.png differ
diff --git a/app/src/main/java/code/name/monkey/retromusic/App.kt b/app/src/main/java/code/name/monkey/retromusic/App.kt
index 93dfe4d96..afabc12b6 100644
--- a/app/src/main/java/code/name/monkey/retromusic/App.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/App.kt
@@ -1,67 +1,78 @@
/*
- * Copyright (c) 2020 Hemanth Savarla.
+ * 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 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
-import android.app.Application
-import androidx.preference.PreferenceManager
-import cat.ereza.customactivityoncrash.config.CaocConfig
+import android.widget.Toast
+import androidx.multidex.MultiDexApplication
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.activities.MainActivity
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
+import code.name.monkey.retromusic.dagger.DaggerMusicComponent
+import code.name.monkey.retromusic.dagger.MusicComponent
+import code.name.monkey.retromusic.dagger.module.AppModule
+import com.anjlab.android.iab.v3.BillingProcessor
+import com.anjlab.android.iab.v3.TransactionDetails
-class App : Application() {
+class App : MultiDexApplication() {
- private val wallpaperAccentManager = WallpaperAccentManager(this)
+ lateinit var billingProcessor: BillingProcessor
override fun onCreate() {
super.onCreate()
instance = this
+ musicComponent = initDagger(this)
- startKoin {
- androidContext(this@App)
- modules(appModules)
- }
// default theme
if (!ThemeStore.isConfigured(this, 3)) {
ThemeStore.editTheme(this)
- .accentColorRes(code.name.monkey.appthemehelper.R.color.md_deep_purple_A200)
+ .accentColorRes(R.color.md_deep_purple_A200)
.coloredNavigationBar(true)
.commit()
}
- wallpaperAccentManager.init()
if (VersionUtils.hasNougatMR())
DynamicShortcutManager(this).initDynamicShortcuts()
- // setting Error activity
- CaocConfig.Builder.create().errorActivity(ErrorActivity::class.java)
- .restartActivity(MainActivity::class.java).apply()
+ // automatically restores purchases
+ billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY,
+ object : BillingProcessor.IBillingHandler {
+ override fun onProductPurchased(productId: String, details: TransactionDetails?) {}
- // Set Default values for now playing preferences
- // This will reduce startup time for now playing settings fragment as Preference listener of AbsSlidingMusicPanelActivity won't be called
- PreferenceManager.setDefaultValues(this, R.xml.pref_now_playing_screen, false)
+ override fun onPurchaseHistoryRestored() {
+ Toast.makeText(
+ this@App,
+ R.string.restored_previous_purchase_please_restart,
+ Toast.LENGTH_LONG
+ )
+ .show()
+ }
+
+ override fun onBillingError(errorCode: Int, error: Throwable?) {}
+
+ override fun onBillingInitialized() {}
+ })
}
+ private fun initDagger(app: App): MusicComponent =
+ DaggerMusicComponent.builder()
+ .appModule(AppModule(app))
+ .build()
+
override fun onTerminate() {
super.onTerminate()
- wallpaperAccentManager.release()
+ billingProcessor.release()
}
companion object {
@@ -70,5 +81,15 @@ class App : Application() {
fun getContext(): App {
return instance!!
}
+
+ fun isProVersion(): Boolean {
+ return BuildConfig.DEBUG || instance?.billingProcessor!!.isPurchased(
+ PRO_VERSION_PRODUCT_ID
+ )
+ }
+
+ lateinit var musicComponent: MusicComponent
+
+ const val PRO_VERSION_PRODUCT_ID = "pro_version"
}
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/Constants.kt b/app/src/main/java/code/name/monkey/retromusic/Constants.kt
index 31032f62b..0b3c6fa9d 100644
--- a/app/src/main/java/code/name/monkey/retromusic/Constants.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/Constants.kt
@@ -1,154 +1,53 @@
/*
- * Copyright (c) 2020 Hemanth Savarla.
+ * 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 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
import android.provider.BaseColumns
import android.provider.MediaStore
object Constants {
- const val TRANSLATE = "https://crowdin.com/project/retromusicplayer"
- const val GITHUB_PROJECT = "https://github.com/MuntashirAkon/Metro"
- const val TELEGRAM_CHANGE_LOG = "https://t.me/AppManagerChannel"
+
+ const val RATE_ON_GOOGLE_PLAY =
+ "https://play.google.com/store/apps/details?id=code.name.monkey.retromusic"
+ const val TRANSLATE = "http://monkeycodeapp.oneskyapp.com/collaboration/project?id=238534"
+ const val GITHUB_PROJECT = "https://github.com/h4h13/RetroMusicPlayer"
+ const val TELEGRAM_CHANGE_LOG = "https://t.me/retromusiclog"
const val USER_PROFILE = "profile.jpg"
const val USER_BANNER = "banner.jpg"
- const val FAQ_LINK = "https://github.com/MuntashirAkon/Metro/blob/master/FAQ.md"
+ 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 PINTEREST = "https://in.pinterest.com/retromusicapp/"
- const val IS_MUSIC =
+ const val BASE_SELECTION =
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
- DATA, // 5
+ MediaStore.Audio.AudioColumns.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
- )
+ MediaStore.Audio.AudioColumns.ARTIST,// 10
+ MediaStore.Audio.AudioColumns.COMPOSER
+ )// 11
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"
-const val EXTRA_PLAYLIST_ID = "extra_playlist_id"
-const val EXTRA_ALBUM_ID = "extra_album_id"
-const val EXTRA_ARTIST_ID = "extra_artist_id"
-const val EXTRA_SONG = "extra_songs"
-const val EXTRA_PLAYLISTS = "extra_playlists"
-const val LIBRARY_CATEGORIES = "library_categories"
-const val EXTRA_SONG_INFO = "extra_song_info"
-const val DESATURATED_COLOR = "desaturated_color"
-const val BLACK_THEME = "black_theme"
-const val KEEP_SCREEN_ON = "keep_screen_on"
-const val TOGGLE_HOME_BANNER = "toggle_home_banner"
-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 = "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"
-const val TOGGLE_HEADSET = "toggle_headset"
-const val GENERAL_THEME = "general_theme"
-const val ACCENT_COLOR = "accent_color"
-const val SHOULD_COLOR_APP_SHORTCUTS = "should_color_app_shortcuts"
-const val CIRCULAR_ALBUM_ART = "circular_album_art"
-const val USER_NAME = "user_name"
-const val TOGGLE_FULL_SCREEN = "toggle_full_screen"
-const val TOGGLE_VOLUME = "toggle_volume"
-const val ADAPTIVE_COLOR_APP = "adaptive_color_app"
-const val HOME_ARTIST_GRID_STYLE = "home_artist_grid_style"
-const val HOME_ALBUM_GRID_STYLE = "home_album_grid_style"
-const val TOGGLE_ADD_CONTROLS = "toggle_add_controls"
-const val ALBUM_COVER_STYLE = "album_cover_style_id"
-const val ALBUM_COVER_TRANSFORM = "album_cover_transform"
-const val TAB_TEXT_MODE = "tab_text_mode"
-const val LANGUAGE_NAME = "language_name"
-const val LOCALE_AUTO_STORE_ENABLED = "locale_auto_store_enabled"
-const val SLEEP_TIMER_FINISH_SONG = "sleep_timer_finish_song"
-const val ALBUM_GRID_STYLE = "album_grid_style_home"
-const val ARTIST_GRID_STYLE = "artist_grid_style_home"
-const val SAF_SDCARD_URI = "saf_sdcard_uri"
-const val SONG_SORT_ORDER = "song_sort_order"
-const val SONG_GRID_SIZE = "song_grid_size"
-const val GENRE_SORT_ORDER = "genre_sort_order"
-const val BLUETOOTH_PLAYBACK = "bluetooth_playback"
-const val INITIALIZED_BLACKLIST = "initialized_blacklist"
-const val ARTIST_SORT_ORDER = "artist_sort_order"
-const val ARTIST_ALBUM_SORT_ORDER = "artist_album_sort_order"
-const val ALBUM_SORT_ORDER = "album_sort_order"
-const val PLAYLIST_SORT_ORDER = "playlist_sort_order"
-const val ALBUM_SONG_SORT_ORDER = "album_song_sort_order"
-const val ARTIST_SONG_SORT_ORDER = "artist_song_sort_order"
-const val ALBUM_GRID_SIZE = "album_grid_size"
-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 LAST_ADDED_CUTOFF = "last_added_interval"
-const val LAST_SLEEP_TIMER_VALUE = "last_sleep_timer_value"
-const val NEXT_SLEEP_TIMER_ELAPSED_REALTIME = "next_sleep_timer_elapsed_real_time"
-const val IGNORE_MEDIA_STORE_ARTWORK = "ignore_media_store_artwork"
-const val LAST_CHANGELOG_VERSION = "last_changelog_version"
-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"
-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"
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/CustomBottomSheetBehavior.java b/app/src/main/java/code/name/monkey/retromusic/CustomBottomSheetBehavior.java
new file mode 100644
index 000000000..ba99e8aae
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/CustomBottomSheetBehavior.java
@@ -0,0 +1,37 @@
+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;
+
+
+public class CustomBottomSheetBehavior extends BottomSheetBehavior {
+
+ private static final String TAG = "CustomBottomSheetBehavi";
+
+ private boolean allowDragging = true;
+
+ public CustomBottomSheetBehavior() {
+ }
+
+ public CustomBottomSheetBehavior(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setAllowDragging(boolean allowDragging) {
+ this.allowDragging = allowDragging;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
+ if (!allowDragging) {
+ return false;
+ }
+ return super.onInterceptTouchEvent(parent, child, event);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/HomeSection.kt b/app/src/main/java/code/name/monkey/retromusic/HomeSection.kt
deleted file mode 100644
index 4f1273a03..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/HomeSection.kt
+++ /dev/null
@@ -1,42 +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
-
-import androidx.annotation.IntDef
-
-@IntDef(
- RECENT_ALBUMS,
- TOP_ALBUMS,
- RECENT_ARTISTS,
- TOP_ARTISTS,
- SUGGESTIONS,
- FAVOURITES,
- GENRES,
- PLAYLISTS
-)
-@Retention(AnnotationRetention.SOURCE)
-annotation class HomeSection
-
-const val RECENT_ALBUMS = 3
-const val TOP_ALBUMS = 1
-const val RECENT_ARTISTS = 2
-const val TOP_ARTISTS = 0
-const val SUGGESTIONS = 5
-const val FAVOURITES = 4
-const val GENRES = 6
-const val PLAYLISTS = 7
-const val HISTORY_PLAYLIST = 8
-const val LAST_ADDED_PLAYLIST = 9
-const val TOP_PLAYED_PLAYLIST = 10
diff --git a/app/src/main/java/code/name/monkey/retromusic/LanguageContextWrapper.java b/app/src/main/java/code/name/monkey/retromusic/LanguageContextWrapper.java
new file mode 100644
index 000000000..72ae6aba3
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/LanguageContextWrapper.java
@@ -0,0 +1,42 @@
+package code.name.monkey.retromusic;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.LocaleList;
+
+import java.util.Locale;
+
+import code.name.monkey.appthemehelper.util.VersionUtils;
+
+public class LanguageContextWrapper extends android.content.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);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt
deleted file mode 100644
index d88f8a760..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt
+++ /dev/null
@@ -1,156 +0,0 @@
-package code.name.monkey.retromusic
-
-import androidx.room.Room
-import code.name.monkey.retromusic.auto.AutoMusicProvider
-import code.name.monkey.retromusic.db.MIGRATION_23_24
-import code.name.monkey.retromusic.db.RetroDatabase
-import code.name.monkey.retromusic.fragments.LibraryViewModel
-import code.name.monkey.retromusic.fragments.albums.AlbumDetailsViewModel
-import code.name.monkey.retromusic.fragments.artists.ArtistDetailsViewModel
-import code.name.monkey.retromusic.fragments.genres.GenreDetailsViewModel
-import code.name.monkey.retromusic.fragments.playlists.PlaylistDetailsViewModel
-import code.name.monkey.retromusic.model.Genre
-import code.name.monkey.retromusic.repository.*
-import org.koin.android.ext.koin.androidContext
-import org.koin.androidx.viewmodel.dsl.viewModel
-import org.koin.dsl.bind
-import org.koin.dsl.module
-
-private val roomModule = module {
-
- single {
- Room.databaseBuilder(androidContext(), RetroDatabase::class.java, "playlist.db")
- .addMigrations(MIGRATION_23_24)
- .build()
- }
-
- factory {
- get().playlistDao()
- }
-
- factory {
- get().playCountDao()
- }
-
- factory {
- get().historyDao()
- }
-
- single {
- RealRoomRepository(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
- }
-}
-private val dataModule = module {
- single {
- RealRepository(
- get(),
- get(),
- get(),
- get(),
- get(),
- get(),
- get(),
- get(),
- get(),
- get(),
- )
- } bind Repository::class
-
- single {
- RealSongRepository(get())
- } bind SongRepository::class
-
- single {
- RealGenreRepository(get(), get())
- } bind GenreRepository::class
-
- single {
- RealAlbumRepository(get())
- } bind AlbumRepository::class
-
- single {
- RealArtistRepository(get(), get())
- } bind ArtistRepository::class
-
- single {
- RealPlaylistRepository(get())
- } bind PlaylistRepository::class
-
- single {
- RealTopPlayedRepository(get(), get(), get(), get())
- } bind TopPlayedRepository::class
-
- single {
- RealLastAddedRepository(
- get(),
- get(),
- get()
- )
- } bind LastAddedRepository::class
-
- single {
- RealSearchRepository(
- get(),
- get(),
- get(),
- get(),
- get()
- )
- }
-}
-
-private val viewModules = module {
-
- viewModel {
- LibraryViewModel(get())
- }
-
- viewModel { (albumId: Long) ->
- AlbumDetailsViewModel(
- get(),
- albumId
- )
- }
-
- viewModel { (artistId: Long?, artistName: String?) ->
- ArtistDetailsViewModel(
- get(),
- artistId,
- artistName
- )
- }
-
- viewModel { (playlistId: Long) ->
- PlaylistDetailsViewModel(
- get(),
- playlistId
- )
- }
-
- viewModel { (genre: Genre) ->
- GenreDetailsViewModel(
- get(),
- genre
- )
- }
-}
-
-val appModules = listOf(mainModule, dataModule, autoModule, viewModules, roomModule)
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/Result.kt b/app/src/main/java/code/name/monkey/retromusic/Result.kt
new file mode 100644
index 000000000..64b638a6e
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/Result.kt
@@ -0,0 +1,26 @@
+/*
+ * 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
+
+/**
+ * Created by hemanths on 2019-10-23.
+ */
+
+sealed class Result {
+
+ class Success(val data: T) : Result()
+
+ class Error(val exception: Throwable) : Result()
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/AboutActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/AboutActivity.kt
new file mode 100644
index 000000000..6f51e696f
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/AboutActivity.kt
@@ -0,0 +1,169 @@
+package code.name.monkey.retromusic.activities
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.Bundle
+import android.view.MenuItem
+import android.view.View
+import androidx.core.app.ShareCompat
+import androidx.recyclerview.widget.DefaultItemAnimator
+import androidx.recyclerview.widget.LinearLayoutManager
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
+import code.name.monkey.retromusic.App
+import code.name.monkey.retromusic.Constants.APP_INSTAGRAM_LINK
+import code.name.monkey.retromusic.Constants.APP_TELEGRAM_LINK
+import code.name.monkey.retromusic.Constants.APP_TWITTER_LINK
+import code.name.monkey.retromusic.Constants.FAQ_LINK
+import code.name.monkey.retromusic.Constants.GITHUB_PROJECT
+import code.name.monkey.retromusic.Constants.PINTEREST
+import code.name.monkey.retromusic.Constants.RATE_ON_GOOGLE_PLAY
+import code.name.monkey.retromusic.Constants.TELEGRAM_CHANGE_LOG
+import code.name.monkey.retromusic.Constants.TRANSLATE
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.activities.base.AbsBaseActivity
+import code.name.monkey.retromusic.adapter.ContributorAdapter
+import code.name.monkey.retromusic.model.Contributor
+import code.name.monkey.retromusic.util.NavigationUtil
+import code.name.monkey.retromusic.util.PreferenceUtil
+import com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.list.listItems
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import kotlinx.android.synthetic.main.activity_about.*
+import kotlinx.android.synthetic.main.card_credit.*
+import kotlinx.android.synthetic.main.card_other.*
+import kotlinx.android.synthetic.main.card_retro_info.*
+import kotlinx.android.synthetic.main.card_social.*
+import java.io.IOException
+import java.nio.charset.StandardCharsets
+
+class AboutActivity : AbsBaseActivity(), View.OnClickListener {
+
+ private val contributorsJson: String?
+ get() {
+ val json: String
+ try {
+ val inputStream = assets.open("contributors.json")
+ val size = inputStream.available()
+ val buffer = ByteArray(size)
+ inputStream.read(buffer)
+ inputStream.close()
+ json = String(buffer, StandardCharsets.UTF_8)
+ } catch (ex: IOException) {
+ ex.printStackTrace()
+ return null
+ }
+ return json
+ }
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setDrawUnderStatusBar()
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_about)
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setLightNavigationBar(true)
+
+ val toolbarColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
+ toolbar.setBackgroundColor(toolbarColor)
+ ToolbarContentTintHelper.colorBackButton(toolbar)
+ setSupportActionBar(toolbar)
+ version.setSummary(getAppVersion())
+ setUpView()
+ loadContributors()
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home) {
+ onBackPressed()
+ return true
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ private fun openUrl(url: String) {
+ val i = Intent(Intent.ACTION_VIEW)
+ i.data = Uri.parse(url)
+ i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ startActivity(i)
+ }
+
+ private fun setUpView() {
+ appGithub.setOnClickListener(this)
+ faqLink.setOnClickListener(this)
+ telegramLink.setOnClickListener(this)
+ appRate.setOnClickListener(this)
+ appTranslation.setOnClickListener(this)
+ appShare.setOnClickListener(this)
+ donateLink.setOnClickListener(this)
+ instagramLink.setOnClickListener(this)
+ twitterLink.setOnClickListener(this)
+ changelog.setOnClickListener(this)
+ openSource.setOnClickListener(this)
+ pinterestLink.setOnClickListener(this)
+ bugReportLink.setOnClickListener(this)
+ }
+
+ override fun onClick(view: View) {
+ when (view.id) {
+ R.id.pinterestLink -> openUrl(PINTEREST)
+ R.id.faqLink -> openUrl(FAQ_LINK)
+ R.id.telegramLink -> openUrl(APP_TELEGRAM_LINK)
+ R.id.appGithub -> openUrl(GITHUB_PROJECT)
+ R.id.appTranslation -> openUrl(TRANSLATE)
+ R.id.appRate -> openUrl(RATE_ON_GOOGLE_PLAY)
+ R.id.appShare -> shareApp()
+ R.id.donateLink -> NavigationUtil.goToSupportDevelopment(this)
+ R.id.instagramLink -> openUrl(APP_INSTAGRAM_LINK)
+ R.id.twitterLink -> openUrl(APP_TWITTER_LINK)
+ R.id.changelog -> openUrl(TELEGRAM_CHANGE_LOG)
+ R.id.openSource -> NavigationUtil.goToOpenSource(this)
+ R.id.bugReportLink -> NavigationUtil.bugReport(this)
+ }
+ }
+
+ private fun showChangeLogOptions() {
+ MaterialDialog(this).show {
+ cornerRadius(PreferenceUtil.getInstance(this@AboutActivity).dialogCorner)
+ listItems(items = listOf("Telegram Channel", "App")) { _, position, _ ->
+ if (position == 0) {
+ openUrl(TELEGRAM_CHANGE_LOG)
+ } else {
+ NavigationUtil.gotoWhatNews(this@AboutActivity)
+ }
+ }
+ }
+ }
+
+ private fun getAppVersion(): String {
+ return try {
+ val isPro = if (App.isProVersion()) "Pro" else "Free"
+ val packageInfo = packageManager.getPackageInfo(packageName, 0)
+ "${packageInfo.versionName} $isPro"
+ } catch (e: PackageManager.NameNotFoundException) {
+ e.printStackTrace()
+ "0.0.0"
+ }
+ }
+
+ private fun shareApp() {
+ ShareCompat.IntentBuilder.from(this).setType("text/plain")
+ .setChooserTitle(R.string.share_app)
+ .setText(String.format(getString(R.string.app_share), packageName)).startChooser()
+ }
+
+ private fun loadContributors() {
+ val type = object : TypeToken>() {
+
+ }.type
+ val contributors = Gson().fromJson>(contributorsJson, type)
+
+ val contributorAdapter = ContributorAdapter(contributors)
+ recyclerView.layoutManager = LinearLayoutManager(this)
+ recyclerView.itemAnimator = DefaultItemAnimator()
+ recyclerView.adapter = contributorAdapter
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/AlbumDetailsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/AlbumDetailsActivity.kt
new file mode 100644
index 000000000..7370fdd5f
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/AlbumDetailsActivity.kt
@@ -0,0 +1,406 @@
+package code.name.monkey.retromusic.activities
+
+import android.app.ActivityOptions
+import android.content.Intent
+import android.os.Bundle
+import android.transition.Slide
+import android.view.Menu
+import android.view.MenuItem
+import android.view.SubMenu
+import android.view.View
+import android.widget.ImageView
+import androidx.core.app.ActivityCompat
+import androidx.recyclerview.widget.DefaultItemAnimator
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.LinearLayoutManager
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.appthemehelper.util.MaterialUtil
+import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
+import code.name.monkey.retromusic.App
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
+import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
+import code.name.monkey.retromusic.activities.tageditor.AlbumTagEditorActivity
+import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
+import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
+import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
+import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
+import code.name.monkey.retromusic.extensions.ripAlpha
+import code.name.monkey.retromusic.extensions.show
+import code.name.monkey.retromusic.glide.AlbumGlideRequest
+import code.name.monkey.retromusic.glide.ArtistGlideRequest
+import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
+import code.name.monkey.retromusic.helper.MusicPlayerRemote
+import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder
+import code.name.monkey.retromusic.interfaces.CabHolder
+import code.name.monkey.retromusic.model.Album
+import code.name.monkey.retromusic.model.Artist
+import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsPresenter
+import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsView
+import code.name.monkey.retromusic.rest.model.LastFmAlbum
+import code.name.monkey.retromusic.util.*
+import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
+import com.afollestad.materialcab.MaterialCab
+import com.bumptech.glide.Glide
+import kotlinx.android.synthetic.main.activity_album.*
+import kotlinx.android.synthetic.main.activity_album_content.*
+import java.util.*
+import javax.inject.Inject
+import android.util.Pair as UtilPair
+
+class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, CabHolder {
+ override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
+ cab?.let {
+ if (it.isActive) it.finish()
+ }
+ cab = MaterialCab(this, R.id.cab_stub)
+ .setMenu(menuRes)
+ .setCloseDrawableRes(R.drawable.ic_close_white_24dp)
+ .setBackgroundColor(
+ RetroColorUtil.shiftBackgroundColorForLightText(
+ ATHUtil.resolveColor(
+ this,
+ R.attr.colorSurface
+ )
+ )
+ )
+ .start(callback)
+ return cab as MaterialCab
+ }
+
+ private lateinit var simpleSongAdapter: SimpleSongAdapter
+ private lateinit var album: Album
+ private lateinit var artistImage: ImageView
+ private var cab: MaterialCab? = null
+ private val savedSortOrder: String
+ get() = PreferenceUtil.getInstance(this).albumDetailSongSortOrder
+
+ override fun createContentView(): View {
+ return wrapSlidingMusicPanel(R.layout.activity_album)
+ }
+
+ @Inject
+ lateinit var albumDetailsPresenter: AlbumDetailsPresenter
+
+ private fun windowEnterTransition() {
+ val slide = Slide()
+ slide.excludeTarget(R.id.appBarLayout, true)
+ slide.excludeTarget(R.id.status_bar, true)
+ slide.excludeTarget(android.R.id.statusBarBackground, true)
+ slide.excludeTarget(android.R.id.navigationBarBackground, true)
+
+ window.enterTransition = slide
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setDrawUnderStatusBar()
+ super.onCreate(savedInstanceState)
+ toggleBottomNavigationView(true)
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setTaskDescriptionColorAuto()
+ setLightNavigationBar(true)
+ window.sharedElementsUseOverlay = true
+
+ App.musicComponent.inject(this)
+ albumDetailsPresenter.attachView(this)
+
+ if (intent.extras!!.containsKey(EXTRA_ALBUM_ID)) {
+ intent.extras?.getInt(EXTRA_ALBUM_ID)?.let {
+ albumDetailsPresenter.loadAlbum(it)
+ albumCoverContainer?.transitionName =
+ "${getString(R.string.transition_album_art)}_$it"
+ }
+ } else {
+ finish()
+ }
+
+ windowEnterTransition()
+ ActivityCompat.postponeEnterTransition(this)
+
+
+ artistImage = findViewById(R.id.artistImage)
+
+ setupRecyclerView()
+
+ artistImage.setOnClickListener {
+ val artistPairs = ActivityOptions.makeSceneTransitionAnimation(
+ this,
+ UtilPair.create(
+ artistImage,
+ "${getString(R.string.transition_artist_image)}_${album.artistId}"
+ )
+ )
+ NavigationUtil.goToArtistOptions(this, album.artistId, artistPairs)
+ }
+ playAction.apply {
+ setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) }
+ }
+ shuffleAction.apply {
+ setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(album.songs!!, true) }
+ }
+
+ aboutAlbumText.setOnClickListener {
+ if (aboutAlbumText.maxLines == 4) {
+ aboutAlbumText.maxLines = Integer.MAX_VALUE
+ } else {
+ aboutAlbumText.maxLines = 4
+ }
+ }
+ }
+
+ private fun setupRecyclerView() {
+ simpleSongAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, this)
+ recyclerView.apply {
+ layoutManager = LinearLayoutManager(this@AlbumDetailsActivity)
+ itemAnimator = DefaultItemAnimator()
+ isNestedScrollingEnabled = false
+ adapter = simpleSongAdapter
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ albumDetailsPresenter.detachView()
+ }
+
+ override fun complete() {
+ ActivityCompat.startPostponedEnterTransition(this)
+ }
+
+ override fun album(album: Album) {
+ complete()
+ if (album.songs!!.isEmpty()) {
+ finish()
+ return
+ }
+ this.album = album
+
+ albumTitle.text = album.title
+ val songText =
+ resources.getQuantityString(
+ R.plurals.albumSongs,
+ album.songCount,
+ album.songCount
+ )
+ songTitle.text = songText
+
+ if (MusicUtil.getYearString(album.year) == "-") {
+ albumText.text = String.format(
+ "%s • %s",
+ album.artistName,
+ MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs))
+ )
+ } else {
+ albumText.text = String.format(
+ "%s • %s • %s",
+ album.artistName,
+ MusicUtil.getYearString(album.year),
+ MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs))
+ )
+ }
+ loadAlbumCover()
+ simpleSongAdapter.swapDataSet(album.songs)
+ albumDetailsPresenter.loadMore(album.artistId)
+ albumDetailsPresenter.aboutAlbum(album.artistName ?: "-", album.title ?: "-")
+ }
+
+ override fun moreAlbums(albums: List) {
+ moreTitle.show()
+ moreRecyclerView.show()
+ moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName)
+
+ val albumAdapter = HorizontalAlbumAdapter(this, albums, null)
+ moreRecyclerView.layoutManager = GridLayoutManager(
+ this,
+ 1,
+ GridLayoutManager.HORIZONTAL,
+ false
+ )
+ moreRecyclerView.adapter = albumAdapter
+ }
+
+ override fun aboutAlbum(lastFmAlbum: LastFmAlbum) {
+ if (lastFmAlbum.album != null) {
+ if (lastFmAlbum.album.wiki != null) {
+ aboutAlbumText.show()
+ aboutAlbumTitle.show()
+ aboutAlbumTitle.text =
+ String.format(getString(R.string.about_album_label), lastFmAlbum.album.name)
+ aboutAlbumText.text = lastFmAlbum.album.wiki.content
+ }
+ if (lastFmAlbum.album.listeners.isNotEmpty()) {
+ listeners.show()
+ listenersLabel.show()
+ scrobbles.show()
+ scrobblesLabel.show()
+
+ listeners.text = RetroUtil.formatValue(lastFmAlbum.album.listeners.toFloat())
+ scrobbles.text = RetroUtil.formatValue(lastFmAlbum.album.playcount.toFloat())
+ }
+ }
+ }
+
+ override fun loadArtistImage(artist: Artist) {
+ ArtistGlideRequest.Builder.from(Glide.with(this), artist)
+ .generatePalette(this)
+ .build()
+ .dontAnimate()
+ .dontTransform()
+ .into(object : RetroMusicColoredTarget(artistImage) {
+ override fun onColorReady(colors: MediaNotificationProcessor) {
+ }
+ })
+ }
+
+ private fun loadAlbumCover() {
+ AlbumGlideRequest.Builder.from(Glide.with(this), album.safeGetFirstSong())
+ .checkIgnoreMediaStore(this)
+ .ignoreMediaStore(PreferenceUtil.getInstance(this).ignoreMediaStoreArtwork())
+ .generatePalette(this)
+ .build()
+ .dontAnimate()
+ .dontTransform()
+ .into(object : RetroMusicColoredTarget(image) {
+ override fun onColorReady(colors: MediaNotificationProcessor) {
+ setColors(colors.backgroundColor)
+ }
+ })
+ }
+
+ private fun setColors(color: Int) {
+ val textColor = if (PreferenceUtil.getInstance(this).adaptiveColor)
+ color.ripAlpha()
+ else
+ ATHUtil.resolveColor(this, android.R.attr.textColorPrimary)
+
+ songTitle.setTextColor(textColor)
+ moreTitle.setTextColor(textColor)
+ aboutAlbumTitle.setTextColor(textColor)
+
+ val buttonColor = if (PreferenceUtil.getInstance(this).adaptiveColor)
+ color.ripAlpha()
+ else
+ ATHUtil.resolveColor(this, R.attr.colorSurface)
+
+ MaterialUtil.setTint(button = shuffleAction, color = buttonColor)
+ MaterialUtil.setTint(button = playAction, color = buttonColor)
+
+ val toolbarColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
+ toolbar.setBackgroundColor(toolbarColor)
+ setSupportActionBar(toolbar)
+ supportActionBar?.title = null
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.menu_album_detail, menu)
+ val sortOrder = menu.findItem(R.id.action_sort_order)
+ setUpSortOrderMenu(sortOrder.subMenu)
+ ToolbarContentTintHelper.handleOnCreateOptionsMenu(
+ this,
+ toolbar,
+ menu,
+ getToolbarBackgroundColor(toolbar)
+ )
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ return handleSortOrderMenuItem(item)
+ }
+
+ private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
+ var sortOrder: String? = null
+ val songs = simpleSongAdapter.dataSet
+ when (item.itemId) {
+ R.id.action_play_next -> {
+ MusicPlayerRemote.playNext(songs)
+ return true
+ }
+ R.id.action_add_to_current_playing -> {
+ MusicPlayerRemote.enqueue(songs)
+ return true
+ }
+ R.id.action_add_to_playlist -> {
+ AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST")
+ return true
+ }
+ R.id.action_delete_from_device -> {
+ DeleteSongsDialog.create(songs).show(supportFragmentManager, "DELETE_SONGS")
+ return true
+ }
+ android.R.id.home -> {
+ super.onBackPressed()
+ return true
+ }
+ R.id.action_tag_editor -> {
+ val intent = Intent(this, AlbumTagEditorActivity::class.java)
+ intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id)
+ val options = ActivityOptions.makeSceneTransitionAnimation(
+ this,
+ albumCoverContainer,
+ "${getString(R.string.transition_album_art)}_${album.id}"
+ )
+ startActivityForResult(intent, TAG_EDITOR_REQUEST, options.toBundle())
+ return true
+ }
+ /*Sort*/
+ R.id.action_sort_order_title -> sortOrder = AlbumSongSortOrder.SONG_A_Z
+ R.id.action_sort_order_title_desc -> sortOrder = AlbumSongSortOrder.SONG_Z_A
+ R.id.action_sort_order_track_list -> sortOrder = AlbumSongSortOrder.SONG_TRACK_LIST
+ R.id.action_sort_order_artist_song_duration -> sortOrder =
+ AlbumSongSortOrder.SONG_DURATION
+ }
+ if (sortOrder != null) {
+ item.isChecked = true
+ setSaveSortOrder(sortOrder)
+ }
+ return true
+ }
+
+ private fun setUpSortOrderMenu(sortOrder: SubMenu) {
+ when (savedSortOrder) {
+ AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title)
+ .isChecked = true
+ AlbumSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc)
+ .isChecked = true
+ AlbumSongSortOrder.SONG_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list)
+ .isChecked =
+ true
+ AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration)
+ .isChecked = true
+ }
+ }
+
+ private fun setSaveSortOrder(sortOrder: String?) {
+ PreferenceUtil.getInstance(this).albumDetailSongSortOrder = sortOrder
+ reload()
+ }
+
+ override fun onMediaStoreChanged() {
+ super.onMediaStoreChanged()
+ reload()
+ }
+
+ private fun reload() {
+ if (intent.extras!!.containsKey(EXTRA_ALBUM_ID)) {
+ intent.extras?.getInt(EXTRA_ALBUM_ID)?.let { albumDetailsPresenter.loadAlbum(it) }
+ } else {
+ finish()
+ }
+ }
+
+ override fun onBackPressed() {
+ if (cab != null && cab!!.isActive) {
+ cab?.finish()
+ } else {
+ super.onBackPressed()
+ }
+ }
+
+ companion object {
+
+ const val EXTRA_ALBUM_ID = "extra_album_id"
+ private const val TAG_EDITOR_REQUEST = 2001
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/ArtistDetailActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/ArtistDetailActivity.kt
new file mode 100755
index 000000000..08c3f68f0
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/ArtistDetailActivity.kt
@@ -0,0 +1,360 @@
+package code.name.monkey.retromusic.activities
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.text.Spanned
+import android.transition.Slide
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.widget.Toast
+import androidx.core.app.ActivityCompat
+import androidx.core.text.HtmlCompat
+import androidx.recyclerview.widget.DefaultItemAnimator
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.LinearLayoutManager
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.appthemehelper.util.MaterialUtil
+import code.name.monkey.retromusic.App
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
+import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
+import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
+import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
+import code.name.monkey.retromusic.extensions.ripAlpha
+import code.name.monkey.retromusic.extensions.show
+import code.name.monkey.retromusic.glide.ArtistGlideRequest
+import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
+import code.name.monkey.retromusic.helper.MusicPlayerRemote
+import code.name.monkey.retromusic.interfaces.CabHolder
+import code.name.monkey.retromusic.model.Artist
+import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter
+import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsView
+import code.name.monkey.retromusic.rest.model.LastFmArtist
+import code.name.monkey.retromusic.util.*
+import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
+import com.afollestad.materialcab.MaterialCab
+import com.bumptech.glide.Glide
+import kotlinx.android.synthetic.main.activity_artist_content.*
+import kotlinx.android.synthetic.main.activity_artist_details.*
+import java.util.*
+import javax.inject.Inject
+import kotlin.collections.ArrayList
+
+class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView, CabHolder {
+ override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
+ cab?.let {
+ if (it.isActive) it.finish()
+ }
+ cab = MaterialCab(this, R.id.cab_stub)
+ .setMenu(menuRes)
+ .setCloseDrawableRes(R.drawable.ic_close_white_24dp)
+ .setBackgroundColor(
+ RetroColorUtil.shiftBackgroundColorForLightText(
+ ATHUtil.resolveColor(
+ this,
+ R.attr.colorSurface
+ )
+ )
+ )
+ .start(callback)
+ return cab as MaterialCab
+ }
+
+ private var cab: MaterialCab? = null
+ private var biography: Spanned? = null
+ private lateinit var artist: Artist
+ private lateinit var songAdapter: SimpleSongAdapter
+ private lateinit var albumAdapter: HorizontalAlbumAdapter
+ private var forceDownload: Boolean = false
+
+ override fun createContentView(): View {
+ return wrapSlidingMusicPanel(R.layout.activity_artist_details)
+ }
+
+ @Inject
+ lateinit var artistDetailsPresenter: ArtistDetailsPresenter
+
+ private fun windowEnterTransition() {
+ val slide = Slide()
+ slide.excludeTarget(R.id.appBarLayout, true)
+ slide.excludeTarget(R.id.status_bar, true)
+ slide.excludeTarget(android.R.id.statusBarBackground, true)
+ slide.excludeTarget(android.R.id.navigationBarBackground, true)
+ window.enterTransition = slide
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setDrawUnderStatusBar()
+ super.onCreate(savedInstanceState)
+ toggleBottomNavigationView(true)
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setTaskDescriptionColorAuto()
+ setLightNavigationBar(true)
+ window.sharedElementsUseOverlay = true
+
+ App.musicComponent.inject(this)
+ artistDetailsPresenter.attachView(this)
+
+ if (intent.extras!!.containsKey(EXTRA_ARTIST_ID)) {
+ intent.extras?.getInt(EXTRA_ARTIST_ID)?.let {
+ artistDetailsPresenter.loadArtist(it)
+ val name = "${getString(R.string.transition_artist_image)}_$it"
+ artistCoverContainer?.transitionName = name
+ }
+ } else {
+ finish()
+ }
+
+ windowEnterTransition()
+ ActivityCompat.postponeEnterTransition(this)
+
+ setupRecyclerView()
+
+ playAction.apply {
+ setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) }
+ }
+ shuffleAction.apply {
+ setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) }
+ }
+
+ biographyText.setOnClickListener {
+ if (biographyText.maxLines == 4) {
+ biographyText.maxLines = Integer.MAX_VALUE
+ } else {
+ biographyText.maxLines = 4
+ }
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ artistDetailsPresenter.detachView()
+ }
+
+ private fun setupRecyclerView() {
+ albumAdapter = HorizontalAlbumAdapter(this, ArrayList(), null)
+ albumRecyclerView.apply {
+ itemAnimator = DefaultItemAnimator()
+ layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false)
+ adapter = albumAdapter
+ }
+ songAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, this)
+ recyclerView.apply {
+ itemAnimator = DefaultItemAnimator()
+ layoutManager = LinearLayoutManager(this.context)
+ adapter = songAdapter
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ when (requestCode) {
+ REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
+ data?.data?.let {
+ CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist, it)
+ }
+ }
+ else -> if (resultCode == Activity.RESULT_OK) {
+ reload()
+ }
+ }
+ }
+
+ override fun showEmptyView() {
+ }
+
+ override fun complete() {
+ ActivityCompat.startPostponedEnterTransition(this)
+ }
+
+ override fun artist(artist: Artist) {
+ complete()
+ if (artist.songCount <= 0) {
+ finish()
+ }
+ this.artist = artist
+ loadArtistImage()
+
+ if (RetroUtil.isAllowedToDownloadMetadata(this)) {
+ loadBiography(artist.name)
+ }
+ artistTitle.text = artist.name
+ text.text = String.format(
+ "%s • %s",
+ MusicUtil.getArtistInfoString(this, artist),
+ MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs))
+ )
+ val songText =
+ resources.getQuantityString(
+ R.plurals.albumSongs,
+ artist.songCount,
+ artist.songCount
+ )
+ val albumText =
+ resources.getQuantityString(
+ R.plurals.albums,
+ artist.songCount,
+ artist.songCount
+ )
+ songTitle.text = songText
+ albumTitle.text = albumText
+ songAdapter.swapDataSet(artist.songs)
+ albumAdapter.swapDataSet(artist.albums!!)
+ }
+
+ private fun loadBiography(
+ name: String,
+ lang: String? = Locale.getDefault().language
+ ) {
+ biography = null
+ this.lang = lang
+ artistDetailsPresenter.loadBiography(name, lang, null)
+ }
+
+ override fun artistInfo(lastFmArtist: LastFmArtist?) {
+ if (lastFmArtist != null && lastFmArtist.artist != null) {
+ val bioContent = lastFmArtist.artist.bio.content
+ if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) {
+ biographyText.visibility = View.VISIBLE
+ biographyTitle.visibility = View.VISIBLE
+ biography = HtmlCompat.fromHtml(bioContent, HtmlCompat.FROM_HTML_MODE_LEGACY)
+ biographyText.text = biography
+ if (lastFmArtist.artist.stats.listeners.isNotEmpty()) {
+ listeners.show()
+ listenersLabel.show()
+ scrobbles.show()
+ scrobblesLabel.show()
+
+ listeners.text =
+ RetroUtil.formatValue(lastFmArtist.artist.stats.listeners.toFloat())
+ scrobbles.text =
+ RetroUtil.formatValue(lastFmArtist.artist.stats.playcount.toFloat())
+ }
+ }
+ }
+
+ // If the "lang" parameter is set and no biography is given, retry with default language
+ if (biography == null && lang != null) {
+ loadBiography(artist.name, null)
+ }
+ }
+
+ private var lang: String? = null
+
+ private fun loadArtistImage() {
+ ArtistGlideRequest.Builder.from(Glide.with(this), artist).generatePalette(this).build()
+ .dontAnimate().into(object : RetroMusicColoredTarget(image) {
+ override fun onColorReady(colors: MediaNotificationProcessor) {
+ setColors(colors.backgroundColor)
+ }
+
+ })
+ }
+
+ private fun setColors(color: Int) {
+ val textColor = if (PreferenceUtil.getInstance(this).adaptiveColor)
+ color.ripAlpha()
+ else
+ ATHUtil.resolveColor(this, android.R.attr.textColorPrimary)
+
+ albumTitle.setTextColor(textColor)
+ songTitle.setTextColor(textColor)
+ biographyTitle.setTextColor(textColor)
+
+ val buttonColor = if (PreferenceUtil.getInstance(this).adaptiveColor)
+ color.ripAlpha()
+ else
+ ATHUtil.resolveColor(this, R.attr.colorSurface)
+
+ MaterialUtil.setTint(button = shuffleAction, color = buttonColor)
+ MaterialUtil.setTint(button = playAction, color = buttonColor)
+
+ val toolbarColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
+ toolbar.setBackgroundColor(toolbarColor)
+ setSupportActionBar(toolbar)
+ supportActionBar?.title = null
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ return handleSortOrderMenuItem(item)
+ }
+
+ private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
+ val songs = artist.songs
+ when (item.itemId) {
+ android.R.id.home -> {
+ super.onBackPressed()
+ return true
+ }
+ R.id.action_play_next -> {
+ MusicPlayerRemote.playNext(songs)
+ return true
+ }
+ R.id.action_add_to_current_playing -> {
+ MusicPlayerRemote.enqueue(songs)
+ return true
+ }
+ R.id.action_add_to_playlist -> {
+ AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST")
+ return true
+ }
+ R.id.action_set_artist_image -> {
+ val intent = Intent(Intent.ACTION_GET_CONTENT)
+ intent.type = "image/*"
+ startActivityForResult(
+ Intent.createChooser(intent, getString(R.string.pick_from_local_storage)),
+ REQUEST_CODE_SELECT_IMAGE
+ )
+ return true
+ }
+ R.id.action_reset_artist_image -> {
+ Toast.makeText(
+ this@ArtistDetailActivity,
+ resources.getString(R.string.updating),
+ Toast.LENGTH_SHORT
+ )
+ .show()
+ CustomArtistImageUtil.getInstance(this@ArtistDetailActivity)
+ .resetCustomArtistImage(artist)
+ forceDownload = true
+ return true
+ }
+ }
+ return true
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.menu_artist_detail, menu)
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ override fun onMediaStoreChanged() {
+ super.onMediaStoreChanged()
+ reload()
+ }
+
+ private fun reload() {
+ if (intent.extras!!.containsKey(EXTRA_ARTIST_ID)) {
+ intent.extras?.getInt(EXTRA_ARTIST_ID)?.let { artistDetailsPresenter.loadArtist(it) }
+ } else {
+ finish()
+ }
+ }
+
+ override fun onBackPressed() {
+ if (cab != null && cab!!.isActive) {
+ cab?.finish()
+ } else {
+ super.onBackPressed()
+ }
+ }
+
+ companion object {
+
+ const val EXTRA_ARTIST_ID = "extra_artist_id"
+ const val REQUEST_CODE_SELECT_IMAGE = 9003
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/DriveModeActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/DriveModeActivity.kt
index af3890d22..633bf0fd9 100644
--- a/app/src/main/java/code/name/monkey/retromusic/activities/DriveModeActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/DriveModeActivity.kt
@@ -1,48 +1,45 @@
/*
- * Copyright (c) 2020 Hemanth Savarla.
+ * Copyright (c) 2020 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 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.Intent
+import android.animation.ObjectAnimator
import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Bundle
-import androidx.lifecycle.lifecycleScope
+import android.view.animation.LinearInterpolator
+import android.widget.SeekBar
+import code.name.monkey.appthemehelper.ThemeStore
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.RetroGlideExtension
-import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
+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.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
-import code.name.monkey.retromusic.model.Song
-import code.name.monkey.retromusic.repository.RealRepository
+import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import com.bumptech.glide.Glide
-import com.google.android.material.slider.Slider
+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.
@@ -50,24 +47,21 @@ import org.koin.android.ext.android.inject
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)
- binding = ActivityDriveModeBinding.inflate(layoutInflater)
- setContentView(binding.root)
+ setContentView(R.layout.activity_drive_mode)
setUpMusicControllers()
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
- lastPlaybackControlsColor = accentColor()
- binding.close.setOnClickListener {
- onBackPressedDispatcher.onBackPressed()
+ lastPlaybackControlsColor = ThemeStore.accentColor(this)
+ close.setOnClickListener {
+ onBackPressed()
}
- binding.repeatButton.drawAboveSystemBars()
}
private fun setUpMusicControllers() {
@@ -80,45 +74,36 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
}
private fun setupFavouriteToggle() {
- binding.songFavourite.setOnClickListener {
- toggleFavorite(MusicPlayerRemote.currentSong)
+ songFavourite.setOnClickListener {
+ MusicUtil.toggleFavorite(
+ 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)
+ private fun toggleFavourite() {
+ CoroutineScope(Dispatchers.IO).launch {
+ val isFavourite =
+ MusicUtil.isFavorite(this@DriveModeActivity, MusicPlayerRemote.currentSong)
withContext(Dispatchers.Main) {
- binding.songFavourite.setImageResource(if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border)
+ songFavourite.setImageResource(if (isFavourite) R.drawable.ic_favorite_white_24dp else R.drawable.ic_favorite_border_white_24dp)
}
}
}
private fun setUpProgressSlider() {
- binding.progressSlider.addOnChangeListener { _: Slider, progress: Float, fromUser: Boolean ->
- if (fromUser) {
- MusicPlayerRemote.seekTo(progress.toInt())
- onUpdateProgressViews(
- MusicPlayerRemote.songProgressMillis,
- MusicPlayerRemote.songDurationMillis
- )
+ progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
+ override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+ if (fromUser) {
+ MusicPlayerRemote.seekTo(progress)
+ onUpdateProgressViews(
+ MusicPlayerRemote.songProgressMillis,
+ MusicPlayerRemote.songDurationMillis
+ )
+ }
}
- }
+ })
}
override fun onPause() {
@@ -132,20 +117,21 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
}
private fun setUpPrevNext() {
- binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
- binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
+
+ nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
+ previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun setUpShuffleButton() {
- binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
+ shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
private fun setUpRepeatButton() {
- binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
+ repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
private fun setUpPlayPauseFab() {
- binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
+ playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
}
override fun onRepeatModeChanged() {
@@ -169,25 +155,24 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
updateSong()
updateRepeatState()
updateShuffleState()
- updateFavorite()
+ toggleFavourite()
}
private fun updatePlayPauseDrawableState() {
if (MusicPlayerRemote.isPlaying) {
- binding.playPauseButton.setImageResource(R.drawable.ic_pause)
+ playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
} else {
- binding.playPauseButton.setImageResource(R.drawable.ic_play_arrow)
+ playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp)
}
}
fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
- MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
+ MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
-
- else -> binding.shuffleButton.setColorFilter(
+ else -> shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
@@ -197,27 +182,19 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
private fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
- binding.repeatButton.setImageResource(R.drawable.ic_repeat)
- binding.repeatButton.setColorFilter(
+ repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
+ repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
-
MusicService.REPEAT_MODE_ALL -> {
- binding.repeatButton.setImageResource(R.drawable.ic_repeat)
- binding.repeatButton.setColorFilter(
- lastPlaybackControlsColor,
- PorterDuff.Mode.SRC_IN
- )
+ repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
+ repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
-
MusicService.REPEAT_MODE_THIS -> {
- binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
- binding.repeatButton.setColorFilter(
- lastPlaybackControlsColor,
- PorterDuff.Mode.SRC_IN
- )
+ repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
+ repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
}
}
@@ -225,34 +202,35 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged()
updateSong()
- updateFavorite()
- }
-
- override fun onFavoriteStateChanged() {
- super.onFavoriteStateChanged()
- updateFavorite()
+ toggleFavourite()
}
private fun updateSong() {
val song = MusicPlayerRemote.currentSong
- binding.songTitle.text = song.title
- binding.songText.text = song.artistName
+ songTitle.text = song.title
+ songText.text = song.artistName
- Glide.with(this)
- .load(RetroGlideExtension.getSongModel(song))
- .songCoverOptions(song)
+ SongGlideRequest.Builder.from(Glide.with(this), song)
+ .checkIgnoreMediaStore(this)
+ .generatePalette(this)
+ .build()
.transform(BlurTransformation.Builder(this).build())
- .into(binding.image)
+ .into(object : RetroMusicColoredTarget(image) {
+ override fun onColorReady(color: Int) {
+ }
+ })
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
- binding.progressSlider.run {
- valueTo = total.toFloat()
- value = progress.toFloat().coerceIn(valueFrom, valueTo)
- }
+ progressSlider.max = total
- binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
- binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
+ val animator = ObjectAnimator.ofInt(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())
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/ErrorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/ErrorActivity.kt
deleted file mode 100644
index 6823b80f8..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/activities/ErrorActivity.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-package code.name.monkey.retromusic.activities
-
-import android.os.Bundle
-import android.widget.Button
-import android.widget.ImageView
-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 com.google.android.material.dialog.MaterialAlertDialogBuilder
-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(cat.ereza.customactivityoncrash.R.layout.customactivityoncrash_default_error_activity)
-
- val restartButton =
- findViewById(cat.ereza.customactivityoncrash.R.id.customactivityoncrash_error_activity_restart_button)
-
- val config = CustomActivityOnCrash.getConfigFromIntent(intent)
- if (config == null) {
- finish()
- return
- }
- restartButton.setText(cat.ereza.customactivityoncrash.R.string.customactivityoncrash_error_activity_restart_app)
- restartButton.setOnClickListener {
- CustomActivityOnCrash.restartApplication(
- this@ErrorActivity,
- config
- )
- }
- val moreInfoButton =
- findViewById(cat.ereza.customactivityoncrash.R.id.customactivityoncrash_error_activity_more_info_button)
-
- moreInfoButton.setOnClickListener { //We retrieve all the error data and show it
- MaterialAlertDialogBuilder(this@ErrorActivity)
- .setTitle(cat.ereza.customactivityoncrash.R.string.customactivityoncrash_error_activity_error_details_title)
- .setMessage(
- CustomActivityOnCrash.getAllErrorDetailsFromIntent(
- this@ErrorActivity,
- intent
- )
- )
- .setPositiveButton(
- cat.ereza.customactivityoncrash.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, "text/*")
- }
- .show()
- }
- val errorActivityDrawableId = config.errorDrawable
- val errorImageView =
- findViewById(cat.ereza.customactivityoncrash.R.id.customactivityoncrash_error_activity_image)
- if (errorActivityDrawableId != null) {
- errorImageView.setImageResource(
- errorActivityDrawableId
- )
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/GenreDetailsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/GenreDetailsActivity.kt
new file mode 100644
index 000000000..3f57ac584
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/GenreDetailsActivity.kt
@@ -0,0 +1,163 @@
+package code.name.monkey.retromusic.activities
+
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import androidx.recyclerview.widget.DefaultItemAnimator
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.retromusic.App
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
+import code.name.monkey.retromusic.adapter.song.ShuffleButtonSongAdapter
+import code.name.monkey.retromusic.helper.menu.GenreMenuHelper
+import code.name.monkey.retromusic.interfaces.CabHolder
+import code.name.monkey.retromusic.model.Genre
+import code.name.monkey.retromusic.model.Song
+import code.name.monkey.retromusic.mvp.presenter.GenreDetailsPresenter
+import code.name.monkey.retromusic.mvp.presenter.GenreDetailsView
+import code.name.monkey.retromusic.util.DensityUtil
+import code.name.monkey.retromusic.util.RetroColorUtil
+import com.afollestad.materialcab.MaterialCab
+import kotlinx.android.synthetic.main.activity_playlist_detail.*
+import java.util.*
+import javax.inject.Inject
+
+/**
+ * @author Hemanth S (h4h13).
+ */
+
+class GenreDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder, GenreDetailsView {
+
+ @Inject
+ lateinit var genreDetailsPresenter: GenreDetailsPresenter
+
+ private lateinit var genre: Genre
+ private lateinit var songAdapter: ShuffleButtonSongAdapter
+ private var cab: MaterialCab? = null
+
+ private fun getEmojiByUnicode(unicode: Int): String {
+ return String(Character.toChars(unicode))
+ }
+
+ private fun checkIsEmpty() {
+ checkForPadding()
+ emptyEmoji.text = getEmojiByUnicode(0x1F631)
+ empty?.visibility = if (songAdapter.itemCount == 0) View.VISIBLE else View.GONE
+ }
+
+ private fun checkForPadding() {
+ val height = DensityUtil.dip2px(this, 52f)
+ recyclerView.setPadding(0, 0, 0, (height))
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setDrawUnderStatusBar()
+ super.onCreate(savedInstanceState)
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setTaskDescriptionColorAuto()
+ setLightNavigationBar(true)
+ toggleBottomNavigationView(true)
+
+ if (intent.extras != null) {
+ genre = intent?.extras?.getParcelable(EXTRA_GENRE_ID)!!
+ } else {
+ finish()
+ }
+
+ setUpToolBar()
+ setupRecyclerView()
+
+ App.musicComponent.inject(this)
+ genreDetailsPresenter.attachView(this)
+ }
+
+ private fun setUpToolBar() {
+ toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
+ setSupportActionBar(toolbar)
+ title = genre.name
+ }
+
+ override fun onResume() {
+ super.onResume()
+ genreDetailsPresenter.loadGenreSongs(genre.id)
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ genreDetailsPresenter.detachView()
+ }
+
+ override fun createContentView(): View {
+ return wrapSlidingMusicPanel(R.layout.activity_playlist_detail)
+ }
+
+ override fun showEmptyView() {
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.menu_genre_detail, menu)
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home) {
+ onBackPressed()
+ }
+ return GenreMenuHelper.handleMenuClick(this, genre, item)
+ }
+
+ private fun setupRecyclerView() {
+ songAdapter = ShuffleButtonSongAdapter(this, ArrayList(), R.layout.item_list, this)
+ recyclerView.apply {
+ itemAnimator = DefaultItemAnimator()
+ layoutManager = LinearLayoutManager(this@GenreDetailsActivity)
+ adapter = songAdapter
+ }
+ songAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
+ override fun onChanged() {
+ super.onChanged()
+ checkIsEmpty()
+ }
+ })
+ }
+
+ override fun songs(songs: List) {
+ songAdapter.swapDataSet(songs)
+ }
+
+ override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
+ if (cab != null && cab!!.isActive) cab?.finish()
+ cab = MaterialCab(this, R.id.cab_stub).setMenu(menuRes)
+ .setCloseDrawableRes(R.drawable.ic_close_white_24dp)
+ .setBackgroundColor(
+ RetroColorUtil.shiftBackgroundColorForLightText(
+ ATHUtil.resolveColor(
+ this,
+ R.attr.colorSurface
+ )
+ )
+ ).start(callback)
+ return cab!!
+ }
+
+ override fun onBackPressed() {
+ if (cab != null && cab!!.isActive) cab!!.finish()
+ else {
+ recyclerView!!.stopScroll()
+ super.onBackPressed()
+ }
+ }
+
+ override fun onMediaStoreChanged() {
+ super.onMediaStoreChanged()
+ genreDetailsPresenter.loadGenreSongs(genre.id)
+ }
+
+ companion object {
+ const val EXTRA_GENRE_ID = "extra_genre_id"
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.java b/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.java
new file mode 100644
index 000000000..eea9299cc
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.java
@@ -0,0 +1,99 @@
+/*
+ * 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 org.jetbrains.annotations.Nullable;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+
+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;
+
+/**
+ * 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("Unable to load " + e.getLocalizedMessage() + "
", "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
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.kt
deleted file mode 100644
index e41352dae..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.kt
+++ /dev/null
@@ -1,94 +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 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(
- "Unable to load " + e.localizedMessage + "
", "text/html", "UTF-8"
- )
- }
- binding.license.drawAboveSystemBars()
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- if (item.itemId == android.R.id.home) {
- onBackPressedDispatcher.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
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/LockScreenActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/LockScreenActivity.kt
index 3ac219871..e73109cc1 100644
--- a/app/src/main/java/code/name/monkey/retromusic/activities/LockScreenActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/LockScreenActivity.kt
@@ -1,35 +1,17 @@
-/*
- * 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.KeyguardManager
+import android.content.Context
+import android.os.Build
import android.os.Bundle
+import android.view.View
import android.view.WindowManager
-import androidx.core.content.getSystemService
-import code.name.monkey.appthemehelper.util.VersionUtils
+import androidx.core.view.ViewCompat
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.RetroGlideExtension
-import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
-import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
+import code.name.monkey.retromusic.fragments.player.lockscreen.LockScreenPlayerControlsFragment
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
@@ -37,18 +19,30 @@ 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
+ private var fragment: LockScreenPlayerControlsFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- lockScreenInit()
- binding = ActivityLockScreenBinding.inflate(layoutInflater)
- setContentView(binding.root)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
+ setShowWhenLocked(true)
+ setTurnScreenOn(true)
+ } else {
+ this.window.addFlags(
+ WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
+ WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
+ WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ )
+ }
+ setDrawUnderStatusBar()
+ setContentView(R.layout.activity_lock_screen)
hideStatusBar()
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
+ setLightNavigationBar(true)
val config = SlidrConfig.Builder().listener(object : SlidrListener {
override fun onSlideStateChanged(state: Int) {
@@ -61,10 +55,10 @@ class LockScreenActivity : AbsMusicServiceActivity() {
}
override fun onSlideClosed(): Boolean {
- if (VersionUtils.hasOreo()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val keyguardManager =
- getSystemService()
- keyguardManager?.requestDismissKeyguard(this@LockScreenActivity, null)
+ getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
+ keyguardManager.requestDismissKeyguard(this@LockScreenActivity, null)
}
finish()
return true
@@ -73,25 +67,13 @@ class LockScreenActivity : AbsMusicServiceActivity() {
Slidr.attach(this, config)
- fragment = whichFragment(R.id.playback_controls_fragment)
+ fragment =
+ supportFragmentManager.findFragmentById(R.id.playback_controls_fragment) as LockScreenPlayerControlsFragment?
- binding.slide.apply {
+ findViewById(R.id.slide).apply {
translationY = 100f
alpha = 0f
- animate().translationY(0f).alpha(1f).setDuration(1500).start()
- }
- }
-
- @Suppress("Deprecation")
- private fun lockScreenInit() {
- if (VersionUtils.hasOreoMR1()) {
- setShowWhenLocked(true)
- //setTurnScreenOn(true)
- } else {
- window.addFlags(
- WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- // or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
- )
+ ViewCompat.animate(this).translationY(0f).alpha(1f).setDuration(1500).start()
}
}
@@ -107,15 +89,12 @@ class LockScreenActivity : AbsMusicServiceActivity() {
private fun updateSongs() {
val song = MusicPlayerRemote.currentSong
- Glide.with(this)
- .asBitmapPalette()
- .songCoverOptions(song)
- .load(RetroGlideExtension.getSongModel(song))
- .dontAnimate()
- .into(object : RetroMusicColoredTarget(binding.image) {
+ SongGlideRequest.Builder.from(Glide.with(this), song).checkIgnoreMediaStore(this)
+ .generatePalette(this).build().dontAnimate()
+ .into(object : RetroMusicColoredTarget(image) {
override fun onColorReady(colors: MediaNotificationProcessor) {
- fragment?.setColor(colors)
+ fragment?.setDark(colors.backgroundColor)
}
})
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/LyricsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/LyricsActivity.kt
new file mode 100644
index 000000000..b3a690feb
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/LyricsActivity.kt
@@ -0,0 +1,428 @@
+package code.name.monkey.retromusic.activities
+
+import android.R.attr
+import android.annotation.SuppressLint
+import android.content.res.ColorStateList
+import android.os.AsyncTask
+import android.os.Build
+import android.os.Bundle
+import android.text.InputType
+import android.text.TextUtils
+import android.view.*
+import androidx.annotation.StringRes
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.FragmentStatePagerAdapter
+import androidx.viewpager.widget.ViewPager
+import code.name.monkey.appthemehelper.ThemeStore
+import code.name.monkey.appthemehelper.util.*
+import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
+import code.name.monkey.retromusic.App
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
+import code.name.monkey.retromusic.activities.tageditor.WriteTagsAsyncTask
+import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
+import code.name.monkey.retromusic.helper.MusicPlayerRemote
+import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
+import code.name.monkey.retromusic.lyrics.LrcHelper
+import code.name.monkey.retromusic.lyrics.LrcView
+import code.name.monkey.retromusic.model.Song
+import code.name.monkey.retromusic.model.lyrics.Lyrics
+import code.name.monkey.retromusic.util.LyricUtil
+import code.name.monkey.retromusic.util.MusicUtil
+import code.name.monkey.retromusic.util.PreferenceUtil
+import code.name.monkey.retromusic.util.RetroUtil
+import com.afollestad.materialdialogs.LayoutMode
+import com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.bottomsheets.BottomSheet
+import com.afollestad.materialdialogs.input.getInputLayout
+import com.afollestad.materialdialogs.input.input
+import kotlinx.android.synthetic.main.activity_lyrics.*
+import kotlinx.android.synthetic.main.fragment_lyrics.*
+import kotlinx.android.synthetic.main.fragment_synced.*
+import org.jaudiotagger.tag.FieldKey
+import java.io.File
+import java.util.*
+import kotlin.collections.ArrayList
+import kotlin.collections.set
+
+class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener,
+ ViewPager.OnPageChangeListener {
+ override fun onPageScrollStateChanged(state: Int) {
+ when (state) {
+ ViewPager.SCROLL_STATE_IDLE -> fab.show()
+ ViewPager.SCROLL_STATE_DRAGGING,
+ ViewPager.SCROLL_STATE_SETTLING -> fab.hide()
+ }
+ }
+
+ override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
+ }
+
+ override fun onPageSelected(position: Int) {
+ PreferenceUtil.getInstance(this).lyricsOptions = position
+ if (position == 0) fab.text = getString(R.string.synced_lyrics)
+ else if (position == 1) fab.text = getString(R.string.lyrics)
+ }
+
+ override fun onClick(v: View?) {
+ when (viewPager.currentItem) {
+ 0 -> showSyncedLyrics()
+ 1 -> showLyricsSaveDialog()
+ }
+ }
+
+ private lateinit var song: Song
+ private var lyricsString: String? = null
+
+ 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
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_lyrics)
+ setStatusbarColorAuto()
+ setTaskDescriptionColorAuto()
+ setNavigationbarColorAuto()
+
+ fab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
+ ColorStateList.valueOf(
+ MaterialValueHelper.getPrimaryTextColor(
+ this,
+ ColorUtil.isColorLight(ThemeStore.accentColor(this))
+ )
+ )
+ .apply {
+ fab.setTextColor(this)
+ fab.iconTint = this
+ }
+ setupWakelock()
+
+ viewPager.apply {
+ adapter = PagerAdapter(supportFragmentManager)
+ currentItem = PreferenceUtil.getInstance(this@LyricsActivity).lyricsOptions
+ addOnPageChangeListener(this@LyricsActivity)
+ }
+
+ val toolbarColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
+ toolbar.setBackgroundColor(toolbarColor)
+ tabs.setBackgroundColor(toolbarColor)
+ ToolbarContentTintHelper.colorBackButton(toolbar)
+ setSupportActionBar(toolbar)
+ tabs.setupWithViewPager(viewPager)
+ tabs.setSelectedTabIndicator(
+ TintHelper.createTintedDrawable(
+ ContextCompat.getDrawable(
+ this,
+ R.drawable.tab_indicator
+ ), ThemeStore.accentColor(this)
+ )
+ )
+ tabs.setTabTextColors(
+ ATHUtil.resolveColor(this, android.R.attr.textColorSecondary),
+ ThemeStore.accentColor(this)
+ )
+ tabs.setSelectedTabIndicatorColor(ThemeStore.accentColor(this))
+
+ fab.setOnClickListener(this)
+ }
+
+ override fun onPlayingMetaChanged() {
+ super.onPlayingMetaChanged()
+ updateTitleSong()
+ }
+
+ override fun onServiceConnected() {
+ super.onServiceConnected()
+ updateTitleSong()
+ }
+
+ 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 onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home) {
+ finish()
+ return true
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ private fun showSyncedLyrics() {
+ var content = ""
+ try {
+ content = LyricUtil.getStringFromFile(song.title, song.artistName)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ val materialDialog = MaterialDialog(this)
+ .show {
+ cornerRadius(PreferenceUtil.getInstance(this@LyricsActivity).dialogCorner)
+ title(R.string.add_time_framed_lryics)
+ negativeButton(R.string.action_search) {
+ RetroUtil.openUrl(this@LyricsActivity, googleSearchLrcUrl)
+ }
+ input(
+ hint = getString(R.string.paste_lyrics_here),
+ prefill = content,
+ inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE
+ ) { _, input ->
+ LyricUtil.writeLrcToLoc(song.title, song.artistName, input.toString())
+ }
+ positiveButton(android.R.string.ok) {
+ updateSong()
+ }
+ }
+
+ MaterialUtil.setTint(materialDialog.getInputLayout(), false)
+ }
+
+ private fun updateSong() {
+ val page =
+ supportFragmentManager.findFragmentByTag("android:switcher:" + R.id.viewPager + ":" + viewPager.currentItem)
+ if (viewPager.currentItem == 0 && page != null) {
+ (page as BaseLyricsFragment).upDateSong()
+ }
+ }
+
+ private fun showLyricsSaveDialog() {
+ val content: String = if (lyricsString == null) {
+ ""
+ } else {
+ lyricsString!!
+ }
+
+ val materialDialog = MaterialDialog(
+ this
+ ).show {
+ cornerRadius(PreferenceUtil.getInstance(this@LyricsActivity).dialogCorner)
+ title(R.string.add_lyrics)
+ negativeButton(R.string.action_search) {
+ RetroUtil.openUrl(this@LyricsActivity, getGoogleSearchUrl())
+ }
+ input(
+ hint = getString(R.string.paste_lyrics_here),
+ prefill = content,
+ inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE
+ ) { _, input ->
+ val fieldKeyValueMap = EnumMap(FieldKey::class.java)
+ fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
+ WriteTagsAsyncTask(this@LyricsActivity).execute(
+ WriteTagsAsyncTask.LoadingInfo(
+ getSongPaths(song), fieldKeyValueMap, null
+ )
+ )
+ }
+ positiveButton(android.R.string.ok) {
+ updateSong()
+ }
+ }
+ MaterialUtil.setTint(materialDialog.getInputLayout(), false)
+ }
+
+ private fun getSongPaths(song: Song): ArrayList {
+ val paths = ArrayList(1)
+ paths.add(song.data)
+ return paths
+ }
+
+ private fun getGoogleSearchUrl(): String {
+ var baseUrl = "http://www.google.com/search?"
+ var query = song.title + "+" + song.artistName
+ query = "q=" + query.replace(" ", "+") + " lyrics"
+ baseUrl += query
+ return baseUrl
+ }
+
+ class PagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(
+ fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
+ ) {
+
+ class Tabs(
+ @StringRes val title: Int, val fragment: Fragment
+ )
+
+ private var tabs = ArrayList()
+
+ init {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ tabs.add(Tabs(R.string.synced_lyrics, SyncedLyricsFragment()))
+ }
+ tabs.add(Tabs(R.string.normal_lyrics, OfflineLyricsFragment()))
+ }
+
+ override fun getItem(position: Int): Fragment {
+ return tabs[position].fragment
+ }
+
+ override fun getPageTitle(position: Int): CharSequence? {
+ return App.getContext().getString(tabs[position].title)
+ }
+
+ override fun getCount(): Int {
+ return tabs.size
+ }
+ }
+
+ abstract class BaseLyricsFragment : AbsMusicServiceFragment() {
+ abstract fun upDateSong()
+
+ override fun onPlayingMetaChanged() {
+ super.onPlayingMetaChanged()
+ upDateSong()
+ }
+
+ override fun onServiceConnected() {
+ super.onServiceConnected()
+ upDateSong()
+ }
+ }
+
+ class OfflineLyricsFragment : BaseLyricsFragment() {
+ override fun upDateSong() {
+ loadSongLyrics()
+ }
+
+ private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null
+ private var lyrics: Lyrics? = null
+
+ @SuppressLint("StaticFieldLeak")
+ private fun loadSongLyrics() {
+ if (updateLyricsAsyncTask != null) {
+ updateLyricsAsyncTask!!.cancel(false)
+ }
+ val song = MusicPlayerRemote.currentSong
+ updateLyricsAsyncTask = object : AsyncTask() {
+ override fun doInBackground(vararg params: Void?): Lyrics? {
+ val data = MusicUtil.getLyrics(song)
+ return if (TextUtils.isEmpty(data)) {
+ null
+ } else Lyrics.parse(song, data!!)
+ }
+
+ override fun onPreExecute() {
+ super.onPreExecute()
+ lyrics = null
+ }
+
+ override fun onPostExecute(l: Lyrics?) {
+ lyrics = l
+ offlineLyrics?.visibility = View.VISIBLE
+ if (l == null) {
+ offlineLyrics?.setText(R.string.no_lyrics_found)
+ return
+ }
+ (activity as LyricsActivity).lyricsString = l.text
+ offlineLyrics?.text = l.text
+ }
+
+ override fun onCancelled(s: Lyrics?) {
+ onPostExecute(null)
+ }
+ }.execute()
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+ loadSongLyrics()
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask!!.isCancelled) {
+ updateLyricsAsyncTask?.cancel(true)
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.fragment_lyrics, container, false)
+ }
+ }
+
+ class SyncedLyricsFragment : BaseLyricsFragment(), MusicProgressViewUpdateHelper.Callback {
+ override fun upDateSong() {
+ loadLRCLyrics()
+ }
+
+ private lateinit var updateHelper: MusicProgressViewUpdateHelper
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.fragment_synced, container, false)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ updateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ setupLyricsView()
+ }
+
+ private fun setupLyricsView() {
+ lyricsView.apply {
+ setCurrentPlayLineColor(ThemeStore.accentColor(requireContext()))
+ setIndicatorTextColor(ThemeStore.accentColor(requireContext()))
+ setCurrentIndicateLineTextColor(
+ resolveColor(
+ requireContext(),
+ attr.textColorPrimary
+ )
+ )
+ setNoLrcTextColor(resolveColor(requireContext(), attr.textColorPrimary))
+ setOnPlayIndicatorLineListener(object : LrcView.OnPlayIndicatorLineListener {
+ override fun onPlay(time: Long, content: String) {
+ MusicPlayerRemote.seekTo(time.toInt())
+ }
+ })
+ }
+ }
+
+ 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.resetView("Empty")
+ val song = MusicPlayerRemote.currentSong
+ if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
+ showLyricsLocal(LyricUtil.getLocalLyricFile(song.title, song.artistName))
+ }
+ }
+
+ private fun showLyricsLocal(file: File?) {
+ if (file != null) {
+ lyricsView.setLrcData(LrcHelper.parseLrcFromFile(file))
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.java b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.java
new file mode 100644
index 000000000..443b3ecf1
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.java
@@ -0,0 +1,847 @@
+/*
+ * Copyright (c) 2020 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.app.ActivityOptions;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.app.ActivityCompat;
+import androidx.fragment.app.Fragment;
+import androidx.transition.TransitionManager;
+
+import com.afollestad.materialcab.MaterialCab;
+import com.afollestad.materialcab.MaterialCab.Callback;
+import com.google.android.material.appbar.AppBarLayout;
+import com.google.android.material.card.MaterialCardView;
+import com.google.android.material.snackbar.Snackbar;
+import com.google.android.material.textview.MaterialTextView;
+import com.google.android.play.core.appupdate.AppUpdateInfo;
+import com.google.android.play.core.appupdate.AppUpdateManager;
+import com.google.android.play.core.appupdate.AppUpdateManagerFactory;
+import com.google.android.play.core.install.InstallState;
+import com.google.android.play.core.install.InstallStateUpdatedListener;
+import com.google.android.play.core.install.model.AppUpdateType;
+import com.google.android.play.core.install.model.InstallStatus;
+import com.google.android.play.core.install.model.UpdateAvailability;
+import com.google.android.play.core.tasks.Task;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import code.name.monkey.appthemehelper.ThemeStore;
+import code.name.monkey.appthemehelper.util.ATHUtil;
+import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
+import code.name.monkey.retromusic.R;
+import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity;
+import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog;
+import code.name.monkey.retromusic.fragments.albums.AlbumsFragment;
+import code.name.monkey.retromusic.fragments.artists.ArtistsFragment;
+import code.name.monkey.retromusic.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment;
+import code.name.monkey.retromusic.fragments.genres.GenresFragment;
+import code.name.monkey.retromusic.fragments.home.BannerHomeFragment;
+import code.name.monkey.retromusic.fragments.mainactivity.FoldersFragment;
+import code.name.monkey.retromusic.fragments.playlists.PlaylistsFragment;
+import code.name.monkey.retromusic.fragments.queue.PlayingQueueFragment;
+import code.name.monkey.retromusic.fragments.songs.SongsFragment;
+import code.name.monkey.retromusic.helper.MusicPlayerRemote;
+import code.name.monkey.retromusic.helper.SearchQueryHelper;
+import code.name.monkey.retromusic.helper.SortOrder.AlbumSortOrder;
+import code.name.monkey.retromusic.helper.SortOrder.ArtistSortOrder;
+import code.name.monkey.retromusic.helper.SortOrder.SongSortOrder;
+import code.name.monkey.retromusic.interfaces.CabHolder;
+import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks;
+import code.name.monkey.retromusic.loaders.AlbumLoader;
+import code.name.monkey.retromusic.loaders.ArtistLoader;
+import code.name.monkey.retromusic.loaders.PlaylistSongsLoader;
+import code.name.monkey.retromusic.model.Song;
+import code.name.monkey.retromusic.service.MusicService;
+import code.name.monkey.retromusic.util.AppRater;
+import code.name.monkey.retromusic.util.NavigationUtil;
+import code.name.monkey.retromusic.util.PreferenceUtil;
+import code.name.monkey.retromusic.util.RetroColorUtil;
+import code.name.monkey.retromusic.util.RetroUtil;
+
+/**
+ * Created by hemanths on 2020-02-19.
+ */
+public class MainActivity extends AbsSlidingMusicPanelActivity
+ implements CabHolder, SharedPreferences.OnSharedPreferenceChangeListener {
+
+ public static final String TAG = MainActivity.class.getSimpleName();
+ public static final int APP_INTRO_REQUEST = 100;
+ public static final String EXPAND_PANEL = "expand_panel";
+ private static final int APP_UPDATE_REQUEST_CODE = 9002;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ String action = intent.getAction();
+ if (action != null && action.equals(Intent.ACTION_SCREEN_OFF)) {
+ if (PreferenceUtil.getInstance(context).getLockScreen() && MusicPlayerRemote.isPlaying()) {
+ final Intent activity = new Intent(context, LockScreenActivity.class);
+ activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ activity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+ ActivityCompat.startActivity(context, activity, null);
+ }
+ }
+ }
+ };
+ private final IntentFilter mIntentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
+ private MainActivityFragmentCallbacks currentFragment;
+ private boolean blockRequestPermissions = false;
+ private MaterialCab cab;
+ private AppBarLayout mAppBarLayout;
+ private MaterialTextView mAppTitle;
+ private Toolbar mToolbar;
+ private MaterialCardView mToolbarContainer;
+ private AppUpdateManager appUpdateManager;
+ InstallStateUpdatedListener listener = new InstallStateUpdatedListener() {
+ @Override
+ public void onStateUpdate(InstallState state) {
+ if (state.installStatus() == InstallStatus.DOWNLOADED) {
+ popupSnackBarForCompleteUpdate();
+ } else if (state.installStatus() == InstallStatus.INSTALLED) {
+ appUpdateManager.unregisterListener(listener);
+ } else {
+ Log.i(TAG, "InstallStateUpdatedListener: state: " + state.installStatus());
+ }
+ }
+ };
+
+ private void popupSnackBarForCompleteUpdate() {
+ Snackbar snackbar = Snackbar.make(findViewById(R.id.mainContent), "New app is ready!", Snackbar.LENGTH_INDEFINITE);
+ snackbar.setAction("Install", view -> {
+ if (appUpdateManager != null) {
+ appUpdateManager.completeUpdate();
+ }
+ });
+ snackbar.setActionTextColor(ThemeStore.Companion.accentColor(this));
+ snackbar.show();
+ }
+
+ @Override
+ protected void onCreate(@Nullable final Bundle savedInstanceState) {
+ setDrawUnderStatusBar();
+ super.onCreate(savedInstanceState);
+ setStatusbarColorAuto();
+ setNavigationbarColorAuto();
+ setLightNavigationBar(true);
+ setTaskDescriptionColorAuto();
+ hideStatusBar();
+ setBottomBarVisibility(View.VISIBLE);
+
+ getBottomNavigationView().setSelectedItemId(PreferenceUtil.getInstance(this).getLastPage());
+ getBottomNavigationView().setOnNavigationItemSelectedListener(item -> {
+ PreferenceUtil.getInstance(MainActivity.this).setLastPage(item.getItemId());
+ selectedFragment(item.getItemId());
+ return true;
+ });
+
+ if (savedInstanceState == null) {
+ selectedFragment(PreferenceUtil.getInstance(this).getLastPage());
+ } else {
+ restoreCurrentFragment();
+ }
+
+ mToolbarContainer = findViewById(R.id.toolbarContainer);
+ mAppTitle = findViewById(R.id.appTitle);
+ mToolbar = findViewById(R.id.toolbar);
+ mAppBarLayout = findViewById(R.id.appBarLayout);
+
+ //checkShowChangelog();
+ AppRater.appLaunched(this);
+ setupToolbar();
+ checkUpdate();
+ }
+
+ private void checkUpdate() {
+ appUpdateManager = AppUpdateManagerFactory.create(this);
+ appUpdateManager.registerListener(listener);
+
+ Task appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();
+ appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
+ if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
+ && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
+ try {
+ appUpdateManager.startUpdateFlowForResult(
+ appUpdateInfo,
+ AppUpdateType.IMMEDIATE,
+ this,
+ APP_UPDATE_REQUEST_CODE);
+ } catch (IntentSender.SendIntentException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == APP_INTRO_REQUEST) {
+ blockRequestPermissions = false;
+ if (!hasPermissions()) {
+ requestPermissions();
+ }
+ } else if (requestCode == APP_UPDATE_REQUEST_CODE) {
+ if (resultCode != RESULT_OK) {
+
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ registerReceiver(mBroadcastReceiver, mIntentFilter);
+ PreferenceUtil.getInstance(this).registerOnSharedPreferenceChangedListener(this);
+
+ if (getIntent().hasExtra(EXPAND_PANEL) && getIntent().getBooleanExtra(EXPAND_PANEL, false) && PreferenceUtil.getInstance(this).isExpandPanel()) {
+ expandPanel();
+ getIntent().putExtra(EXPAND_PANEL, false);
+ }
+ appUpdateManager.getAppUpdateInfo()
+ .addOnSuccessListener(appUpdateInfo -> {
+ if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
+ popupSnackBarForCompleteUpdate();
+ }
+ try {
+ if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
+ appUpdateManager.startUpdateFlowForResult(
+ appUpdateInfo,
+ AppUpdateType.IMMEDIATE,
+ this,
+ APP_UPDATE_REQUEST_CODE);
+ }
+ } catch (IntentSender.SendIntentException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(mBroadcastReceiver);
+ PreferenceUtil.getInstance(this).unregisterOnSharedPreferenceChangedListener(this);
+ }
+
+ public void addOnAppBarOffsetChangedListener(
+ @NonNull AppBarLayout.OnOffsetChangedListener onOffsetChangedListener) {
+ mAppBarLayout.addOnOffsetChangedListener(onOffsetChangedListener);
+ }
+
+ public int getTotalAppBarScrollingRange() {
+ return mAppBarLayout.getTotalScrollRange();
+ }
+
+ @Override
+ public boolean handleBackPress() {
+ if (cab != null && cab.isActive()) {
+ cab.finish();
+ return true;
+ }
+ return super.handleBackPress() || (currentFragment != null && currentFragment.handleBackPress());
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ if (isPlaylistPage()) {
+ menu.add(0, R.id.action_new_playlist, 0, R.string.new_playlist_title)
+ .setIcon(R.drawable.ic_playlist_add_white_24dp)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ }
+ if (isHomePage()) {
+ menu.add(0, R.id.action_search, 0, getString(R.string.action_search))
+ .setIcon(R.drawable.ic_mic_white_24dp)
+ .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+
+ }
+ if (isFolderPage()) {
+ menu.add(0, R.id.action_scan, 0, R.string.scan_media)
+ .setIcon(R.drawable.ic_scanner_white_24dp)
+ .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ menu.add(0, R.id.action_go_to_start_directory, 0, R.string.action_go_to_start_directory)
+ .setIcon(R.drawable.ic_bookmark_music_white_24dp)
+ .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ }
+ Fragment fragment = getCurrentFragment();
+ if (fragment instanceof AbsLibraryPagerRecyclerViewCustomGridSizeFragment) {
+ AbsLibraryPagerRecyclerViewCustomGridSizeFragment currentFragment
+ = (AbsLibraryPagerRecyclerViewCustomGridSizeFragment) fragment;
+
+ MenuItem gridSizeItem = menu.findItem(R.id.action_grid_size);
+ if (RetroUtil.isLandscape()) {
+ gridSizeItem.setTitle(R.string.action_grid_size_land);
+ }
+ setUpGridSizeMenu(currentFragment, gridSizeItem.getSubMenu());
+ MenuItem layoutItem = menu.findItem(R.id.action_layout_type);
+ setupLayoutMenu(currentFragment, layoutItem.getSubMenu());
+ setUpSortOrderMenu(currentFragment, menu.findItem(R.id.action_sort_order).getSubMenu());
+ } else {
+ menu.removeItem(R.id.action_layout_type);
+ menu.removeItem(R.id.action_grid_size);
+ menu.removeItem(R.id.action_sort_order);
+ }
+ menu.add(0, R.id.action_settings, 0, getString(R.string.action_settings))
+ .setIcon(R.drawable.ic_settings_white_24dp)
+ .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
+ Fragment fragment = getCurrentFragment();
+ if (fragment instanceof AbsLibraryPagerRecyclerViewCustomGridSizeFragment) {
+ AbsLibraryPagerRecyclerViewCustomGridSizeFragment currentFragment
+ = (AbsLibraryPagerRecyclerViewCustomGridSizeFragment) fragment;
+ if (handleGridSizeMenuItem(currentFragment, item)) {
+ return true;
+ }
+ if (handleLayoutResType(currentFragment, item)) {
+ return true;
+ }
+ if (handleSortOrderMenuItem(currentFragment, item)) {
+ return true;
+ }
+ }
+ int id = item.getItemId();
+ switch (id) {
+ case R.id.action_search:
+ ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, mToolbarContainer,
+ getString(R.string.transition_toolbar));
+ NavigationUtil.goToSearch(this, true, options);
+ break;
+ case R.id.action_new_playlist:
+ CreatePlaylistDialog.create().show(getSupportFragmentManager(), "CREATE_PLAYLIST");
+ return true;
+ case R.id.action_settings:
+ NavigationUtil.goToSettings(this);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(final Menu menu) {
+ ToolbarContentTintHelper.handleOnPrepareOptionsMenu(this, mToolbar);
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public void onServiceConnected() {
+ super.onServiceConnected();
+ handlePlaybackIntent(getIntent());
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(final @NonNull SharedPreferences sharedPreferences,
+ final @NonNull String key) {
+ if (key.equals(PreferenceUtil.GENERAL_THEME) || key.equals(PreferenceUtil.BLACK_THEME) ||
+ key.equals(PreferenceUtil.ADAPTIVE_COLOR_APP) || key.equals(PreferenceUtil.DOMINANT_COLOR) ||
+ key.equals(PreferenceUtil.USER_NAME) || key.equals(PreferenceUtil.TOGGLE_FULL_SCREEN) ||
+ key.equals(PreferenceUtil.TOGGLE_VOLUME) || key.equals(PreferenceUtil.ROUND_CORNERS) ||
+ key.equals(PreferenceUtil.CAROUSEL_EFFECT) || key.equals(PreferenceUtil.NOW_PLAYING_SCREEN_ID) ||
+ key.equals(PreferenceUtil.TOGGLE_GENRE) || key.equals(PreferenceUtil.BANNER_IMAGE_PATH) ||
+ key.equals(PreferenceUtil.PROFILE_IMAGE_PATH) || key.equals(PreferenceUtil.CIRCULAR_ALBUM_ART) ||
+ key.equals(PreferenceUtil.KEEP_SCREEN_ON) || key.equals(PreferenceUtil.TOGGLE_SEPARATE_LINE) ||
+ key.equals(PreferenceUtil.TOGGLE_HOME_BANNER) || key.equals(PreferenceUtil.TOGGLE_ADD_CONTROLS) ||
+ key.equals(PreferenceUtil.ALBUM_COVER_STYLE) || key.equals(PreferenceUtil.HOME_ARTIST_GRID_STYLE) ||
+ key.equals(PreferenceUtil.ALBUM_COVER_TRANSFORM) || key.equals(PreferenceUtil.DESATURATED_COLOR) ||
+ key.equals(PreferenceUtil.TAB_TEXT_MODE) || key.equals(PreferenceUtil.LANGUAGE_NAME) ||
+ key.equals(PreferenceUtil.LIBRARY_CATEGORIES)
+ ) {
+ postRecreate();
+ }
+ }
+
+ @NotNull
+ @Override
+ public MaterialCab openCab(final int menuRes, @NotNull final Callback callback) {
+ if (cab != null && cab.isActive()) {
+ cab.finish();
+ }
+ cab = new MaterialCab(this, R.id.cab_stub)
+ .setMenu(menuRes)
+ .setCloseDrawableRes(R.drawable.ic_close_white_24dp)
+ .setBackgroundColor(
+ RetroColorUtil.shiftBackgroundColorForLightText(
+ ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface)))
+ .start(callback);
+ return cab;
+ }
+
+ public void removeOnAppBarOffsetChangedListener(
+ @NonNull AppBarLayout.OnOffsetChangedListener onOffsetChangedListener) {
+ mAppBarLayout.removeOnOffsetChangedListener(onOffsetChangedListener);
+ }
+
+ public void setCurrentFragment(@NonNull Fragment fragment, @NonNull String tag) {
+ String currentTag = null;
+ if (getSupportFragmentManager().findFragmentByTag(tag) != null) {
+ currentTag = getSupportFragmentManager().findFragmentByTag(tag).getTag();
+ }
+
+ if (!tag.equals(currentTag)) {
+ getSupportFragmentManager()
+ .beginTransaction()
+ .replace(R.id.fragment_container, fragment, tag)
+ .commit();
+ currentFragment = (MainActivityFragmentCallbacks) fragment;
+ }
+ }
+
+ @NotNull
+ @Override
+ protected View createContentView() {
+ return wrapSlidingMusicPanel(R.layout.activity_main_content);
+ }
+
+ @Override
+ protected void requestPermissions() {
+ if (!blockRequestPermissions) {
+ super.requestPermissions();
+ }
+ }
+
+ private void checkShowChangelog() {
+ try {
+ final PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
+ final int currentVersion = pInfo.versionCode;
+ if (currentVersion != PreferenceUtil.getInstance(this).getLastChangelogVersion()) {
+ startActivityForResult(new Intent(this, WhatsNewActivity.class), APP_INTRO_REQUEST);
+ }
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Nullable
+ private Fragment getCurrentFragment() {
+ return getSupportFragmentManager().findFragmentById(R.id.fragment_container);
+ }
+
+ private boolean handleGridSizeMenuItem(
+ @NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment,
+ @NonNull MenuItem item) {
+ int gridSize = 0;
+ switch (item.getItemId()) {
+ case R.id.action_grid_size_1:
+ gridSize = 1;
+ break;
+ case R.id.action_grid_size_2:
+ gridSize = 2;
+ break;
+ case R.id.action_grid_size_3:
+ gridSize = 3;
+ break;
+ case R.id.action_grid_size_4:
+ gridSize = 4;
+ break;
+ case R.id.action_grid_size_5:
+ gridSize = 5;
+ break;
+ case R.id.action_grid_size_6:
+ gridSize = 6;
+ break;
+ case R.id.action_grid_size_7:
+ gridSize = 7;
+ break;
+ case R.id.action_grid_size_8:
+ gridSize = 8;
+ break;
+ }
+
+ if (gridSize > 0) {
+ item.setChecked(true);
+ fragment.setAndSaveGridSize(gridSize);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean handleLayoutResType(
+ final AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment,
+ final MenuItem item) {
+ int layoutRes = -1;
+ switch (item.getItemId()) {
+ case R.id.action_layout_normal:
+ layoutRes = R.layout.item_grid;
+ break;
+ case R.id.action_layout_card:
+ layoutRes = R.layout.item_card;
+ break;
+ case R.id.action_layout_colored_card:
+ layoutRes = R.layout.item_card_color;
+ break;
+ case R.id.action_layout_circular:
+ layoutRes = R.layout.item_grid_circle;
+ break;
+ case R.id.action_layout_image:
+ layoutRes = R.layout.image;
+ break;
+ case R.id.action_layout_gradient_image:
+ layoutRes = R.layout.item_image_gradient;
+ break;
+ }
+ if (layoutRes != -1) {
+ item.setChecked(true);
+ fragment.setAndSaveLayoutRes(layoutRes);
+ return true;
+ }
+ return false;
+ }
+
+ private void handlePlaybackIntent(@Nullable Intent intent) {
+ if (intent == null) {
+ return;
+ }
+
+ Uri uri = intent.getData();
+ String mimeType = intent.getType();
+ boolean handled = false;
+
+ if (intent.getAction() != null && intent.getAction()
+ .equals(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH)) {
+ final List songs = SearchQueryHelper.getSongs(this, intent.getExtras());
+ if (MusicPlayerRemote.getShuffleMode() == MusicService.SHUFFLE_MODE_SHUFFLE) {
+ MusicPlayerRemote.openAndShuffleQueue(songs, true);
+ } else {
+ MusicPlayerRemote.openQueue(songs, 0, true);
+ }
+ handled = true;
+ }
+
+ if (uri != null && uri.toString().length() > 0) {
+ MusicPlayerRemote.playFromUri(uri);
+ handled = true;
+ } else if (MediaStore.Audio.Playlists.CONTENT_TYPE.equals(mimeType)) {
+ final int id = (int) parseIdFromIntent(intent, "playlistId", "playlist");
+ if (id >= 0) {
+ int position = intent.getIntExtra("position", 0);
+ List songs = new ArrayList<>(PlaylistSongsLoader.getPlaylistSongList(this, id));
+ MusicPlayerRemote.openQueue(songs, position, true);
+ handled = true;
+ }
+ } else if (MediaStore.Audio.Albums.CONTENT_TYPE.equals(mimeType)) {
+ final int id = (int) parseIdFromIntent(intent, "albumId", "album");
+ if (id >= 0) {
+ int position = intent.getIntExtra("position", 0);
+ MusicPlayerRemote.openQueue(AlbumLoader.getAlbum(this, id).getSongs(), position, true);
+ handled = true;
+ }
+ } else if (MediaStore.Audio.Artists.CONTENT_TYPE.equals(mimeType)) {
+ final int id = (int) parseIdFromIntent(intent, "artistId", "artist");
+ if (id >= 0) {
+ int position = intent.getIntExtra("position", 0);
+ MusicPlayerRemote.openQueue(ArtistLoader.getArtist(this, id).getSongs(), position, true);
+ handled = true;
+ }
+ }
+ if (handled) {
+ setIntent(new Intent());
+ }
+ }
+
+ private boolean handleSortOrderMenuItem(
+ @NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment
+ fragment, @NonNull MenuItem item) {
+ String sortOrder = null;
+ if (fragment instanceof AlbumsFragment) {
+ switch (item.getItemId()) {
+ case R.id.action_album_sort_order_asc:
+ sortOrder = AlbumSortOrder.ALBUM_A_Z;
+ break;
+ case R.id.action_album_sort_order_desc:
+ sortOrder = AlbumSortOrder.ALBUM_Z_A;
+ break;
+ case R.id.action_album_sort_order_artist:
+ sortOrder = AlbumSortOrder.ALBUM_ARTIST;
+ break;
+ case R.id.action_album_sort_order_year:
+ sortOrder = AlbumSortOrder.ALBUM_YEAR;
+ break;
+ }
+ } else if (fragment instanceof ArtistsFragment) {
+ switch (item.getItemId()) {
+ case R.id.action_artist_sort_order_asc:
+ sortOrder = ArtistSortOrder.ARTIST_A_Z;
+ break;
+ case R.id.action_artist_sort_order_desc:
+ sortOrder = ArtistSortOrder.ARTIST_Z_A;
+ break;
+ }
+ } else if (fragment instanceof SongsFragment) {
+ switch (item.getItemId()) {
+ case R.id.action_song_sort_order_asc:
+ sortOrder = SongSortOrder.SONG_A_Z;
+ break;
+ case R.id.action_song_sort_order_desc:
+ sortOrder = SongSortOrder.SONG_Z_A;
+ break;
+ case R.id.action_song_sort_order_artist:
+ sortOrder = SongSortOrder.SONG_ARTIST;
+ break;
+ case R.id.action_song_sort_order_album:
+ sortOrder = SongSortOrder.SONG_ALBUM;
+ break;
+ case R.id.action_song_sort_order_year:
+ sortOrder = SongSortOrder.SONG_YEAR;
+ break;
+ case R.id.action_song_sort_order_date:
+ sortOrder = SongSortOrder.SONG_DATE;
+ break;
+ case R.id.action_song_sort_order_composer:
+ sortOrder = SongSortOrder.COMPOSER;
+ break;
+ case R.id.action_song_sort_order_date_modified:
+ sortOrder = SongSortOrder.SONG_DATE_MODIFIED;
+ break;
+ }
+ }
+
+ if (sortOrder != null) {
+ item.setChecked(true);
+ fragment.setAndSaveSortOrder(sortOrder);
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean isFolderPage() {
+ return getSupportFragmentManager().findFragmentByTag(FoldersFragment.TAG) instanceof FoldersFragment;
+ }
+
+ private boolean isHomePage() {
+ return getSupportFragmentManager().findFragmentByTag(BannerHomeFragment.TAG) instanceof BannerHomeFragment;
+ }
+
+ private boolean isPlaylistPage() {
+ return getSupportFragmentManager().findFragmentByTag(PlaylistsFragment.TAG) instanceof PlaylistsFragment;
+ }
+
+ private long parseIdFromIntent(@NonNull Intent intent, String longKey,
+ String stringKey) {
+ long id = intent.getLongExtra(longKey, -1);
+ if (id < 0) {
+ String idString = intent.getStringExtra(stringKey);
+ if (idString != null) {
+ try {
+ id = Long.parseLong(idString);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ }
+ }
+ return id;
+ }
+
+ private void restoreCurrentFragment() {
+ currentFragment = (MainActivityFragmentCallbacks) getSupportFragmentManager()
+ .findFragmentById(R.id.fragment_container);
+ }
+
+ private void selectedFragment(final int itemId) {
+ switch (itemId) {
+ case R.id.action_album:
+ setCurrentFragment(AlbumsFragment.newInstance(), AlbumsFragment.TAG);
+ break;
+ case R.id.action_artist:
+ setCurrentFragment(ArtistsFragment.newInstance(), ArtistsFragment.TAG);
+ break;
+ case R.id.action_playlist:
+ setCurrentFragment(PlaylistsFragment.newInstance(), PlaylistsFragment.TAG);
+ break;
+ case R.id.action_genre:
+ setCurrentFragment(GenresFragment.newInstance(), GenresFragment.TAG);
+ break;
+ case R.id.action_playing_queue:
+ setCurrentFragment(PlayingQueueFragment.newInstance(), PlayingQueueFragment.TAG);
+ break;
+ case R.id.action_song:
+ setCurrentFragment(SongsFragment.newInstance(), SongsFragment.TAG);
+ break;
+ case R.id.action_folder:
+ setCurrentFragment(FoldersFragment.newInstance(this), FoldersFragment.TAG);
+ break;
+ default:
+ case R.id.action_home:
+ setCurrentFragment(BannerHomeFragment.newInstance(), BannerHomeFragment.TAG);
+ break;
+ }
+ }
+
+
+ private void setUpGridSizeMenu(@NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment,
+ @NonNull SubMenu gridSizeMenu) {
+
+ switch (fragment.getGridSize()) {
+ case 1:
+ gridSizeMenu.findItem(R.id.action_grid_size_1).setChecked(true);
+ break;
+ case 2:
+ gridSizeMenu.findItem(R.id.action_grid_size_2).setChecked(true);
+ break;
+ case 3:
+ gridSizeMenu.findItem(R.id.action_grid_size_3).setChecked(true);
+ break;
+ case 4:
+ gridSizeMenu.findItem(R.id.action_grid_size_4).setChecked(true);
+ break;
+ case 5:
+ gridSizeMenu.findItem(R.id.action_grid_size_5).setChecked(true);
+ break;
+ case 6:
+ gridSizeMenu.findItem(R.id.action_grid_size_6).setChecked(true);
+ break;
+ case 7:
+ gridSizeMenu.findItem(R.id.action_grid_size_7).setChecked(true);
+ break;
+ case 8:
+ gridSizeMenu.findItem(R.id.action_grid_size_8).setChecked(true);
+ break;
+ }
+ int maxGridSize = fragment.getMaxGridSize();
+ if (maxGridSize < 8) {
+ gridSizeMenu.findItem(R.id.action_grid_size_8).setVisible(false);
+ }
+ if (maxGridSize < 7) {
+ gridSizeMenu.findItem(R.id.action_grid_size_7).setVisible(false);
+ }
+ if (maxGridSize < 6) {
+ gridSizeMenu.findItem(R.id.action_grid_size_6).setVisible(false);
+ }
+ if (maxGridSize < 5) {
+ gridSizeMenu.findItem(R.id.action_grid_size_5).setVisible(false);
+ }
+ if (maxGridSize < 4) {
+ gridSizeMenu.findItem(R.id.action_grid_size_4).setVisible(false);
+ }
+ if (maxGridSize < 3) {
+ gridSizeMenu.findItem(R.id.action_grid_size_3).setVisible(false);
+ }
+ }
+
+ private void setUpSortOrderMenu(
+ @NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment,
+ @NonNull SubMenu sortOrderMenu) {
+ String currentSortOrder = fragment.getSortOrder();
+ sortOrderMenu.clear();
+
+ if (fragment instanceof AlbumsFragment) {
+ sortOrderMenu.add(0, R.id.action_album_sort_order_asc, 0, R.string.sort_order_a_z)
+ .setChecked(currentSortOrder.equals(AlbumSortOrder.ALBUM_A_Z));
+ sortOrderMenu.add(0, R.id.action_album_sort_order_desc, 1, R.string.sort_order_z_a)
+ .setChecked(currentSortOrder.equals(AlbumSortOrder.ALBUM_Z_A));
+ sortOrderMenu.add(0, R.id.action_album_sort_order_artist, 2, R.string.sort_order_artist)
+ .setChecked(currentSortOrder.equals(AlbumSortOrder.ALBUM_ARTIST));
+ sortOrderMenu.add(0, R.id.action_album_sort_order_year, 3, R.string.sort_order_year)
+ .setChecked(currentSortOrder.equals(AlbumSortOrder.ALBUM_YEAR));
+ } else if (fragment instanceof ArtistsFragment) {
+ sortOrderMenu.add(0, R.id.action_artist_sort_order_asc, 0, R.string.sort_order_a_z)
+ .setChecked(currentSortOrder.equals(ArtistSortOrder.ARTIST_A_Z));
+ sortOrderMenu.add(0, R.id.action_artist_sort_order_desc, 1, R.string.sort_order_z_a)
+ .setChecked(currentSortOrder.equals(ArtistSortOrder.ARTIST_Z_A));
+ } else if (fragment instanceof SongsFragment) {
+ sortOrderMenu.add(0, R.id.action_song_sort_order_asc, 0, R.string.sort_order_a_z)
+ .setChecked(currentSortOrder.equals(SongSortOrder.SONG_A_Z));
+ sortOrderMenu.add(0, R.id.action_song_sort_order_desc, 1, R.string.sort_order_z_a)
+ .setChecked(currentSortOrder.equals(SongSortOrder.SONG_Z_A));
+ sortOrderMenu.add(0, R.id.action_song_sort_order_artist, 2, R.string.sort_order_artist)
+ .setChecked(currentSortOrder.equals(SongSortOrder.SONG_ARTIST));
+ sortOrderMenu.add(0, R.id.action_song_sort_order_album, 3, R.string.sort_order_album)
+ .setChecked(currentSortOrder.equals(SongSortOrder.SONG_ALBUM));
+ sortOrderMenu.add(0, R.id.action_song_sort_order_year, 4, R.string.sort_order_year)
+ .setChecked(currentSortOrder.equals(SongSortOrder.SONG_YEAR));
+ sortOrderMenu.add(0, R.id.action_song_sort_order_date, 5, R.string.sort_order_date)
+ .setChecked(currentSortOrder.equals(SongSortOrder.SONG_DATE));
+ sortOrderMenu.add(0, R.id.action_song_sort_order_date_modified, 6, R.string.sort_order_date_modified)
+ .setChecked(currentSortOrder.equals(SongSortOrder.SONG_DATE_MODIFIED));
+ sortOrderMenu.add(0, R.id.action_song_sort_order_composer, 7, R.string.sort_order_composer)
+ .setChecked(currentSortOrder.equals(SongSortOrder.COMPOSER));
+ }
+ sortOrderMenu.setGroupCheckable(0, true, true);
+ }
+
+ private void setupLayoutMenu(
+ @NonNull final AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment,
+ @NonNull final SubMenu subMenu) {
+ switch (fragment.itemLayoutRes()) {
+ case R.layout.item_card:
+ subMenu.findItem(R.id.action_layout_card).setChecked(true);
+ break;
+ case R.layout.item_grid:
+ subMenu.findItem(R.id.action_layout_normal).setChecked(true);
+ break;
+ case R.layout.item_card_color:
+ subMenu.findItem(R.id.action_layout_colored_card).setChecked(true);
+ break;
+ case R.layout.item_grid_circle:
+ subMenu.findItem(R.id.action_layout_circular).setChecked(true);
+ break;
+ case R.layout.image:
+ subMenu.findItem(R.id.action_layout_image).setChecked(true);
+ break;
+ case R.layout.item_image_gradient:
+ subMenu.findItem(R.id.action_layout_gradient_image).setChecked(true);
+ break;
+ }
+ }
+
+ private void setupToolbar() {
+ setTitle(null);
+ mToolbar.setBackgroundColor(Color.TRANSPARENT);
+ mToolbarContainer.setCardBackgroundColor(
+ ColorStateList.valueOf(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface)));
+ setSupportActionBar(mToolbar);
+ mToolbar.setOnClickListener(v -> {
+ ActivityOptions options = ActivityOptions
+ .makeSceneTransitionAnimation(this, mToolbarContainer, getString(R.string.transition_toolbar));
+ NavigationUtil.goToSearch(this, options);
+ });
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(mToolbar);
+ mAppTitle.setVisibility(View.GONE);
+ setTitle(R.string.action_search);
+ }
+ }, 3000);
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt
deleted file mode 100644
index e9cb29abe..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt
+++ /dev/null
@@ -1,214 +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.Intent
-import android.net.Uri
-import android.os.Bundle
-import android.provider.MediaStore
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.contains
-import androidx.navigation.ui.setupWithNavController
-import code.name.monkey.retromusic.R
-import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
-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
-import code.name.monkey.retromusic.service.MusicService
-import code.name.monkey.retromusic.util.PreferenceUtil
-import code.name.monkey.retromusic.util.logE
-import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.launch
-import org.koin.android.ext.android.get
-
-class MainActivity : AbsSlidingMusicPanelActivity() {
- companion object {
- const val TAG = "MainActivity"
- const val EXPAND_PANEL = "expand_panel"
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setTaskDescriptionColorAuto()
- hideStatusBar()
- updateTabs()
-
- setupNavigationController()
-
- WhatsNewFragment.showChangeLog(this)
- }
-
- private fun setupNavigationController() {
- val navController = findNavController(R.id.fragment_container)
- val navInflater = navController.navInflater
- val navGraph = navInflater.inflate(R.navigation.main_graph)
-
- val categoryInfo: CategoryInfo = PreferenceUtil.libraryCategory.first { it.visible }
- if (categoryInfo.visible) {
- 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
- navigationView.setupWithNavController(navController)
- // Scroll Fragment to top
- navigationView.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) {
- if (PreferenceUtil.libraryCategory.firstOrNull { it.category.id == id }?.visible == true) {
- 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(EXPAND_PANEL)?.value ?: false
- if (expand && PreferenceUtil.isExpandPanel) {
- fromNotification = true
- slidingPanel.bringToFront()
- expandPanel()
- intent?.removeExtra(EXPAND_PANEL)
- }
- }
-
- override fun onServiceConnected() {
- super.onServiceConnected()
- intent ?: return
- handlePlaybackIntent(intent)
- }
-
- private fun handlePlaybackIntent(intent: Intent) {
- lifecycleScope.launch(IO) {
- val uri: Uri? = intent.data
- val mimeType: String? = intent.type
- var handled = false
- if (intent.action != null &&
- intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
- ) {
- val songs: List = getSongs(intent.extras!!)
- if (MusicPlayerRemote.shuffleMode == MusicService.SHUFFLE_MODE_SHUFFLE) {
- MusicPlayerRemote.openAndShuffleQueue(songs, true)
- } else {
- MusicPlayerRemote.openQueue(songs, 0, true)
- }
- handled = true
- }
- if (uri != null && uri.toString().isNotEmpty()) {
- MusicPlayerRemote.playFromUri(this@MainActivity, uri)
- handled = true
- } else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) {
- val id = parseLongFromIntent(intent, "playlistId", "playlist")
- if (id >= 0L) {
- val position: Int = intent.getIntExtra("position", 0)
- val songs: List = PlaylistSongsLoader.getPlaylistSongList(get(), id)
- MusicPlayerRemote.openQueue(songs, position, true)
- handled = true
- }
- } else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
- val id = parseLongFromIntent(intent, "albumId", "album")
- if (id >= 0L) {
- val position: Int = intent.getIntExtra("position", 0)
- val songs = libraryViewModel.albumById(id).songs
- MusicPlayerRemote.openQueue(
- songs,
- position,
- true
- )
- handled = true
- }
- } else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
- val id = parseLongFromIntent(intent, "artistId", "artist")
- if (id >= 0L) {
- val position: Int = intent.getIntExtra("position", 0)
- val songs: List = libraryViewModel.artistById(id).songs
- MusicPlayerRemote.openQueue(
- songs,
- position,
- true
- )
- handled = true
- }
- }
- if (handled) {
- setIntent(Intent())
- }
- }
- }
-
- private fun parseLongFromIntent(
- intent: Intent,
- longKey: String,
- stringKey: String,
- ): Long {
- var id = intent.getLongExtra(longKey, -1)
- if (id < 0) {
- val idString = intent.getStringExtra(stringKey)
- if (idString != null) {
- try {
- id = idString.toLong()
- } catch (e: NumberFormatException) {
- logE(e)
- }
- }
- }
- return id
- }
-}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/PermissionActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/PermissionActivity.kt
deleted file mode 100644
index 5cb64cf93..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/activities/PermissionActivity.kt
+++ /dev/null
@@ -1,144 +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.Manifest.permission.BLUETOOTH_CONNECT
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.content.res.ColorStateList
-import android.os.Build
-import android.os.Bundle
-import android.provider.Settings
-import androidx.activity.OnBackPressedCallback
-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.databinding.ActivityPermissionBinding
-import code.name.monkey.retromusic.extensions.*
-
-class PermissionActivity : AbsMusicServiceActivity() {
- private lateinit var binding: ActivityPermissionBinding
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = ActivityPermissionBinding.inflate(layoutInflater)
- setContentView(binding.root)
- setStatusBarColorAuto()
- setTaskDescriptionColorAuto()
- setupTitle()
-
- binding.storagePermission.setButtonClick {
- requestPermissions()
- }
- 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)
- }
- }
- }
-
- if (VersionUtils.hasS()) {
- binding.bluetoothPermission.show()
- binding.bluetoothPermission.setButtonClick {
- ActivityCompat.requestPermissions(
- this,
- arrayOf(BLUETOOTH_CONNECT),
- BLUETOOTH_PERMISSION_REQUEST
- )
- }
- } else {
- binding.audioPermission.setNumber("2")
- }
-
- binding.finish.accentBackgroundColor()
- binding.finish.setOnClickListener {
- if (hasPermissions()) {
- startActivity(
- Intent(this, MainActivity::class.java).addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK or
- Intent.FLAG_ACTIVITY_CLEAR_TASK
- )
- )
- finish()
- }
- }
- onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
- override fun handleOnBackPressed() {
- finishAffinity()
- remove()
- }
- })
- }
-
- private fun setupTitle() {
- val appName =
- getString(
- R.string.message_welcome,
- "Metro "
- )
- .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 hasPermissions()
- }
-
- @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)
- }
-}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt
new file mode 100644
index 000000000..0db47dbf4
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt
@@ -0,0 +1,189 @@
+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.ThemeStore
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.appthemehelper.util.ColorUtil
+import code.name.monkey.appthemehelper.util.MaterialValueHelper
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
+import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
+import code.name.monkey.retromusic.helper.MusicPlayerRemote
+import code.name.monkey.retromusic.util.MusicUtil
+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 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()
+ }
+ }
+ })
+ //ViewUtil.setUpFastScrollRecyclerViewColor(this, 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(ATHUtil.resolveColor(this, R.attr.colorSurface))
+ setSupportActionBar(toolbar)
+ val accentColor = ThemeStore.accentColor(this)
+ clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor)
+ ColorStateList.valueOf(
+ MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor))
+ ).apply {
+ clearQueue.setTextColor(this)
+ clearQueue.iconTint = this
+ }
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/PlaylistDetailActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/PlaylistDetailActivity.kt
new file mode 100644
index 000000000..4541ecb56
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/PlaylistDetailActivity.kt
@@ -0,0 +1,245 @@
+package code.name.monkey.retromusic.activities
+
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.retromusic.App
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
+import code.name.monkey.retromusic.adapter.song.OrderablePlaylistSongAdapter
+import code.name.monkey.retromusic.adapter.song.PlaylistSongAdapter
+import code.name.monkey.retromusic.adapter.song.SongAdapter
+import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
+import code.name.monkey.retromusic.interfaces.CabHolder
+import code.name.monkey.retromusic.loaders.PlaylistLoader
+import code.name.monkey.retromusic.model.AbsCustomPlaylist
+import code.name.monkey.retromusic.model.Playlist
+import code.name.monkey.retromusic.model.Song
+import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter
+import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsView
+import code.name.monkey.retromusic.util.DensityUtil
+import code.name.monkey.retromusic.util.PlaylistsUtil
+import code.name.monkey.retromusic.util.RetroColorUtil
+import com.afollestad.materialcab.MaterialCab
+import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator
+import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
+import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
+import kotlinx.android.synthetic.main.activity_playlist_detail.*
+import javax.inject.Inject
+
+class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder, PlaylistSongsView {
+
+ @Inject
+ lateinit var playlistSongsPresenter: PlaylistSongsPresenter
+
+ private lateinit var playlist: Playlist
+ private var cab: MaterialCab? = null
+ private lateinit var adapter: SongAdapter
+ private var wrappedAdapter: RecyclerView.Adapter<*>? = null
+ private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setDrawUnderStatusBar()
+ super.onCreate(savedInstanceState)
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setTaskDescriptionColorAuto()
+ setLightNavigationBar(true)
+ toggleBottomNavigationView(true)
+
+ App.musicComponent.inject(this)
+ playlistSongsPresenter.attachView(this)
+
+ if (intent.extras != null) {
+ playlist = intent.extras!!.getParcelable(EXTRA_PLAYLIST)!!
+ } else {
+ finish()
+ }
+
+ setUpToolBar()
+ setUpRecyclerView()
+ }
+
+ override fun createContentView(): View {
+ return wrapSlidingMusicPanel(R.layout.activity_playlist_detail)
+ }
+
+ private fun setUpRecyclerView() {
+
+ recyclerView.layoutManager = LinearLayoutManager(this)
+ if (playlist is AbsCustomPlaylist) {
+ adapter = PlaylistSongAdapter(this, ArrayList(), R.layout.item_list, this)
+ recyclerView.adapter = adapter
+ } else {
+ recyclerViewDragDropManager = RecyclerViewDragDropManager()
+ val animator = RefactoredDefaultItemAnimator()
+ adapter = OrderablePlaylistSongAdapter(this,
+ ArrayList(),
+ R.layout.item_list,
+ this,
+ object : OrderablePlaylistSongAdapter.OnMoveItemListener {
+ override fun onMoveItem(fromPosition: Int, toPosition: Int) {
+ if (PlaylistsUtil.moveItem(
+ this@PlaylistDetailActivity,
+ playlist.id,
+ fromPosition,
+ toPosition
+ )
+ ) {
+ val song = adapter.dataSet.removeAt(fromPosition)
+ adapter.dataSet.add(toPosition, song)
+ adapter.notifyItemMoved(fromPosition, toPosition)
+ }
+ }
+ })
+ wrappedAdapter = recyclerViewDragDropManager!!.createWrappedAdapter(adapter)
+
+ recyclerView.adapter = wrappedAdapter
+ recyclerView.itemAnimator = animator
+
+ recyclerViewDragDropManager?.attachRecyclerView(recyclerView)
+ }
+ adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
+ override fun onChanged() {
+ super.onChanged()
+ checkIsEmpty()
+ }
+ })
+ }
+
+ override fun onResume() {
+ super.onResume()
+ playlistSongsPresenter.loadPlaylistSongs(playlist)
+ }
+
+ private fun setUpToolBar() {
+ toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
+ setSupportActionBar(toolbar)
+ title = playlist.name
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(
+ if (playlist is AbsCustomPlaylist) R.menu.menu_smart_playlist_detail
+ else R.menu.menu_playlist_detail, menu
+ )
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ android.R.id.home -> {
+ onBackPressed()
+ return true
+ }
+ }
+ return PlaylistMenuHelper.handleMenuClick(this, playlist, item)
+ }
+
+ override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
+ if (cab != null && cab!!.isActive) {
+ cab!!.finish()
+ }
+ cab = MaterialCab(this, R.id.cab_stub).setMenu(menuRes)
+ .setCloseDrawableRes(R.drawable.ic_close_white_24dp)
+ .setBackgroundColor(
+ RetroColorUtil.shiftBackgroundColorForLightText(
+ ATHUtil.resolveColor(
+ this,
+ R.attr.colorSurface
+ )
+ )
+ ).start(callback)
+ return cab!!
+ }
+
+ override fun onBackPressed() {
+ if (cab != null && cab!!.isActive) {
+ cab!!.finish()
+ } else {
+ recyclerView!!.stopScroll()
+ super.onBackPressed()
+ }
+ }
+
+ override fun onMediaStoreChanged() {
+ super.onMediaStoreChanged()
+ if (playlist !is AbsCustomPlaylist) {
+ // Playlist deleted
+ if (!PlaylistsUtil.doesPlaylistExist(this, playlist.id)) {
+ finish()
+ return
+ }
+ // Playlist renamed
+ val playlistName = PlaylistsUtil.getNameForPlaylist(this, playlist.id.toLong())
+ if (playlistName != playlist.name) {
+ playlist = PlaylistLoader.getPlaylist(this, playlist.id)
+ setToolbarTitle(playlist.name)
+ }
+ }
+ playlistSongsPresenter.loadPlaylistSongs(playlist)
+ }
+
+ private fun setToolbarTitle(title: String) {
+ supportActionBar!!.title = title
+ }
+
+ private fun checkForPadding() {
+ val height = DensityUtil.dip2px(this, 52f)
+ recyclerView.setPadding(0, 0, 0, (height))
+ }
+
+ private fun checkIsEmpty() {
+ checkForPadding()
+ emptyEmoji.text = getEmojiByUnicode(0x1F631)
+ empty.visibility = if (adapter.itemCount == 0) View.VISIBLE else View.GONE
+ emptyText.visibility = if (adapter.itemCount == 0) View.VISIBLE else View.GONE
+ }
+
+ private fun getEmojiByUnicode(unicode: Int): String {
+ return String(Character.toChars(unicode))
+ }
+
+ public override fun onPause() {
+ if (recyclerViewDragDropManager != null) {
+ recyclerViewDragDropManager!!.cancelDrag()
+ }
+ super.onPause()
+ }
+
+ override fun onDestroy() {
+ if (recyclerViewDragDropManager != null) {
+ recyclerViewDragDropManager!!.release()
+ recyclerViewDragDropManager = null
+ }
+
+ if (recyclerView != null) {
+ recyclerView!!.itemAnimator = null
+ recyclerView!!.adapter = null
+ }
+
+ if (wrappedAdapter != null) {
+ WrapperAdapterUtils.releaseAll(wrappedAdapter)
+ wrappedAdapter = null
+ }
+ super.onDestroy()
+ playlistSongsPresenter.detachView()
+ }
+
+ override fun showEmptyView() {
+ empty.visibility = View.VISIBLE
+ emptyText.visibility = View.VISIBLE
+ }
+
+ override fun songs(songs: List) {
+ adapter.swapDataSet(songs)
+ }
+
+ companion object {
+ var EXTRA_PLAYLIST = "extra_playlist"
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/PurchaseActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/PurchaseActivity.kt
new file mode 100644
index 000000000..20f187498
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/PurchaseActivity.kt
@@ -0,0 +1,159 @@
+package code.name.monkey.retromusic.activities
+
+import android.content.Intent
+import android.content.res.ColorStateList
+import android.graphics.Color
+import android.os.AsyncTask
+import android.os.Bundle
+import android.util.Log
+import android.view.MenuItem
+import android.widget.Toast
+import code.name.monkey.appthemehelper.ThemeStore
+import code.name.monkey.appthemehelper.util.MaterialUtil
+import code.name.monkey.retromusic.App
+import code.name.monkey.retromusic.BuildConfig
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.activities.base.AbsBaseActivity
+import com.anjlab.android.iab.v3.BillingProcessor
+import com.anjlab.android.iab.v3.TransactionDetails
+import kotlinx.android.synthetic.main.activity_pro_version.*
+import java.lang.ref.WeakReference
+
+class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
+
+ private lateinit var billingProcessor: BillingProcessor
+ private var restorePurchaseAsyncTask: AsyncTask<*, *, *>? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setDrawUnderStatusBar()
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_pro_version)
+ setStatusbarColor(Color.TRANSPARENT)
+ setLightStatusbar(false)
+ setNavigationbarColor(Color.BLACK)
+ setLightNavigationBar(false)
+ toolbar.navigationIcon?.setTint(Color.WHITE)
+ toolbar.setNavigationOnClickListener { onBackPressed() }
+
+ restoreButton.isEnabled = false
+ purchaseButton.isEnabled = false
+
+ billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
+
+ MaterialUtil.setTint(purchaseButton, true)
+
+ restoreButton.setOnClickListener {
+ if (restorePurchaseAsyncTask == null || restorePurchaseAsyncTask!!.status != AsyncTask.Status.RUNNING) {
+ restorePurchase()
+ }
+
+ }
+ purchaseButton.setOnClickListener {
+ billingProcessor.purchase(this@PurchaseActivity, App.PRO_VERSION_PRODUCT_ID)
+ }
+ bannerContainer.backgroundTintList =
+ ColorStateList.valueOf(ThemeStore.accentColor(this))
+ }
+
+ private fun restorePurchase() {
+ if (restorePurchaseAsyncTask != null) {
+ restorePurchaseAsyncTask!!.cancel(false)
+ }
+ restorePurchaseAsyncTask = RestorePurchaseAsyncTask(this).execute()
+ }
+
+ override fun onProductPurchased(productId: String, details: TransactionDetails?) {
+ Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
+ setResult(RESULT_OK)
+ }
+
+ override fun onPurchaseHistoryRestored() {
+ if (App.isProVersion()) {
+ Toast.makeText(
+ this,
+ R.string.restored_previous_purchase_please_restart,
+ Toast.LENGTH_LONG
+ ).show()
+ setResult(RESULT_OK)
+ } else {
+ Toast.makeText(this, R.string.no_purchase_found, Toast.LENGTH_SHORT).show()
+ }
+ }
+
+ override fun onBillingError(errorCode: Int, error: Throwable?) {
+ Log.e(TAG, "Billing error: code = $errorCode", error)
+ }
+
+ override fun onBillingInitialized() {
+ restoreButton.isEnabled = true
+ purchaseButton.isEnabled = true
+ }
+
+ public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if (!billingProcessor.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data)
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ android.R.id.home -> finish()
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ override fun onDestroy() {
+ billingProcessor.release()
+ super.onDestroy()
+ }
+
+ private class RestorePurchaseAsyncTask internal constructor(purchaseActivity: PurchaseActivity) :
+ AsyncTask() {
+
+ private val buyActivityWeakReference: WeakReference = WeakReference(
+ purchaseActivity
+ )
+
+ override fun onPreExecute() {
+ super.onPreExecute()
+ val purchaseActivity = buyActivityWeakReference.get()
+ if (purchaseActivity != null) {
+ Toast.makeText(purchaseActivity, R.string.restoring_purchase, Toast.LENGTH_SHORT)
+ .show()
+ } else {
+ cancel(false)
+ }
+ }
+
+ override fun doInBackground(vararg params: Void): Boolean? {
+ val purchaseActivity = buyActivityWeakReference.get()
+ if (purchaseActivity != null) {
+ return purchaseActivity.billingProcessor.loadOwnedPurchasesFromGoogle()
+ }
+ cancel(false)
+ return null
+ }
+
+ override fun onPostExecute(b: Boolean?) {
+ super.onPostExecute(b)
+ val purchaseActivity = buyActivityWeakReference.get()
+ if (purchaseActivity == null || b == null) {
+ return
+ }
+
+ if (b) {
+ purchaseActivity.onPurchaseHistoryRestored()
+ } else {
+ Toast.makeText(
+ purchaseActivity,
+ R.string.could_not_restore_purchase,
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG: String = "PurchaseActivity"
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/SearchActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/SearchActivity.kt
new file mode 100644
index 000000000..494b6dd58
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/SearchActivity.kt
@@ -0,0 +1,226 @@
+package code.name.monkey.retromusic.activities
+
+import android.app.Activity
+import android.app.Service
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.content.res.ColorStateList
+import android.os.Bundle
+import android.speech.RecognizerIntent
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.View
+import android.view.inputmethod.InputMethodManager
+import android.widget.TextView.BufferType
+import android.widget.Toast
+import androidx.appcompat.widget.SearchView.OnQueryTextListener
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.transition.TransitionManager
+import code.name.monkey.appthemehelper.ThemeStore
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.appthemehelper.util.ColorUtil
+import code.name.monkey.appthemehelper.util.MaterialValueHelper
+import code.name.monkey.retromusic.App
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
+import code.name.monkey.retromusic.adapter.SearchAdapter
+import code.name.monkey.retromusic.mvp.presenter.SearchPresenter
+import code.name.monkey.retromusic.mvp.presenter.SearchView
+import code.name.monkey.retromusic.util.RetroUtil
+import com.google.android.material.textfield.TextInputEditText
+import kotlinx.android.synthetic.main.activity_search.*
+import java.util.*
+import javax.inject.Inject
+import kotlin.collections.ArrayList
+import kotlin.collections.MutableList
+import kotlin.collections.emptyList
+
+class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatcher, SearchView {
+ @Inject
+ lateinit var searchPresenter: SearchPresenter
+
+ private var searchAdapter: SearchAdapter? = null
+ private var query: String? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setDrawUnderStatusBar()
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_search)
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setTaskDescriptionColorAuto()
+ setLightNavigationBar(true)
+
+ App.musicComponent.inject(this)
+ searchPresenter.attachView(this)
+
+ setupRecyclerView()
+ setUpToolBar()
+ setupSearchView()
+
+ if (intent.getBooleanExtra(EXTRA_SHOW_MIC, false)) {
+ startMicSearch()
+ }
+
+ back.setOnClickListener { onBackPressed() }
+ voiceSearch.setOnClickListener { startMicSearch() }
+ clearText.setOnClickListener { searchView.clearText() }
+ searchContainer.backgroundTintList =
+ ColorStateList.valueOf(ATHUtil.resolveColor(this, R.attr.colorSurface))
+
+ keyboardPopup.setOnClickListener {
+ val inputManager = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
+ inputManager.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT)
+ }
+
+ keyboardPopup.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
+ ColorStateList.valueOf(
+ MaterialValueHelper.getPrimaryTextColor(
+ this,
+ ColorUtil.isColorLight(ThemeStore.accentColor(this))
+ )
+ ).apply {
+ keyboardPopup.setTextColor(this)
+ keyboardPopup.iconTint = this
+ }
+ if (savedInstanceState != null) {
+ query = savedInstanceState.getString(QUERY)
+ }
+ }
+
+ private fun setupRecyclerView() {
+ searchAdapter = SearchAdapter(this, emptyList())
+ searchAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
+ override fun onChanged() {
+ super.onChanged()
+ empty.visibility = if (searchAdapter!!.itemCount < 1) View.VISIBLE else View.GONE
+ }
+ })
+ recyclerView.apply {
+ layoutManager = LinearLayoutManager(this@SearchActivity)
+ adapter = searchAdapter
+ }
+ recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ super.onScrolled(recyclerView, dx, dy)
+ if (dy > 0) {
+ keyboardPopup.shrink()
+ } else if (dy < 0) {
+ keyboardPopup.extend()
+ }
+ }
+ })
+ }
+
+ private fun setupSearchView() {
+ searchView.addTextChangedListener(this)
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ searchPresenter.detachView()
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ outState.putString(QUERY, query)
+ }
+
+ private fun setUpToolBar() {
+ title = null
+ }
+
+ private fun search(query: String) {
+ this.query = query
+ TransitionManager.beginDelayedTransition(appBarLayout)
+ voiceSearch.visibility = if (query.isNotEmpty()) View.GONE else View.VISIBLE
+ clearText.visibility = if (query.isNotEmpty()) View.VISIBLE else View.GONE
+ searchPresenter.search(query)
+ }
+
+ override fun onMediaStoreChanged() {
+ super.onMediaStoreChanged()
+ query?.let { search(it) }
+ }
+
+ override fun onQueryTextSubmit(query: String): Boolean {
+ hideSoftKeyboard()
+ return false
+ }
+
+ override fun onQueryTextChange(newText: String): Boolean {
+ search(newText)
+ return false
+ }
+
+ private fun hideSoftKeyboard() {
+ RetroUtil.hideSoftKeyboard(this@SearchActivity)
+ if (searchView != null) {
+ searchView.clearFocus()
+ }
+ }
+
+ override fun showEmptyView() {
+ searchAdapter?.swapDataSet(ArrayList())
+ }
+
+ override fun showData(data: MutableList) {
+ searchAdapter?.swapDataSet(data)
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ when (requestCode) {
+ REQ_CODE_SPEECH_INPUT -> {
+ if (resultCode == Activity.RESULT_OK && null != data) {
+ val result: ArrayList? =
+ data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
+ query = result?.get(0)
+ searchView.setText(query, BufferType.EDITABLE)
+ searchPresenter.search(query!!)
+ }
+ }
+ }
+ }
+
+ private fun startMicSearch() {
+ val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
+ intent.putExtra(
+ RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+ RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
+ )
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault())
+ intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.speech_prompt))
+ try {
+ startActivityForResult(intent, REQ_CODE_SPEECH_INPUT)
+ } catch (e: ActivityNotFoundException) {
+ e.printStackTrace()
+ Toast.makeText(this, getString(R.string.speech_not_supported), Toast.LENGTH_SHORT)
+ .show()
+ }
+ }
+
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
+ }
+
+ override fun onTextChanged(newText: CharSequence, start: Int, before: Int, count: Int) {
+ search(newText.toString())
+ }
+
+ override fun afterTextChanged(s: Editable) {
+ }
+
+ companion object {
+ val TAG: String = SearchActivity::class.java.simpleName
+
+ const val EXTRA_SHOW_MIC = "extra_show_mic"
+ const val QUERY: String = "query"
+
+ private const val REQ_CODE_SPEECH_INPUT = 9002
+ }
+}
+
+fun TextInputEditText.clearText() {
+ text = null
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt
new file mode 100755
index 000000000..a788e5673
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt
@@ -0,0 +1,80 @@
+package code.name.monkey.retromusic.activities
+
+import android.os.Bundle
+import android.view.MenuItem
+import androidx.annotation.StringRes
+import androidx.fragment.app.Fragment
+import code.name.monkey.appthemehelper.util.ATHUtil
+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.fragments.settings.MainSettingsFragment
+import kotlinx.android.synthetic.main.activity_settings.*
+
+class SettingsActivity : AbsBaseActivity() {
+
+ private val fragmentManager = supportFragmentManager
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setDrawUnderStatusBar()
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_settings)
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setLightNavigationBar(true)
+
+ setupToolbar()
+
+ if (savedInstanceState == null) {
+ fragmentManager.beginTransaction().replace(R.id.contentFrame, MainSettingsFragment())
+ .commit()
+ }
+ }
+
+ private fun setupToolbar() {
+ setTitle(R.string.action_settings)
+ toolbar.apply {
+ setBackgroundColor(ATHUtil.resolveColor(this@SettingsActivity, R.attr.colorSurface))
+ setNavigationOnClickListener { onBackPressed() }
+ ToolbarContentTintHelper.colorBackButton(toolbar)
+ }
+ setSupportActionBar(toolbar)
+ }
+
+ fun setupFragment(fragment: Fragment, @StringRes titleName: Int) {
+ val fragmentTransaction = fragmentManager
+ .beginTransaction()
+ .setCustomAnimations(
+ R.anim.sliding_in_left,
+ R.anim.sliding_out_right,
+ android.R.anim.slide_in_left,
+ android.R.anim.slide_out_right
+ )
+ fragmentTransaction.replace(R.id.contentFrame, fragment, fragment.tag)
+ fragmentTransaction.addToBackStack(null)
+ fragmentTransaction.commit()
+
+ setTitle(titleName)
+ }
+
+ override fun onBackPressed() {
+ if (fragmentManager.backStackEntryCount == 0) {
+ super.onBackPressed()
+ } else {
+ setTitle(R.string.action_settings)
+ fragmentManager.popBackStack()
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home) {
+ onBackPressed()
+ return true
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ companion object {
+ const val TAG: String = "SettingsActivity"
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/ShareInstagramStory.kt b/app/src/main/java/code/name/monkey/retromusic/activities/ShareInstagramStory.kt
index 546c9d794..3b3f36049 100644
--- a/app/src/main/java/code/name/monkey/retromusic/activities/ShareInstagramStory.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/ShareInstagramStory.kt
@@ -1,51 +1,46 @@
/*
- * Copyright (c) 2020 Hemanth Savarla.
+ * Copyright (c) 2020 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 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.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.os.BundleCompat
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.activities.base.AbsThemeActivity
-import code.name.monkey.retromusic.databinding.ActivityShareInstagramBinding
-import code.name.monkey.retromusic.extensions.accentColor
-import code.name.monkey.retromusic.extensions.setStatusBarColor
-import code.name.monkey.retromusic.glide.RetroGlideExtension
-import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
-import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.activities.base.AbsBaseActivity
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.
*/
-class ShareInstagramStory : AbsThemeActivity() {
-
- private lateinit var binding: ActivityShareInstagramBinding
+class ShareInstagramStory : AbsBaseActivity() {
companion object {
const val EXTRA_SONG = "extra_song"
@@ -53,59 +48,76 @@ class ShareInstagramStory : AbsThemeActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
- onBackPressedDispatcher.onBackPressed()
+ onBackPressed()
return true
}
return super.onOptionsItemSelected(item)
}
override fun onCreate(savedInstanceState: Bundle?) {
+ setDrawUnderStatusBar()
super.onCreate(savedInstanceState)
- binding = ActivityShareInstagramBinding.inflate(layoutInflater)
- setContentView(binding.root)
- setStatusBarColor(Color.TRANSPARENT)
+ setContentView(R.layout.activity_share_instagram)
+ setStatusbarColor(Color.TRANSPARENT)
+ setNavigationbarColor(Color.BLACK)
- binding.toolbar.setBackgroundColor(Color.TRANSPARENT)
- setSupportActionBar(binding.toolbar)
+ toolbar.setBackgroundColor(Color.TRANSPARENT)
+ setSupportActionBar(toolbar)
- val song = intent.extras?.let { BundleCompat.getParcelable(it, EXTRA_SONG, Song::class.java) }
+ val song = intent.extras?.getParcelable(EXTRA_SONG)
song?.let { songFinal ->
- Glide.with(this)
- .asBitmapPalette()
- .songCoverOptions(songFinal)
- .load(RetroGlideExtension.getSongModel(songFinal))
- .into(object : RetroMusicColoredTarget(binding.image) {
+ SongGlideRequest.Builder.from(Glide.with(this), songFinal)
+ .checkIgnoreMediaStore(this@ShareInstagramStory)
+ .generatePalette(this@ShareInstagramStory)
+ .build()
+ .into(object : RetroMusicColoredTarget(image) {
override fun onColorReady(colors: MediaNotificationProcessor) {
- setColors(colors.backgroundColor)
+ val isColorLight = ColorUtil.isColorLight(colors.backgroundColor)
+ setColors(isColorLight, colors.backgroundColor)
}
})
- binding.shareTitle.text = songFinal.title
- binding.shareText.text = songFinal.artistName
- binding.shareButton.setOnClickListener {
+ shareTitle.text = songFinal.title
+ shareText.text = songFinal.artistName
+ shareButton.setOnClickListener {
val path: String = Media.insertImage(
contentResolver,
- binding.mainContent.drawToBitmap(Bitmap.Config.ARGB_8888),
+ mainContent.drawToBitmap(Bitmap.Config.ARGB_8888),
"Design", null
)
+ val uri = Uri.parse(path)
Share.shareStoryToSocial(
this@ShareInstagramStory,
- path.toUri()
+ uri
)
}
}
- binding.shareButton.setTextColor(
+ shareButton.setTextColor(
MaterialValueHelper.getPrimaryTextColor(
this,
- ColorUtil.isColorLight(accentColor())
+ ColorUtil.isColorLight(ThemeStore.accentColor(this))
)
)
- binding.shareButton.backgroundTintList =
- ColorStateList.valueOf(accentColor())
+ shareButton.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
}
- private fun setColors(color: Int) {
- binding.mainContent.background =
+ private fun setColors(colorLight: Boolean, color: Int) {
+ setLightStatusbar(colorLight)
+ toolbar.setTitleTextColor(
+ MaterialValueHelper.getPrimaryTextColor(
+ this@ShareInstagramStory,
+ colorLight
+ )
+ )
+ toolbar.navigationIcon?.setTintList(
+ ColorStateList.valueOf(
+ MaterialValueHelper.getPrimaryTextColor(
+ this@ShareInstagramStory,
+ colorLight
+ )
+ )
+ )
+ mainContent.background =
GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
intArrayOf(color, Color.BLACK)
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt
new file mode 100644
index 000000000..010b18081
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt
@@ -0,0 +1,262 @@
+package code.name.monkey.retromusic.activities
+
+import android.content.Intent
+import android.graphics.Paint
+import android.os.AsyncTask
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import android.widget.Toast
+import androidx.annotation.LayoutRes
+import androidx.appcompat.widget.AppCompatImageView
+import androidx.recyclerview.widget.DefaultItemAnimator
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import code.name.monkey.appthemehelper.ThemeStore
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.appthemehelper.util.MaterialUtil
+import code.name.monkey.appthemehelper.util.TintHelper
+import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
+import code.name.monkey.retromusic.BuildConfig
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.activities.base.AbsBaseActivity
+import code.name.monkey.retromusic.dialogs.UpiPaymentBottomSheetDialogFragment
+import code.name.monkey.retromusic.extensions.textColorPrimary
+import code.name.monkey.retromusic.extensions.textColorSecondary
+import com.anjlab.android.iab.v3.BillingProcessor
+import com.anjlab.android.iab.v3.SkuDetails
+import com.anjlab.android.iab.v3.TransactionDetails
+import kotlinx.android.synthetic.main.activity_about.toolbar
+import kotlinx.android.synthetic.main.activity_donation.*
+import java.lang.ref.WeakReference
+import java.util.*
+
+class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
+
+ companion object {
+ val TAG: String = SupportDevelopmentActivity::class.java.simpleName
+ const val DONATION_PRODUCT_IDS = R.array.donation_ids
+ private const val TEZ_REQUEST_CODE = 123
+ }
+
+ var billingProcessor: BillingProcessor? = null
+ private var skuDetailsLoadAsyncTask: AsyncTask<*, *, *>? = null
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home) {
+ onBackPressed()
+ return true
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ fun donate(i: Int) {
+ val ids = resources.getStringArray(DONATION_PRODUCT_IDS)
+ billingProcessor?.purchase(this, ids[i])
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_donation)
+
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setTaskDescriptionColorAuto()
+ setLightNavigationBar(true)
+
+ setupToolbar()
+
+ billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
+ TintHelper.setTint(progress, ThemeStore.accentColor(this))
+ donation.setTextColor(ThemeStore.accentColor(this))
+
+ MaterialUtil.setTint(upiClick)
+ upiClick.setOnClickListener {
+ UpiPaymentBottomSheetDialogFragment().show(
+ supportFragmentManager,
+ UpiPaymentBottomSheetDialogFragment.TAG
+ )
+ }
+ }
+
+ private fun setupToolbar() {
+ val toolbarColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
+ toolbar.setBackgroundColor(toolbarColor)
+ ToolbarContentTintHelper.colorBackButton(toolbar)
+ setSupportActionBar(toolbar)
+ }
+
+ override fun onBillingInitialized() {
+ loadSkuDetails()
+ }
+
+ private fun loadSkuDetails() {
+ if (skuDetailsLoadAsyncTask != null) {
+ skuDetailsLoadAsyncTask!!.cancel(false)
+ }
+ skuDetailsLoadAsyncTask = SkuDetailsLoadAsyncTask(this).execute()
+ }
+
+ override fun onProductPurchased(productId: String, details: TransactionDetails?) {
+ //loadSkuDetails();
+ Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
+ }
+
+ override fun onBillingError(errorCode: Int, error: Throwable?) {
+ Log.e(TAG, "Billing error: code = $errorCode", error)
+ }
+
+ override fun onPurchaseHistoryRestored() {
+ //loadSkuDetails();
+ Toast.makeText(this, R.string.restored_previous_purchases, Toast.LENGTH_SHORT).show()
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if (!billingProcessor!!.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data)
+ }
+ if (requestCode == TEZ_REQUEST_CODE) {
+ // Process based on the data in response.
+ Log.d("result", data!!.getStringExtra("Status"))
+ }
+ }
+
+ override fun onDestroy() {
+ billingProcessor?.release()
+ skuDetailsLoadAsyncTask?.cancel(true)
+ super.onDestroy()
+ }
+}
+
+private class SkuDetailsLoadAsyncTask internal constructor(supportDevelopmentActivity: SupportDevelopmentActivity) :
+ AsyncTask>() {
+
+ private val weakReference: WeakReference = WeakReference(
+ supportDevelopmentActivity
+ )
+
+ override fun onPreExecute() {
+ super.onPreExecute()
+ val supportDevelopmentActivity = weakReference.get() ?: return
+
+ supportDevelopmentActivity.progressContainer.visibility = View.VISIBLE
+ supportDevelopmentActivity.recyclerView.visibility = View.GONE
+ }
+
+ override fun doInBackground(vararg params: Void): List? {
+ val dialog = weakReference.get()
+ if (dialog != null) {
+ val ids =
+ dialog.resources.getStringArray(SupportDevelopmentActivity.DONATION_PRODUCT_IDS)
+ return dialog.billingProcessor!!.getPurchaseListingDetails(ArrayList(Arrays.asList(*ids)))
+ }
+ cancel(false)
+ return null
+ }
+
+ override fun onPostExecute(skuDetails: List?) {
+ super.onPostExecute(skuDetails)
+ val dialog = weakReference.get() ?: return
+
+ if (skuDetails == null || skuDetails.isEmpty()) {
+ dialog.progressContainer.visibility = View.GONE
+ return
+ }
+
+ dialog.progressContainer.visibility = View.GONE
+ dialog.recyclerView.itemAnimator = DefaultItemAnimator()
+ dialog.recyclerView.layoutManager = GridLayoutManager(dialog, 2)
+ dialog.recyclerView.adapter = SkuDetailsAdapter(dialog, skuDetails)
+ dialog.recyclerView.visibility = View.VISIBLE
+ }
+}
+
+class SkuDetailsAdapter(
+ private var donationsDialog: SupportDevelopmentActivity, objects: List
+) : RecyclerView.Adapter() {
+
+ private var skuDetailsList: List = ArrayList()
+
+ init {
+ skuDetailsList = objects
+ }
+
+ private fun getIcon(position: Int): Int {
+ return when (position) {
+ 0 -> R.drawable.ic_cookie_white_24dp
+ 1 -> R.drawable.ic_take_away_white_24dp
+ 2 -> R.drawable.ic_take_away_coffe_white_24dp
+ 3 -> R.drawable.ic_beer_white_24dp
+ 4 -> R.drawable.ic_fast_food_meal_white_24dp
+ 5 -> R.drawable.ic_popcorn_white_24dp
+ 6 -> R.drawable.ic_card_giftcard_white_24dp
+ 7 -> R.drawable.ic_food_croissant_white_24dp
+ else -> R.drawable.ic_card_giftcard_white_24dp
+ }
+ }
+
+ override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
+ return ViewHolder(
+ LayoutInflater.from(donationsDialog).inflate(
+ LAYOUT_RES_ID,
+ viewGroup,
+ false
+ )
+ )
+ }
+
+ override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
+ val skuDetails = skuDetailsList[i]
+ viewHolder.title.text = skuDetails.title.replace("(Retro Music Player)", "")
+ .trim { it <= ' ' }
+ viewHolder.text.text = skuDetails.description
+ viewHolder.text.visibility = View.GONE
+ viewHolder.price.text = skuDetails.priceText
+ viewHolder.image.setImageResource(getIcon(i))
+
+ val purchased = donationsDialog.billingProcessor!!.isPurchased(skuDetails.productId)
+ val titleTextColor = if (purchased) ATHUtil.resolveColor(
+ donationsDialog,
+ android.R.attr.textColorHint
+ ) else textColorPrimary(donationsDialog)
+ val contentTextColor =
+ if (purchased) titleTextColor else textColorSecondary(donationsDialog)
+
+ viewHolder.title.setTextColor(titleTextColor)
+ viewHolder.text.setTextColor(contentTextColor)
+ viewHolder.price.setTextColor(titleTextColor)
+
+ strikeThrough(viewHolder.title, purchased)
+ strikeThrough(viewHolder.text, purchased)
+ strikeThrough(viewHolder.price, purchased)
+
+ viewHolder.itemView.setOnTouchListener { _, _ -> purchased }
+ viewHolder.itemView.setOnClickListener { donationsDialog.donate(i) }
+ }
+
+ override fun getItemCount(): Int {
+ return skuDetailsList.size
+ }
+
+ class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ var title: TextView = view.findViewById(R.id.itemTitle)
+ var text: TextView = view.findViewById(R.id.itemText)
+ var price: TextView = view.findViewById(R.id.itemPrice)
+ var image: AppCompatImageView = view.findViewById(R.id.itemImage)
+ }
+
+ companion object {
+ @LayoutRes
+ private val LAYOUT_RES_ID = R.layout.item_donation_option
+
+ private fun strikeThrough(textView: TextView, strikeThrough: Boolean) {
+ textView.paintFlags =
+ if (strikeThrough) textView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
+ else textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
+ }
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/UserInfoActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/UserInfoActivity.kt
new file mode 100644
index 000000000..258befbdd
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/UserInfoActivity.kt
@@ -0,0 +1,267 @@
+package code.name.monkey.retromusic.activities
+
+import android.app.Activity
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.content.res.ColorStateList
+import android.graphics.Bitmap
+import android.net.Uri
+import android.os.Bundle
+import android.provider.MediaStore.Images.Media
+import android.provider.MediaStore.Images.Media.getBitmap
+import android.text.TextUtils
+import android.view.MenuItem
+import android.widget.Toast
+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.MaterialUtil
+import code.name.monkey.appthemehelper.util.MaterialValueHelper
+import code.name.monkey.retromusic.App
+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.util.Compressor
+import code.name.monkey.retromusic.util.ImageUtil.getResizedBitmap
+import code.name.monkey.retromusic.util.PreferenceUtil
+import com.afollestad.materialdialogs.LayoutMode
+import com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.bottomsheets.BottomSheet
+import com.afollestad.materialdialogs.list.listItems
+import kotlinx.android.synthetic.main.activity_user_info.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+
+class UserInfoActivity : AbsBaseActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_user_info)
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setTaskDescriptionColorAuto()
+ setLightNavigationBar(true)
+
+ toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
+ setSupportActionBar(toolbar)
+
+ MaterialUtil.setTint(nameContainer, false)
+ name.setText(PreferenceUtil.getInstance(this).userName)
+
+ if (PreferenceUtil.getInstance(this).profileImage.isNotEmpty()) {
+ loadImageFromStorage(PreferenceUtil.getInstance(this).profileImage)
+ }
+ if (PreferenceUtil.getInstance(this).bannerImage.isNotEmpty()) {
+ loadBannerFromStorage(PreferenceUtil.getInstance(this).bannerImage)
+ }
+ userImage.setOnClickListener {
+ MaterialDialog(this).show {
+ cornerRadius(PreferenceUtil.getInstance(this@UserInfoActivity).dialogCorner)
+ title(text = getString(R.string.set_photo))
+ listItems(
+ items = listOf(
+ getString(R.string.new_profile_photo),
+ getString(R.string.remove_profile_photo)
+ )
+ ) { _, position, _ ->
+ when (position) {
+ 0 -> pickNewPhoto()
+ 1 -> PreferenceUtil.getInstance(this@UserInfoActivity).saveProfileImage("")
+ }
+ }
+ }
+ }
+ bannerSelect.setOnClickListener {
+ MaterialDialog(this).show {
+ cornerRadius(PreferenceUtil.getInstance(this@UserInfoActivity).dialogCorner)
+ title(R.string.select_banner_photo)
+ listItems(
+ items = listOf(
+ getString(R.string.new_banner_photo),
+ getString(R.string.remove_banner_photo)
+ )
+ )
+ { _, position, _ ->
+ when (position) {
+ 0 -> selectBannerImage()
+ 1 -> PreferenceUtil.getInstance(this@UserInfoActivity).setBannerImagePath("")
+ }
+ }
+ }
+ }
+ next.setOnClickListener {
+ val nameString = name.text.toString().trim { it <= ' ' }
+ if (TextUtils.isEmpty(nameString)) {
+ Toast.makeText(this, "Umm name is empty", Toast.LENGTH_SHORT).show()
+ return@setOnClickListener
+ }
+ PreferenceUtil.getInstance(this).userName = nameString
+ setResult(Activity.RESULT_OK)
+ finish()
+ }
+ next.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
+ ColorStateList.valueOf(
+ MaterialValueHelper.getPrimaryTextColor(
+ this,
+ ColorUtil.isColorLight(ThemeStore.accentColor(this))
+ )
+ )
+ .apply {
+ next.setTextColor(this)
+ next.iconTint = this
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home) {
+ onBackPressed()
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ private fun selectBannerImage() {
+ val pickImageIntent = Intent(Intent.ACTION_PICK, Media.EXTERNAL_CONTENT_URI)
+ pickImageIntent.type = "image/*"
+ //pickImageIntent.putExtra("crop", "true")
+ pickImageIntent.putExtra("outputX", 1290)
+ pickImageIntent.putExtra("outputY", 720)
+ pickImageIntent.putExtra("aspectX", 16)
+ pickImageIntent.putExtra("aspectY", 9)
+ pickImageIntent.putExtra("scale", true)
+ //intent.setAction(Intent.ACTION_GET_CONTENT);
+ startActivityForResult(
+ Intent.createChooser(pickImageIntent, "Select Picture"),
+ PICK_BANNER_REQUEST
+ )
+ }
+
+ private fun pickNewPhoto() {
+ val pickImageIntent = Intent(Intent.ACTION_PICK, Media.EXTERNAL_CONTENT_URI)
+ pickImageIntent.type = "image/*"
+ pickImageIntent.putExtra("crop", "true")
+ pickImageIntent.putExtra("outputX", 512)
+ pickImageIntent.putExtra("outputY", 512)
+ pickImageIntent.putExtra("aspectX", 1)
+ pickImageIntent.putExtra("aspectY", 1)
+ pickImageIntent.putExtra("scale", true)
+ startActivityForResult(
+ Intent.createChooser(pickImageIntent, "Select Picture"),
+ PICK_IMAGE_REQUEST
+ )
+ }
+
+ public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ when (requestCode) {
+ PICK_IMAGE_REQUEST -> {
+ try {
+ data.data?.let {
+ val bitmap = getResizedBitmap(
+ getBitmap(contentResolver, it),
+ PROFILE_ICON_SIZE
+ )
+ val profileImagePath = saveToInternalStorage(bitmap, USER_PROFILE)
+ PreferenceUtil.getInstance(this).saveProfileImage(profileImagePath)
+ loadImageFromStorage(profileImagePath)
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ PICK_BANNER_REQUEST -> {
+ try {
+ data.data?.let {
+ val bitmap = getBitmap(contentResolver, it)
+ val profileImagePath = saveToInternalStorage(bitmap, USER_BANNER)
+ PreferenceUtil.getInstance(this).setBannerImagePath(profileImagePath)
+ loadBannerFromStorage(profileImagePath)
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ }
+ }
+
+ private fun getImagePath(aUri: Uri, aSelection: String?): String? {
+ var path: String? = null
+ val cursor = App.getContext().contentResolver.query(aUri, null, aSelection, null, null)
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ path = cursor.getString(cursor.getColumnIndex(Media.DATA))
+ }
+ cursor.close()
+ }
+ return path
+ }
+
+ private fun loadBannerFromStorage(profileImagePath: String) {
+ CoroutineScope(Dispatchers.IO).launch {
+ withContext(Dispatchers.IO) {
+ val bitmap = Compressor(this@UserInfoActivity).setQuality(100)
+ .setCompressFormat(Bitmap.CompressFormat.WEBP)
+ .compressToBitmap(File(profileImagePath, USER_BANNER))
+ withContext(Dispatchers.Main) { bannerImage.setImageBitmap(bitmap) }
+ }
+ }
+ }
+
+ private fun loadImageFromStorage(path: String) {
+ CoroutineScope(Dispatchers.IO).launch {
+ withContext(Dispatchers.IO) {
+ val bitmap = Compressor(this@UserInfoActivity)
+ .setMaxHeight(300)
+ .setMaxWidth(300)
+ .setQuality(75)
+ .setCompressFormat(Bitmap.CompressFormat.WEBP)
+ .compressToBitmap(File(path, USER_PROFILE))
+ withContext(Dispatchers.Main) { userImage.setImageBitmap(bitmap) }
+ }
+ }
+ }
+
+ private fun saveToInternalStorage(bitmapImage: Bitmap, userBanner: String): String {
+ val cw = ContextWrapper(this)
+ val directory = cw.getDir("imageDir", Context.MODE_PRIVATE)
+ val myPath = File(directory, userBanner)
+ var fos: FileOutputStream? = null
+ try {
+ fos = FileOutputStream(myPath)
+ // Use the compress method on the BitMap object to write image to the OutputStream
+ bitmapImage.compress(Bitmap.CompressFormat.WEBP, 100, fos)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ } finally {
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ return directory.absolutePath
+ }
+
+ companion object {
+
+ private const val PICK_IMAGE_REQUEST = 9002
+ private const val PICK_BANNER_REQUEST = 9004
+ private const val PROFILE_ICON_SIZE = 400
+ }
+}
+
+fun Activity.pickImage(requestCode: Int) {
+ Intent(Intent.ACTION_GET_CONTENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "image/*"
+ startActivityForResult(this, requestCode)
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewActivity.java b/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewActivity.java
new file mode 100644
index 000000000..d01c2d406
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewActivity.java
@@ -0,0 +1,98 @@
+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 androidx.core.widget.NestedScrollView;
+
+import com.google.android.material.appbar.AppBarLayout;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+
+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;
+
+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.getInstance(context).setLastChangeLogVersion(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);
+ AppBarLayout appBarLayout = findViewById(R.id.appBarLayout);
+
+ toolbar.setBackgroundColor(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface));
+ //setSupportActionBar(toolbar);
+
+ toolbar.setNavigationOnClickListener(v -> onBackPressed());
+ ToolbarContentTintHelper.colorBackButton(toolbar);
+ NestedScrollView nestedScrollView = findViewById(R.id.container);
+
+ 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("Unable to load " + e.getLocalizedMessage() + "
", "text/html", "UTF-8");
+ }
+ setChangelogRead(this);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewFragment.kt b/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewFragment.kt
deleted file mode 100644
index 48b39ed35..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewFragment.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-package code.name.monkey.retromusic.activities
-
-import android.content.Context
-import android.content.Intent
-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 android.webkit.WebResourceRequest
-import android.webkit.WebView
-import android.webkit.WebViewClient
-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")
- binding.webView.webViewClient = object : WebViewClient() {
- override fun shouldOverrideUrlLoading(
- view: WebView?,
- request: WebResourceRequest?
- ): Boolean {
- val url = request?.url ?: return false
- //you can do checks here e.g. url.host equals to target one
- startActivity(Intent(Intent.ACTION_VIEW, url))
- return true
- }
- }
- } catch (e: Throwable) {
- binding.webView.loadData(
- "Unable to load " + e.localizedMessage + "
", "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)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsBaseActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsBaseActivity.kt
index 1e0c110d5..7cd90a8b4 100644
--- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsBaseActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsBaseActivity.kt
@@ -1,39 +1,18 @@
-/*
- * 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.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 androidx.core.content.getSystemService
-import code.name.monkey.appthemehelper.util.VersionUtils
+import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.retromusic.R
-import code.name.monkey.retromusic.extensions.accentColor
-import code.name.monkey.retromusic.extensions.rootView
-import code.name.monkey.retromusic.util.logD
import com.google.android.material.snackbar.Snackbar
abstract class AbsBaseActivity : AbsThemeActivity() {
@@ -54,7 +33,7 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
}
private val snackBarContainer: View
- get() = rootView
+ get() = window.decorView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -64,12 +43,19 @@ 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 (VersionUtils.hasMarshmallow()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
onHasPermissionsChanged(hasPermissions)
}
}
@@ -77,7 +63,6 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
protected open fun onHasPermissionsChanged(hasPermissions: Boolean) {
// implemented by sub classes
- logD(hasPermissions)
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
@@ -92,15 +77,17 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
}
protected open fun requestPermissions() {
- ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ requestPermissions(permissions, PERMISSION_REQUEST)
+ }
}
protected fun hasPermissions(): Boolean {
- for (permission in permissions) {
- if (ActivityCompat.checkSelfPermission(this,
- permission) != PackageManager.PERMISSION_GRANTED
- ) {
- return false
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ for (permission in permissions) {
+ if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+ return false
+ }
}
}
return true
@@ -109,96 +96,51 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array,
- grantResults: IntArray,
+ grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSION_REQUEST) {
for (grantResult in grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
- this@AbsBaseActivity, Manifest.permission.READ_EXTERNAL_STORAGE,
- ) || ActivityCompat.shouldShowRequestPermissionRationale(
- this@AbsBaseActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ this@AbsBaseActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE
)
) {
- // User has deny from permission dialog
+ //User has deny from permission dialog
Snackbar.make(
snackBarContainer,
permissionDeniedMessage!!,
- Snackbar.LENGTH_SHORT
+ Snackbar.LENGTH_INDEFINITE
)
- .setAction(R.string.action_grant) { requestPermissions() }
- .setActionTextColor(accentColor()).show()
+ .setAction(code.name.monkey.retromusic.R.string.action_grant) { requestPermissions() }
+ .setActionTextColor(ThemeStore.accentColor(this)).show()
} else {
// User has deny permission and checked never show permission dialog so you can redirect to Application settings page
Snackbar.make(
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(accentColor()).show()
+ ).setAction(code.name.monkey.retromusic.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()
}
return
}
}
hadPermissions = true
onHasPermissionsChanged(true)
- } else if (requestCode == BLUETOOTH_PERMISSION_REQUEST) {
- for (grantResult in grantResults) {
- if (grantResult != PackageManager.PERMISSION_GRANTED) {
- 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_SHORT
- )
- .setAction(R.string.action_grant) {
- ActivityCompat.requestPermissions(this,
- arrayOf(Manifest.permission.BLUETOOTH_CONNECT),
- BLUETOOTH_PERMISSION_REQUEST)
- }
- .setActionTextColor(accentColor()).show()
- }
- }
- }
}
}
companion object {
const val PERMISSION_REQUEST = 100
- const val BLUETOOTH_PERMISSION_REQUEST = 101
- }
-
- // 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()?.hideSoftInputFromWindow(
- v.windowToken,
- 0
- )
- }
- }
- }
- return super.dispatchTouchEvent(event)
}
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsMusicServiceActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsMusicServiceActivity.kt
index f9f05c919..c545b338e 100644
--- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsMusicServiceActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsMusicServiceActivity.kt
@@ -1,49 +1,20 @@
-/*
- * 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.base
import android.Manifest
import android.content.*
import android.os.Bundle
import android.os.IBinder
-import androidx.core.content.ContextCompat
-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.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 code.name.monkey.retromusic.util.logD
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import org.koin.android.ext.android.inject
+import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
+import code.name.monkey.retromusic.service.MusicService.*
import java.lang.ref.WeakReference
+import java.util.*
-abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventListener {
+abstract class AbsMusicServiceActivity : AbsBaseActivity(), MusicServiceEventListener {
+
+ private val mMusicServiceEventListeners = ArrayList()
- private val mMusicServiceEventListeners = ArrayList()
- private val repository: RealRepository by inject()
private var serviceToken: MusicPlayerRemote.ServiceToken? = null
private var musicStateReceiver: MusicStateReceiver? = null
private var receiverRegistered: Boolean = false
@@ -72,15 +43,15 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
}
}
- fun addMusicServiceEventListener(listenerI: IMusicServiceEventListener?) {
- if (listenerI != null) {
- mMusicServiceEventListeners.add(listenerI)
+ fun addMusicServiceEventListener(listener: MusicServiceEventListener?) {
+ if (listener != null) {
+ mMusicServiceEventListeners.add(listener)
}
}
- fun removeMusicServiceEventListener(listenerI: IMusicServiceEventListener?) {
- if (listenerI != null) {
- mMusicServiceEventListeners.remove(listenerI)
+ fun removeMusicServiceEventListener(listener: MusicServiceEventListener?) {
+ if (listener != null) {
+ mMusicServiceEventListeners.remove(listener)
}
}
@@ -97,7 +68,8 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
filter.addAction(MEDIA_STORE_CHANGED)
filter.addAction(FAVORITE_STATE_CHANGED)
- ContextCompat.registerReceiver(this, musicStateReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
+ registerReceiver(musicStateReceiver, filter)
+
receiverRegistered = true
}
@@ -121,16 +93,6 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
for (listener in mMusicServiceEventListeners) {
listener.onPlayingMetaChanged()
}
- lifecycleScope.launch(Dispatchers.IO) {
- if (!PreferenceUtil.pauseHistory) {
- repository.upsertSongInHistory(MusicPlayerRemote.currentSong)
- }
- val song = repository.findSongExistInPlayCount(MusicPlayerRemote.currentSong.id)
- ?.apply { playCount += 1 }
- ?: MusicPlayerRemote.currentSong.toPlayCount()
-
- repository.upsertSongInPlayCount(song)
- }
}
override fun onQueueChanged() {
@@ -163,12 +125,6 @@ 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)
@@ -177,21 +133,14 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
true
) // just in case we need to know this at some point
sendBroadcast(intent)
- logD("sendBroadcast $hasPermissions")
}
override fun getPermissionsToRequest(): Array {
- return mutableListOf().apply {
- if (VersionUtils.hasT()) {
- add(Manifest.permission.READ_MEDIA_AUDIO)
- add(Manifest.permission.POST_NOTIFICATIONS)
- } else {
- add(Manifest.permission.READ_EXTERNAL_STORAGE)
- }
- if (!VersionUtils.hasR()) {
- add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- }
- }.toTypedArray()
+ return arrayOf(
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.BLUETOOTH
+ )
}
private class MusicStateReceiver(activity: AbsMusicServiceActivity) : BroadcastReceiver() {
@@ -203,8 +152,7 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
val activity = reference.get()
if (activity != null && action != null) {
when (action) {
- FAVORITE_STATE_CHANGED -> activity.onFavoriteStateChanged()
- META_CHANGED -> activity.onPlayingMetaChanged()
+ FAVORITE_STATE_CHANGED, META_CHANGED -> activity.onPlayingMetaChanged()
QUEUE_CHANGED -> activity.onQueueChanged()
PLAY_STATE_CHANGED -> activity.onPlayStateChanged()
REPEAT_MODE_CHANGED -> activity.onRepeatModeChanged()
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt
index 2a81ca9b4..1bec676ee 100644
--- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt
@@ -1,178 +1,86 @@
-/*
- * 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.base
-import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
-import android.content.Intent
-import android.content.SharedPreferences
-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.activity.OnBackPressedCallback
-import androidx.core.animation.doOnEnd
-import androidx.core.view.*
-import androidx.fragment.app.commit
-import code.name.monkey.appthemehelper.util.VersionUtils
-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.CAROUSEL_EFFECT
-import code.name.monkey.retromusic.CIRCLE_PLAY_BUTTON
-import code.name.monkey.retromusic.EXTRA_SONG_INFO
-import code.name.monkey.retromusic.KEEP_SCREEN_ON
-import code.name.monkey.retromusic.LIBRARY_CATEGORIES
-import code.name.monkey.retromusic.NOW_PLAYING_SCREEN_ID
+import androidx.annotation.LayoutRes
+import androidx.fragment.app.Fragment
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.appthemehelper.util.ColorUtil
+import code.name.monkey.retromusic.CustomBottomSheetBehavior
import code.name.monkey.retromusic.R
-import code.name.monkey.retromusic.SCREEN_ON_LYRICS
-import code.name.monkey.retromusic.SWIPE_ANYWHERE_NOW_PLAYING
-import code.name.monkey.retromusic.SWIPE_DOWN_DISMISS
-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_VOLUME
-import code.name.monkey.retromusic.activities.PermissionActivity
-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.extensions.hide
+import code.name.monkey.retromusic.extensions.show
+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.classic.ClassicPlayerFragment
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
import code.name.monkey.retromusic.fragments.player.cardblur.CardBlurFragment
import code.name.monkey.retromusic.fragments.player.circle.CirclePlayerFragment
-import code.name.monkey.retromusic.fragments.player.classic.ClassicPlayerFragment
import code.name.monkey.retromusic.fragments.player.color.ColorFragment
import code.name.monkey.retromusic.fragments.player.fit.FitFragment
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.peek.PeekPlayerFragment
+import code.name.monkey.retromusic.fragments.player.peak.PeakPlayerFragment
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.DensityUtil
import code.name.monkey.retromusic.util.PreferenceUtil
-import code.name.monkey.retromusic.util.ViewUtil
-import code.name.monkey.retromusic.util.logD
-import com.google.android.material.bottomnavigation.BottomNavigationView
+import code.name.monkey.retromusic.views.BottomNavigationBarTinted
import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
-import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED
-import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_DRAGGING
-import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
-import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
-import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_SETTLING
-import com.google.android.material.bottomsheet.BottomSheetBehavior.from
-import org.koin.androidx.viewmodel.ext.android.viewModel
-
+import kotlinx.android.synthetic.main.sliding_music_panel_layout.*
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
- SharedPreferences.OnSharedPreferenceChangeListener {
+ AbsPlayerFragment.Callbacks {
companion object {
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
}
- var fromNotification = false
- private var windowInsets: WindowInsetsCompat? = null
- protected val libraryViewModel by viewModel()
- private lateinit var bottomSheetBehavior: BottomSheetBehavior
- private lateinit var playerFragment: AbsPlayerFragment
+ private lateinit var bottomSheetBehavior: CustomBottomSheetBehavior
private var miniPlayerFragment: MiniPlayerFragment? = null
- private var nowPlayingScreen: NowPlayingScreen? = null
+ private var playerFragment: AbsPlayerFragment? = null
+ private var currentNowPlayingScreen: NowPlayingScreen? = null
+ private var navigationBarColor: Int = 0
private var taskColor: Int = 0
- private var paletteColor: Int = Color.WHITE
- private var navigationBarColor = 0
-
+ private var lightStatusBar: Boolean = false
+ private var lightNavigationBar: Boolean = false
+ private var navigationBarColorAnimator: ValueAnimator? = null
+ protected abstract fun createContentView(): View
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 : BottomSheetBehavior.BottomSheetCallback() {
- private val onBackPressedCallback = object : OnBackPressedCallback(true) {
- override fun handleOnBackPressed() {
- println("Handle back press ${bottomSheetBehavior.state}")
- if (!handleBackPress()) {
- remove()
- onBackPressedDispatcher.onBackPressed()
- }
+ override fun onSlide(bottomSheet: View, slideOffset: Float) {
+ setMiniPlayerAlphaProgress(slideOffset)
+ dimBackground.show()
+ dimBackground.alpha = slideOffset
}
- }
- private val bottomSheetCallbackList by lazy {
- object : BottomSheetCallback() {
+ override fun onStateChanged(bottomSheet: View, newState: Int) {
+ when (newState) {
+ BottomSheetBehavior.STATE_EXPANDED -> {
+ onPanelExpanded()
+ }
+ BottomSheetBehavior.STATE_COLLAPSED -> {
+ onPanelCollapsed()
+ dimBackground.hide()
+ }
+ else -> {
- override fun onSlide(bottomSheet: View, slideOffset: Float) {
- setMiniPlayerAlphaProgress(slideOffset)
- navigationBarColorAnimator?.cancel()
- setNavigationBarColorPreOreo(
- argbEvaluator.evaluate(
- slideOffset,
- surfaceColor(),
- navigationBarColor
- ) as Int
- )
- }
-
- override fun onStateChanged(bottomSheet: View, newState: Int) {
- onBackPressedCallback.isEnabled = newState == STATE_EXPANDED
- when (newState) {
- STATE_EXPANDED -> {
- onPanelExpanded()
- if (PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics) {
- keepScreenOn(true)
- }
- }
-
- STATE_COLLAPSED -> {
- onPanelCollapsed()
- if ((PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics) || !PreferenceUtil.isScreenOnEnabled) {
- keepScreenOn(false)
- }
- }
-
- STATE_SETTLING, STATE_DRAGGING -> {
- if (fromNotification) {
- binding.navigationView.bringToFront()
- fromNotification = false
- }
- }
-
- STATE_HIDDEN -> {
- MusicPlayerRemote.clearQueue()
- }
-
- else -> {
- logD("Do a flip")
- }
}
}
}
@@ -182,45 +90,28 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- if (!hasPermissions()) {
- startActivity(Intent(this, PermissionActivity::class.java))
- finish()
- }
- binding = SlidingMusicPanelLayoutBinding.inflate(layoutInflater)
- setContentView(binding.root)
- binding.root.setOnApplyWindowInsetsListener { _, insets ->
- windowInsets = WindowInsetsCompat.toWindowInsetsCompat(insets)
- insets
- }
+ setContentView(createContentView())
+
chooseFragmentForTheme()
setupSlidingUpPanel()
- setupBottomSheet()
- updateColor()
- if (!PreferenceUtil.materialYou) {
- binding.slidingPanel.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
- navigationView.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
- }
- navigationBarColor = surfaceColor()
+ updateTabs()
- onBackPressedDispatcher.addCallback(onBackPressedCallback)
- }
+ bottomSheetBehavior =
+ BottomSheetBehavior.from(slidingPanel) as CustomBottomSheetBehavior
- private fun setupBottomSheet() {
- bottomSheetBehavior = from(binding.slidingPanel)
- bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList)
- bottomSheetBehavior.isHideable = PreferenceUtil.swipeDownToDismiss
- bottomSheetBehavior.significantVelocityThreshold = 300
- setMiniPlayerAlphaProgress(0F)
+ val themeColor = ATHUtil.resolveColor(this, android.R.attr.windowBackground, Color.GRAY)
+ dimBackground.setBackgroundColor(ColorUtil.withAlpha(themeColor, 0.5f))
}
override fun onResume() {
super.onResume()
- PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
- if (nowPlayingScreen != PreferenceUtil.nowPlayingScreen) {
+ if (currentNowPlayingScreen != PreferenceUtil.getInstance(this).nowPlayingScreen) {
postRecreate()
}
- if (bottomSheetBehavior.state == STATE_EXPANDED) {
+ bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList)
+
+ if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED) {
setMiniPlayerAlphaProgress(1f)
}
}
@@ -228,354 +119,265 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
override fun onDestroy() {
super.onDestroy()
bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallbackList)
- PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this)
+ if (navigationBarColorAnimator != null) navigationBarColorAnimator?.cancel() // just in case
}
- override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
- when (key) {
- SWIPE_DOWN_DISMISS -> {
- bottomSheetBehavior.isHideable = PreferenceUtil.swipeDownToDismiss
- }
-
- TOGGLE_ADD_CONTROLS -> {
- miniPlayerFragment?.setUpButtons()
- }
-
- NOW_PLAYING_SCREEN_ID -> {
- chooseFragmentForTheme()
- binding.slidingPanel.updateLayoutParams {
- height = if (nowPlayingScreen != Peek) {
- ViewGroup.LayoutParams.MATCH_PARENT
- } else {
- ViewGroup.LayoutParams.WRAP_CONTENT
- }
- onServiceConnected()
- }
- }
-
- ALBUM_COVER_TRANSFORM, CAROUSEL_EFFECT,
- ALBUM_COVER_STYLE, TOGGLE_VOLUME, EXTRA_SONG_INFO, CIRCLE_PLAY_BUTTON,
- -> {
- chooseFragmentForTheme()
- onServiceConnected()
- }
-
- SWIPE_ANYWHERE_NOW_PLAYING -> {
- playerFragment.addSwipeDetector()
- }
-
- ADAPTIVE_COLOR_APP -> {
- if (PreferenceUtil.nowPlayingScreen in listOf(Normal, Material, Flat)) {
- chooseFragmentForTheme()
- onServiceConnected()
- }
- }
-
- LIBRARY_CATEGORIES -> {
- updateTabs()
- }
-
- TAB_TEXT_MODE -> {
- navigationView.labelVisibilityMode = PreferenceUtil.tabTitleMode
- }
-
- TOGGLE_FULL_SCREEN -> {
- recreate()
- }
-
- SCREEN_ON_LYRICS -> {
- keepScreenOn(bottomSheetBehavior.state == STATE_EXPANDED && PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics || PreferenceUtil.isScreenOnEnabled)
- }
-
- KEEP_SCREEN_ON -> {
- maybeSetScreenOn()
- }
- }
+ protected fun wrapSlidingMusicPanel(@LayoutRes resId: Int): View {
+ val slidingMusicPanelLayout =
+ layoutInflater.inflate(R.layout.sliding_music_panel_layout, null)
+ val contentContainer =
+ slidingMusicPanelLayout.findViewById(R.id.mainContentFrame)
+ layoutInflater.inflate(resId, contentContainer)
+ return slidingMusicPanelLayout
}
- fun collapsePanel() {
- bottomSheetBehavior.state = STATE_COLLAPSED
+ private fun collapsePanel() {
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
}
fun expandPanel() {
- bottomSheetBehavior.state = STATE_EXPANDED
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
+ setMiniPlayerAlphaProgress(1f)
}
private fun setMiniPlayerAlphaProgress(progress: Float) {
- if (progress < 0) return
+ if (miniPlayerFragment?.view == null) return
val alpha = 1 - progress
- miniPlayerFragment?.view?.alpha = 1 - (progress / 0.2F)
- miniPlayerFragment?.view?.isGone = alpha == 0f
- if (!isLandscape) {
- binding.navigationView.translationY = progress * 500
- binding.navigationView.alpha = alpha
- }
- binding.playerFragmentContainer.alpha = (progress - 0.2F) / 0.2F
- }
+ miniPlayerFragment?.view?.alpha = alpha
+ // necessary to make the views below clickable
+ miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
- 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()
- }
+ bottomNavigationView.translationY = progress * 500
+ //bottomNavigationView.alpha = alpha
}
open fun onPanelCollapsed() {
- setMiniPlayerAlphaProgress(0F)
// restore values
- animateNavigationBarColor(surfaceColor())
- setLightStatusBarAuto()
- setLightNavigationBarAuto()
- setTaskDescriptionColor(taskColor)
- //playerFragment?.onHide()
+ super.setLightStatusbar(lightStatusBar)
+ super.setTaskDescriptionColor(taskColor)
+ super.setNavigationbarColor(navigationBarColor)
+ super.setLightNavigationBar(lightNavigationBar)
+
+
+ playerFragment?.setMenuVisibility(false)
+ playerFragment?.userVisibleHint = false
+ playerFragment?.onHide()
}
open fun onPanelExpanded() {
- setMiniPlayerAlphaProgress(1F)
+ val playerFragmentColor = playerFragment!!.paletteColor
+ super.setTaskDescriptionColor(playerFragmentColor)
+
+ playerFragment?.setMenuVisibility(true)
+ playerFragment?.userVisibleHint = true
+ playerFragment?.onShow()
onPaletteColorChanged()
- //playerFragment?.onShow()
}
private fun setupSlidingUpPanel() {
- binding.slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
+ slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
- binding.slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
- if (nowPlayingScreen != Peek) {
- binding.slidingPanel.updateLayoutParams {
- height = ViewGroup.LayoutParams.MATCH_PARENT
- }
+ slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
+ if (currentNowPlayingScreen != PEAK) {
+ val params = slidingPanel.layoutParams as ViewGroup.LayoutParams
+ params.height = ViewGroup.LayoutParams.MATCH_PARENT
+ slidingPanel.layoutParams = params
}
when (panelState) {
- STATE_EXPANDED -> onPanelExpanded()
- STATE_COLLAPSED -> onPanelCollapsed()
- else -> {
- // playerFragment!!.onHide()
- }
+ BottomSheetBehavior.STATE_EXPANDED -> onPanelExpanded()
+ BottomSheetBehavior.STATE_COLLAPSED -> onPanelCollapsed()
+ else -> playerFragment!!.onHide()
}
}
})
}
- val navigationView get() = binding.navigationView
+ fun toggleBottomNavigationView(toggle: Boolean) {
+ bottomNavigationView.visibility = if (toggle) View.GONE else View.VISIBLE
+ }
- val slidingPanel get() = binding.slidingPanel
+ fun getBottomNavigationView(): BottomNavigationBarTinted {
+ return bottomNavigationView
+ }
- val isBottomNavVisible get() = navigationView.isVisible && navigationView is BottomNavigationView
+ private fun hideBottomBar(hide: Boolean) {
+ val heightOfBar = resources.getDimensionPixelSize(R.dimen.mini_player_height)
+ val heightOfBarWithTabs =
+ resources.getDimensionPixelSize(R.dimen.mini_player_height_expanded)
+
+ if (hide) {
+ bottomSheetBehavior.isHideable = true
+ bottomSheetBehavior.peekHeight = 0
+ collapsePanel()
+ bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat()
+ } else {
+ if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
+ slidingPanel.elevation = DensityUtil.dip2px(this, 10f).toFloat()
+ bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat()
+ bottomSheetBehavior.isHideable = false
+ bottomSheetBehavior.peekHeight =
+ if (bottomNavigationView.visibility == View.VISIBLE) heightOfBarWithTabs else heightOfBar
+ }
+ }
+ }
+
+ fun setBottomBarVisibility(gone: Int) {
+ bottomNavigationView.visibility = gone
+ hideBottomBar(false)
+ }
+
+ private fun chooseFragmentForTheme() {
+ currentNowPlayingScreen = PreferenceUtil.getInstance(this).nowPlayingScreen
+
+ val fragment: Fragment = when (currentNowPlayingScreen) {
+ BLUR -> BlurPlayerFragment()
+ ADAPTIVE -> AdaptiveFragment()
+ NORMAL -> PlayerFragment()
+ CARD -> CardFragment()
+ BLUR_CARD -> CardBlurFragment()
+ FIT -> FitFragment()
+ FLAT -> FlatPlayerFragment()
+ FULL -> FullPlayerFragment()
+ PLAIN -> PlainPlayerFragment()
+ SIMPLE -> SimplePlayerFragment()
+ MATERIAL -> MaterialFragment()
+ COLOR -> ColorFragment()
+ TINY -> TinyPlayerFragment()
+ PEAK -> PeakPlayerFragment()
+ CIRCLE -> CirclePlayerFragment()
+ EXAMPLE -> ClassicPlayerFragment()
+ else -> PlayerFragment()
+ } // must implement AbsPlayerFragment
+ supportFragmentManager.beginTransaction().replace(R.id.playerFragmentContainer, fragment)
+ .commit()
+ supportFragmentManager.executePendingTransactions()
+
+ playerFragment =
+ supportFragmentManager.findFragmentById(R.id.playerFragmentContainer) as AbsPlayerFragment
+ miniPlayerFragment =
+ supportFragmentManager.findFragmentById(R.id.miniPlayerFragment) as MiniPlayerFragment
+ miniPlayerFragment?.view?.setOnClickListener { expandPanel() }
+ }
override fun onServiceConnected() {
super.onServiceConnected()
- hideBottomSheet(false)
+ if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
+ slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
+ ViewTreeObserver.OnGlobalLayoutListener {
+ override fun onGlobalLayout() {
+ slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
+ hideBottomBar(false)
+ }
+ })
+ } // don't call hideBottomBar(true) here as it causes a bug with the SlidingUpPanelLayout
}
override fun onQueueChanged() {
super.onQueueChanged()
- // 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())
- }
+ hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
}
- private fun handleBackPress(): Boolean {
- if (panelState == STATE_EXPANDED) {
+ override fun onBackPressed() {
+ if (!handleBackPress()) super.onBackPressed()
+ }
+
+ open fun handleBackPress(): Boolean {
+ if (bottomSheetBehavior.peekHeight != 0 && playerFragment!!.onBackPressed()) return true
+ if (panelState == BottomSheetBehavior.STATE_EXPANDED) {
collapsePanel()
return true
}
return false
}
- private fun onPaletteColorChanged() {
- if (panelState == STATE_EXPANDED) {
- 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) {
- animateNavigationBarColor(Color.BLACK)
- navigationBarColor = Color.BLACK
- setLightStatusBar(false)
- setLightNavigationBar(true)
- } else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) {
- animateNavigationBarColor(paletteColor)
- navigationBarColor = paletteColor
- setLightNavigationBar(isColorLight)
- setLightStatusBar(isColorLight)
- } else if (nowPlayingScreen == Full) {
- animateNavigationBarColor(paletteColor)
- navigationBarColor = paletteColor
- setLightNavigationBar(isColorLight)
- setLightStatusBar(false)
- } else if (nowPlayingScreen == Classic) {
- setLightStatusBar(false)
- } else if (nowPlayingScreen == Fit) {
- setLightStatusBar(false)
+ override fun onPaletteColorChanged() {
+ if (panelState == BottomSheetBehavior.STATE_EXPANDED) {
+ val paletteColor = playerFragment!!.paletteColor
+ super.setTaskDescriptionColor(paletteColor)
+
+ val isColorLight = ColorUtil.isColorLight(paletteColor)
+
+ if (PreferenceUtil.getInstance(this).adaptiveColor && (currentNowPlayingScreen == NORMAL || currentNowPlayingScreen == FLAT)) {
+ super.setLightNavigationBar(true)
+ super.setLightStatusbar(isColorLight)
+ } else if (currentNowPlayingScreen == FULL || currentNowPlayingScreen == CARD || currentNowPlayingScreen == BLUR || currentNowPlayingScreen == BLUR_CARD) {
+ super.setLightStatusbar(false)
+ super.setLightNavigationBar(true)
+ super.setNavigationbarColor(Color.BLACK)
+ } else if (currentNowPlayingScreen == COLOR || currentNowPlayingScreen == TINY) {
+ super.setNavigationbarColor(paletteColor)
+ super.setLightNavigationBar(isColorLight)
+ super.setLightStatusbar(isColorLight)
+ } else if (currentNowPlayingScreen == FIT) {
+ super.setLightStatusbar(false)
+ } else {
+ super.setLightStatusbar(
+ ColorUtil.isColorLight(
+ ATHUtil.resolveColor(
+ this,
+ android.R.attr.windowBackground
+ )
+ )
+ )
+ super.setLightNavigationBar(true)
}
}
}
- private fun setTaskDescColor(color: Int) {
- taskColor = color
- if (panelState == STATE_COLLAPSED) {
- setTaskDescriptionColor(color)
+ override fun setLightStatusbar(enabled: Boolean) {
+ lightStatusBar = enabled
+ if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
+ super.setLightStatusbar(enabled)
}
}
- fun updateTabs() {
- binding.navigationView.menu.clear()
- val currentTabs: List = PreferenceUtil.libraryCategory
+ override fun setLightNavigationBar(enabled: Boolean) {
+ lightNavigationBar = enabled
+ if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
+ super.setLightNavigationBar(enabled)
+ }
+ }
+
+ override fun setNavigationbarColor(color: Int) {
+ navigationBarColor = color
+ if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
+ if (navigationBarColorAnimator != null) navigationBarColorAnimator!!.cancel()
+ super.setNavigationbarColor(color)
+ }
+ }
+
+ override fun setTaskDescriptionColor(color: Int) {
+ taskColor = color
+ if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
+ super.setTaskDescriptionColor(color)
+ }
+ }
+
+ private fun updateTabs() {
+ bottomNavigationView.menu.clear()
+ val currentTabs: List = PreferenceUtil.getInstance(this).libraryCategoryInfos
for (tab in currentTabs) {
if (tab.visible) {
val menu = tab.category
- binding.navigationView.menu.add(0, menu.id, 0, menu.stringRes)
- .setIcon(menu.icon)
+ bottomNavigationView.menu.add(0, menu.id, 0, menu.stringRes).setIcon(menu.icon)
}
}
- if (binding.navigationView.menu.size() == 1) {
- isInOneTabMode = true
- binding.navigationView.isVisible = false
- } else {
- isInOneTabMode = false
+ if (currentTabs.size <= 1) {
+ toggleBottomNavigationView(true)
}
}
- private fun updateColor() {
- libraryViewModel.paletteColor.observe(this) { color ->
- this.paletteColor = color
- onPaletteColorChanged()
- }
- }
-
- fun setBottomNavVisibility(
- visible: Boolean,
- animate: Boolean = false,
- hideBottomSheet: Boolean = MusicPlayerRemote.playingQueue.isEmpty(),
- ) {
- if (!ViewCompat.isLaidOut(navigationView)) {
- return
- }
- if (isInOneTabMode) {
- hideBottomSheet(
- hide = hideBottomSheet,
- animate = animate,
- isBottomNavVisible = false
- )
- return
- }
- if (visible xor navigationView.isVisible) {
- val mAnimate = animate && bottomSheetBehavior.state == STATE_COLLAPSED
- if (mAnimate) {
- if (visible) {
- binding.navigationView.bringToFront()
- binding.navigationView.show()
- } else {
- binding.navigationView.hide()
- }
- } else {
- binding.navigationView.isVisible = visible
- if (visible && bottomSheetBehavior.state != STATE_EXPANDED) {
- binding.navigationView.bringToFront()
+ /*override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
+ if (ev?.action == MotionEvent.ACTION_DOWN) {
+ if (panelState == BottomSheetBehavior.STATE_EXPANDED) {
+ val outRect = Rect()
+ slidingPanel.getGlobalVisibleRect(outRect)
+ if (!outRect.contains(ev.rawX.toInt(), ev.rawY.toInt())) {
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
}
}
}
- hideBottomSheet(
- hide = hideBottomSheet,
- animate = animate,
- isBottomNavVisible = visible && navigationView is BottomNavigationView
- )
- }
-
- fun hideBottomSheet(
- hide: Boolean,
- animate: Boolean = false,
- isBottomNavVisible: Boolean = navigationView.isVisible && navigationView is BottomNavigationView,
- ) {
- val heightOfBar = windowInsets.getBottomInsets() + dip(R.dimen.mini_player_height)
- val heightOfBarWithTabs = heightOfBar + dip(R.dimen.bottom_nav_height)
- if (hide) {
- bottomSheetBehavior.peekHeight = -windowInsets.getBottomInsets()
- bottomSheetBehavior.state = STATE_COLLAPSED
- libraryViewModel.setFabMargin(
- this,
- if (isBottomNavVisible) dip(R.dimen.bottom_nav_height) else 0
- )
- } else {
- if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
- binding.slidingPanel.elevation = 0F
- binding.navigationView.elevation = 5F
- if (isBottomNavVisible) {
- logD("List")
- if (animate) {
- bottomSheetBehavior.peekHeightAnimate(heightOfBarWithTabs)
- } else {
- bottomSheetBehavior.peekHeight = heightOfBarWithTabs
- }
- libraryViewModel.setFabMargin(
- this,
- dip(R.dimen.bottom_nav_mini_player_height)
- )
- } else {
- logD("Details")
- 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
-
- val fragment: AbsPlayerFragment = when (nowPlayingScreen) {
- Blur -> BlurPlayerFragment()
- Adaptive -> AdaptiveFragment()
- Normal -> PlayerFragment()
- Card -> CardFragment()
- BlurCard -> CardBlurFragment()
- Fit -> FitFragment()
- Flat -> FlatPlayerFragment()
- Full -> FullPlayerFragment()
- Plain -> PlainPlayerFragment()
- Simple -> SimplePlayerFragment()
- Material -> MaterialFragment()
- Color -> ColorFragment()
- Gradient -> GradientPlayerFragment()
- Tiny -> TinyPlayerFragment()
- Peek -> PeekPlayerFragment()
- Circle -> CirclePlayerFragment()
- Classic -> ClassicPlayerFragment()
- MD3 -> MD3PlayerFragment()
- else -> PlayerFragment()
- } // must extend AbsPlayerFragment
- supportFragmentManager.commit {
- replace(R.id.playerFragmentContainer, fragment)
- }
- supportFragmentManager.executePendingTransactions()
- playerFragment = whichFragment(R.id.playerFragmentContainer)
- miniPlayerFragment = whichFragment(R.id.miniPlayerFragment)
- miniPlayerFragment?.view?.setOnClickListener { expandPanel() }
- }
-}
+ return super.dispatchTouchEvent(ev)
+ }*/
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsThemeActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsThemeActivity.kt
index 32b025f01..14550e5ef 100644
--- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsThemeActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsThemeActivity.kt
@@ -1,69 +1,54 @@
-/*
- * 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.base
import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.Drawable
import android.os.Bundle
import android.os.Handler
-import android.os.Looper
import android.view.KeyEvent
-import androidx.appcompat.app.AppCompatDelegate
+import android.view.View
+import android.view.WindowManager
+import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
-import androidx.core.os.LocaleListCompat
+import androidx.core.content.ContextCompat
+import code.name.monkey.appthemehelper.ATH
+import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.appthemehelper.util.ColorUtil
+import code.name.monkey.appthemehelper.util.TintHelper
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.theme.getNightMode
-import code.name.monkey.retromusic.util.theme.getThemeResValue
+import code.name.monkey.retromusic.util.RetroUtil
+import code.name.monkey.retromusic.util.theme.ThemeManager
+import java.util.*
abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
- private val handler = Handler(Looper.getMainLooper())
+ private val handler = Handler()
override fun onCreate(savedInstanceState: Bundle?) {
- updateLocale()
updateTheme()
hideStatusBar()
super.onCreate(savedInstanceState)
- setEdgeToEdgeOrImmersive()
- maybeSetScreenOn()
- setLightNavigationBarAuto()
- setLightStatusBarAuto(surfaceColor())
- if (VersionUtils.hasQ()) {
- window.decorView.isForceDarkAllowed = false
- }
+ setImmersiveFullscreen()
+ registerSystemUiVisibility()
+ toggleScreenOn()
}
+
private fun updateTheme() {
- setTheme(getThemeResValue())
- if (PreferenceUtil.materialYou) {
- setDefaultNightMode(getNightMode())
- }
-
- if (PreferenceUtil.isCustomFont) {
- setTheme(R.style.FontThemeOverlay)
- }
+ setTheme(ThemeManager.getThemeResValue(this))
+ setDefaultNightMode(ThemeManager.getNightMode(this))
}
- private fun updateLocale() {
- val localeCode = PreferenceUtil.languageCode
- if (PreferenceUtil.isLocaleAutoStorageEnabled) {
- AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(localeCode))
- PreferenceUtil.isLocaleAutoStorageEnabled = true
+ private fun toggleScreenOn() {
+ if (PreferenceUtil.getInstance(this).isScreenOnEnabled) {
+ window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ } else {
+ window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
@@ -78,6 +63,131 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
}
}
+ fun hideStatusBar() {
+ hideStatusBar(PreferenceUtil.getInstance(this).fullScreenMode)
+ }
+
+ private fun hideStatusBar(fullscreen: Boolean) {
+ val statusBar = window.decorView.rootView.findViewById(R.id.status_bar)
+ if (statusBar != null) {
+ statusBar.visibility = if (fullscreen) View.GONE else View.VISIBLE
+ }
+ }
+
+ private fun changeBackgroundShape() {
+ var background: Drawable? = if (PreferenceUtil.getInstance(this).isRoundCorners)
+ ContextCompat.getDrawable(this, R.drawable.round_window)
+ else ContextCompat.getDrawable(this, R.drawable.square_window)
+ background =
+ TintHelper.createTintedDrawable(
+ background,
+ ATHUtil.resolveColor(this, android.R.attr.windowBackground)
+ )
+ window.setBackgroundDrawable(background)
+ }
+
+ 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(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 ->
+ if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
+ setImmersiveFullscreen()
+ }
+ }
+ }
+
+ private fun unregisterSystemUiVisibility() {
+ val decorView = window.decorView
+ 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.getInstance(this).fullScreenMode) {
+ window.decorView.systemUiVisibility = flags
+ }
+ }
+
+ private fun exitFullscreen() {
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
+ }
+
override fun run() {
setImmersiveFullscreen()
}
@@ -89,6 +199,7 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
public override fun onDestroy() {
super.onDestroy()
+ unregisterSystemUiVisibility()
exitFullscreen()
}
@@ -101,6 +212,9 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
}
override fun attachBaseContext(newBase: Context?) {
- super.attachBaseContext(newBase)
+ val code = PreferenceUtil.getInstance(newBase).languageCode
+ if (code != "auto") {
+ super.attachBaseContext(LanguageContextWrapper.wrap(newBase, Locale(code)))
+ } else super.attachBaseContext(newBase)
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/BugReportActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/BugReportActivity.kt
index e99881953..4ee68b9b7 100644
--- a/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/BugReportActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/BugReportActivity.kt
@@ -1,91 +1,331 @@
-/*
- * 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.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 androidx.core.content.getSystemService
-import androidx.core.net.toUri
+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 code.name.monkey.appthemehelper.util.MaterialUtil
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
import code.name.monkey.retromusic.activities.bugreport.model.DeviceInfo
-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 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 com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.callbacks.onCancel
+import com.google.android.material.floatingactionbutton.FloatingActionButton
+import com.google.android.material.textfield.TextInputLayout
+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 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"
+private const val RESULT_INVALID_TOKEN = "RESULT_INVALID_TOKEN"
+private const val RESULT_ISSUES_NOT_ENABLED = "RESULT_ISSUES_NOT_ENABLED"
+private const val RESULT_UNKNOWN = "RESULT_UNKNOWN"
+
+@StringDef(
+ RESULT_SUCCESS,
+ RESULT_BAD_CREDENTIALS,
+ RESULT_INVALID_TOKEN,
+ RESULT_ISSUES_NOT_ENABLED,
+ RESULT_UNKNOWN
+)
+@Retention(AnnotationRetention.SOURCE)
+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)
- binding = ActivityBugReportBinding.inflate(layoutInflater)
- setContentView(binding.root)
+ setContentView(R.layout.activity_bug_report)
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
initViews()
- if (title.isNullOrEmpty()) setTitle(R.string.report_an_issue)
+ if (TextUtils.isEmpty(title)) setTitle(R.string.report_an_issue)
deviceInfo = DeviceInfo(this)
- binding.cardDeviceInfo.airTextDeviceInfo.text = deviceInfo.toString()
+ airTextDeviceInfo.text = deviceInfo.toString()
}
private fun initViews() {
- val accentColor = accentColor()
- setSupportActionBar(binding.toolbar)
- ToolbarContentTintHelper.colorBackButton(binding.toolbar)
+ val accentColor = ThemeStore.accentColor(this)
+ val primaryColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
+ toolbar.setBackgroundColor(primaryColor)
+ setSupportActionBar(toolbar)
+ ToolbarContentTintHelper.colorBackButton(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ TintHelper.setTintAuto(optionUseAccount, accentColor, false)
+ optionUseAccount?.setOnClickListener {
+ inputTitle.isEnabled = true
+ inputDescription.isEnabled = true
+ inputUsername.isEnabled = true
+ inputPassword.isEnabled = true
- binding.cardDeviceInfo.airTextDeviceInfo.setOnClickListener { copyDeviceInfoToClipBoard() }
+ optionAnonymous.isChecked = false
+ sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
+ override fun onHidden(fab: FloatingActionButton?) {
+ super.onHidden(fab)
+ sendFab.setImageResource(R.drawable.ic_send_white_24dp)
+ sendFab.show()
+ }
+ })
+ }
+ TintHelper.setTintAuto(optionAnonymous, accentColor, false)
+ optionAnonymous.setOnClickListener {
+ inputTitle.isEnabled = false
+ inputDescription.isEnabled = false
+ inputUsername.isEnabled = false
+ inputPassword.isEnabled = false
- TintHelper.setTintAuto(binding.sendFab, accentColor, true)
- binding.sendFab.setOnClickListener { reportIssue() }
+ optionUseAccount.isChecked = false
+ sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
+ override fun onHidden(fab: FloatingActionButton?) {
+ super.onHidden(fab)
+ sendFab.setImageResource(R.drawable.ic_open_in_browser_white_24dp)
+ sendFab.show()
+ }
+ })
+ }
+
+ inputPassword.setOnEditorActionListener { _, actionId, _ ->
+ if (actionId == EditorInfo.IME_ACTION_SEND) {
+ reportIssue()
+ return@setOnEditorActionListener true
+ }
+ false
+ }
+
+ airTextDeviceInfo.setOnClickListener { copyDeviceInfoToClipBoard() }
+
+ TintHelper.setTintAuto(sendFab, accentColor, true)
+ sendFab.setOnClickListener { reportIssue() }
+
+ MaterialUtil.setTint(inputLayoutTitle, false)
+ MaterialUtil.setTint(inputLayoutDescription, false)
+ MaterialUtil.setTint(inputLayoutUsername, false)
+ MaterialUtil.setTint(inputLayoutPassword, false)
}
private fun reportIssue() {
- copyDeviceInfoToClipBoard()
- val i = Intent(Intent.ACTION_VIEW)
- i.data = ISSUE_TRACKER_LINK.toUri()
- i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
- startActivity(i)
+ if (optionUseAccount.isChecked) {
+ if (!validateInput()) return
+ val username = inputUsername.text.toString()
+ val password = inputPassword.text.toString()
+ sendBugReport(GithubLogin(username, password))
+ } else {
+ copyDeviceInfoToClipBoard()
+
+ val i = Intent(Intent.ACTION_VIEW)
+ i.data = Uri.parse(ISSUE_TRACKER_LINK)
+ i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ startActivity(i)
+ }
}
private fun copyDeviceInfoToClipBoard() {
- val clipboard = getSystemService()
+ val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(getString(R.string.device_info), deviceInfo?.toMarkdown())
- clipboard?.setPrimaryClip(clip)
- showToast(R.string.copied_device_info_to_clipboard)
+ clipboard.setPrimaryClip(clip)
+ Toast.makeText(
+ this@BugReportActivity,
+ R.string.copied_device_info_to_clipboard,
+ Toast.LENGTH_LONG
+ ).show()
}
+ private fun validateInput(): Boolean {
+ var hasErrors = false
+
+ if (optionUseAccount.isChecked) {
+ if (TextUtils.isEmpty(inputUsername.text)) {
+ setError(inputLayoutUsername, R.string.bug_report_no_username)
+ hasErrors = true
+ } else {
+ removeError(inputLayoutUsername)
+ }
+
+ if (TextUtils.isEmpty(inputPassword.text)) {
+ setError(inputLayoutPassword, R.string.bug_report_no_password)
+ hasErrors = true
+ } else {
+ removeError(inputLayoutPassword)
+ }
+ }
+
+ if (TextUtils.isEmpty(inputTitle.text)) {
+ setError(inputLayoutTitle, R.string.bug_report_no_title)
+ hasErrors = true
+ } else {
+ removeError(inputLayoutTitle)
+ }
+
+ if (TextUtils.isEmpty(inputDescription.text)) {
+ setError(inputLayoutDescription, R.string.bug_report_no_description)
+ hasErrors = true
+ } else {
+ removeError(inputLayoutDescription)
+ }
+
+ return !hasErrors
+ }
+
+ private fun setError(editTextLayout: TextInputLayout, @StringRes errorRes: Int) {
+ editTextLayout.error = getString(errorRes)
+ }
+
+ private fun removeError(editTextLayout: TextInputLayout) {
+ editTextLayout.error = null
+ }
+
+ private fun sendBugReport(login: GithubLogin) {
+ if (!validateInput()) return
+
+ val bugTitle = inputTitle.text.toString()
+ val bugDescription = inputDescription.text.toString()
+
+ val extraInfo = ExtraInfo()
+ onSaveExtraInfo()
+
+ val report = Report(bugTitle, bugDescription, deviceInfo, extraInfo)
+ val target = GithubTarget("h4h13", "RetroMusicPlayer")
+
+ ReportIssueAsyncTask.report(this, report, target, login)
+ }
+
+ private fun onSaveExtraInfo() {}
+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
- onBackPressedDispatcher.onBackPressed()
+ onBackPressed()
}
return super.onOptionsItemSelected(item)
}
+ private class ReportIssueAsyncTask private constructor(
+ activity: Activity,
+ private val report: Report,
+ private val target: GithubTarget,
+ private val login: GithubLogin
+ ) : DialogAsyncTask(activity) {
+
+ override fun createDialog(context: Context): Dialog {
+ return AlertDialog.Builder(context).show()
+ }
+
+ @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.description)
+ try {
+ IssueService(client).createIssue(target.username, target.repository, issue)
+ return RESULT_SUCCESS
+ } catch (e: RequestException) {
+ return 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
+ }
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ return RESULT_UNKNOWN
+ }
+ }
+
+ override fun onPostExecute(@Result result: String) {
+ super.onPostExecute(result)
+
+ val context = context ?: return
+
+ when (result) {
+ RESULT_SUCCESS -> tryToFinishActivity()
+ RESULT_BAD_CREDENTIALS -> MaterialDialog(context).show {
+ title(R.string.bug_report_failed)
+ message(R.string.bug_report_failed_wrong_credentials)
+ positiveButton(android.R.string.ok)
+ }
+ RESULT_INVALID_TOKEN -> MaterialDialog(context).show {
+ title(R.string.bug_report_failed)
+ message(R.string.bug_report_failed_invalid_token)
+ positiveButton(android.R.string.ok)
+ }
+ RESULT_ISSUES_NOT_ENABLED -> MaterialDialog(context).show {
+ title(R.string.bug_report_failed)
+ message(R.string.bug_report_failed_issues_not_available)
+ positiveButton(android.R.string.ok)
+ }
+ else -> MaterialDialog(context).show {
+ title(R.string.bug_report_failed)
+ message(R.string.bug_report_failed_unknown)
+ positiveButton(android.R.string.ok) { tryToFinishActivity() }
+ onCancel { 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()
+ }
+ }
+ }
+
companion object {
- private const val ISSUE_TRACKER_LINK =
- "https://github.com/MuntashirAkon/Metro/issues/new"
+
+ 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"
}
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/DeviceInfo.java b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/DeviceInfo.java
new file mode 100644
index 000000000..5a2661277
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/DeviceInfo.java
@@ -0,0 +1,130 @@
+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 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(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.getInstance(context).getBaseTheme();
+ nowPlayingTheme = context.getString(PreferenceUtil.getInstance(context).getNowPlayingScreen().getTitleRes());
+ isAdaptive = PreferenceUtil.getInstance(context).getAdaptiveColor();
+ selectedLang = PreferenceUtil.getInstance(context).getLanguageCode();
+ }
+
+ public String toMarkdown() {
+ return "Device info:\n"
+ + "---\n"
+ + "\n"
+ + "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"
+ + "Language " + selectedLang + " \n"
+ + "
\n";
+ }
+
+ @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;
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/DeviceInfo.kt b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/DeviceInfo.kt
deleted file mode 100644
index 19aff73a2..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/DeviceInfo.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-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.appcompat.app.AppCompatDelegate
-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.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:
- ---
-
- 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)}
- Language $selectedLang
-
-
- """.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 = AppCompatDelegate.getApplicationLocales().toLanguageTags()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/Report.java b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/Report.java
new file mode 100644
index 000000000..1da9313bb
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/Report.java
@@ -0,0 +1,33 @@
+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;
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/github/ExtraInfo.java b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/github/ExtraInfo.java
new file mode 100644
index 000000000..ec27388cb
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/github/ExtraInfo.java
@@ -0,0 +1,62 @@
+package code.name.monkey.retromusic.activities.bugreport.model.github;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class ExtraInfo {
+
+ private final Map 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"
+ + "\n");
+ for (String key : extraInfo.keySet()) {
+ output.append("")
+ .append(key)
+ .append(" ")
+ .append(extraInfo.get(key))
+ .append(" \n");
+ }
+ output.append("
\n");
+
+ return output.toString();
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/github/GithubLogin.java b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/github/GithubLogin.java
new file mode 100644
index 000000000..e388249cb
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/github/GithubLogin.java
@@ -0,0 +1,41 @@
+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);
+ }
+
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/github/GithubTarget.java b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/github/GithubTarget.java
new file mode 100644
index 000000000..21126d30c
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/model/github/GithubTarget.java
@@ -0,0 +1,21 @@
+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;
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/saf/SAFGuideActivity.java b/app/src/main/java/code/name/monkey/retromusic/activities/saf/SAFGuideActivity.java
index efd56210e..a5f2d9083 100644
--- a/app/src/main/java/code/name/monkey/retromusic/activities/saf/SAFGuideActivity.java
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/saf/SAFGuideActivity.java
@@ -24,53 +24,49 @@ import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
import code.name.monkey.retromusic.R;
-/** Created by hemanths on 2019-07-31. */
+/**
+ * Created by hemanths on 2019-07-31.
+ */
public class SAFGuideActivity extends IntroActivity {
- public static final int REQUEST_CODE_SAF_GUIDE = 98;
+ public static final int REQUEST_CODE_SAF_GUIDE = 98;
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
- setButtonCtaVisible(false);
- setButtonNextVisible(false);
- setButtonBackVisible(false);
+ setButtonCtaVisible(false);
+ setButtonNextVisible(false);
+ setButtonBackVisible(false);
- setButtonCtaTintMode(BUTTON_CTA_TINT_MODE_TEXT);
+ setButtonCtaTintMode(BUTTON_CTA_TINT_MODE_TEXT);
- String title =
- String.format(getString(R.string.saf_guide_slide1_title), getString(R.string.app_name));
+ String title = String.format(getString(R.string.saf_guide_slide1_title), getString(R.string.app_name));
- addSlide(
- new SimpleSlide.Builder()
- .title(title)
- .description(
- Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
- ? R.string.saf_guide_slide1_description_before_o
- : R.string.saf_guide_slide1_description)
- .image(R.drawable.saf_guide_1)
- .background(code.name.monkey.appthemehelper.R.color.md_deep_purple_300)
- .backgroundDark(code.name.monkey.appthemehelper.R.color.md_deep_purple_400)
- .layout(R.layout.fragment_simple_slide_large_image)
- .build());
- addSlide(
- new SimpleSlide.Builder()
- .title(R.string.saf_guide_slide2_title)
- .description(R.string.saf_guide_slide2_description)
- .image(R.drawable.saf_guide_2)
- .background(code.name.monkey.appthemehelper.R.color.md_deep_purple_500)
- .backgroundDark(code.name.monkey.appthemehelper.R.color.md_deep_purple_600)
- .layout(R.layout.fragment_simple_slide_large_image)
- .build());
- addSlide(
- new SimpleSlide.Builder()
- .title(R.string.saf_guide_slide3_title)
- .description(R.string.saf_guide_slide3_description)
- .image(R.drawable.saf_guide_3)
- .background(code.name.monkey.appthemehelper.R.color.md_deep_purple_700)
- .backgroundDark(code.name.monkey.appthemehelper.R.color.md_deep_purple_800)
- .layout(R.layout.fragment_simple_slide_large_image)
- .build());
- }
+ addSlide(new SimpleSlide.Builder()
+ .title(title)
+ .description(Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
+ ? R.string.saf_guide_slide1_description_before_o : R.string.saf_guide_slide1_description)
+ .image(R.drawable.saf_guide_1)
+ .background(R.color.md_deep_purple_300)
+ .backgroundDark(R.color.md_deep_purple_400)
+ .layout(R.layout.fragment_simple_slide_large_image)
+ .build());
+ addSlide(new SimpleSlide.Builder()
+ .title(R.string.saf_guide_slide2_title)
+ .description(R.string.saf_guide_slide2_description)
+ .image(R.drawable.saf_guide_2)
+ .background(R.color.md_deep_purple_500)
+ .backgroundDark(R.color.md_deep_purple_600)
+ .layout(R.layout.fragment_simple_slide_large_image)
+ .build());
+ addSlide(new SimpleSlide.Builder()
+ .title(R.string.saf_guide_slide3_title)
+ .description(R.string.saf_guide_slide3_description)
+ .image(R.drawable.saf_guide_3)
+ .background(R.color.md_deep_purple_700)
+ .backgroundDark(R.color.md_deep_purple_800)
+ .layout(R.layout.fragment_simple_slide_large_image)
+ .build());
+ }
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/saf/SAFRequestActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/saf/SAFRequestActivity.kt
deleted file mode 100644
index 1b389b9d2..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/activities/saf/SAFRequestActivity.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2021 Bartlomiej Uliasz.
- *
- * 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.saf
-
-import android.app.Activity
-import android.content.Intent
-import android.os.Bundle
-import code.name.monkey.retromusic.activities.saf.SAFGuideActivity.REQUEST_CODE_SAF_GUIDE
-import code.name.monkey.retromusic.util.SAFUtil
-
-/** Created by buliasz on 2021-02-07. */
-class SAFRequestActivity : Activity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val intent = Intent(this, code.name.monkey.retromusic.activities.saf.SAFGuideActivity::class.java)
- startActivityForResult(intent, REQUEST_CODE_SAF_GUIDE)
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
- super.onActivityResult(requestCode, resultCode, intent)
- when (requestCode) {
- REQUEST_CODE_SAF_GUIDE -> {
- SAFUtil.openTreePicker(this)
- }
- SAFUtil.REQUEST_SAF_PICK_TREE -> {
- if (resultCode == RESULT_OK) {
- SAFUtil.saveTreeUri(this, intent)
- }
- finish()
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt
index 3a9ca4e4b..3964d6660 100755
--- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt
@@ -1,105 +1,75 @@
-/*
- * 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.tageditor
import android.app.Activity
import android.app.SearchManager
import android.content.Intent
+import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
+import android.os.Build
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.PickVisualMediaRequest
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AlertDialog
-import androidx.lifecycle.lifecycleScope
-import androidx.viewbinding.ViewBinding
-import code.name.monkey.appthemehelper.util.VersionUtils
+import code.name.monkey.appthemehelper.ThemeStore
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.appthemehelper.util.ColorUtil
+import code.name.monkey.appthemehelper.util.MaterialValueHelper
+import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.R.drawable
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
-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.AudioTagInfo
-import code.name.monkey.retromusic.repository.Repository
-import code.name.monkey.retromusic.util.logD
-import code.name.monkey.retromusic.util.logE
+import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
+import code.name.monkey.retromusic.util.PreferenceUtil
+import code.name.monkey.retromusic.util.RetroUtil
+import code.name.monkey.retromusic.util.SAFUtil
+import com.afollestad.materialdialogs.LayoutMode
+import com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.bottomsheets.BottomSheet
+import com.afollestad.materialdialogs.list.listItems
import com.google.android.material.button.MaterialButton
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
+import kotlinx.android.synthetic.main.activity_album_tag_editor.*
import org.jaudiotagger.audio.AudioFile
import org.jaudiotagger.audio.AudioFileIO
import org.jaudiotagger.tag.FieldKey
-import org.koin.android.ext.android.inject
import java.io.File
+import java.util.*
-abstract class AbsTagEditorActivity : AbsBaseActivity() {
- abstract val editorImage: ImageView
- val repository by inject()
+abstract class AbsTagEditorActivity : AbsBaseActivity() {
- lateinit var saveFab: MaterialButton
- protected var id: Long = 0
+ protected var id: Int = 0
private set
private var paletteColorPrimary: Int = 0
+ private var isInNoImageMode: Boolean = false
private var songPaths: List? = null
+ lateinit var saveFab: MaterialButton
+
private var savedSongPaths: List? = null
private val currentSongPath: String? = null
private var savedTags: Map? = null
private var savedArtworkInfo: ArtworkInfo? = null
- private var _binding: VB? = null
- protected val binding: VB get() = _binding!!
- private var cacheFiles = listOf()
- abstract val bindingInflater: (LayoutInflater) -> VB
-
- private lateinit var launcher: ActivityResultLauncher
-
- protected abstract fun loadImageFromFile(selectedFile: Uri?)
-
- protected val show: AlertDialog
- get() =
- MaterialAlertDialogBuilder(this)
- .setTitle(R.string.update_image)
- .setItems(items.toTypedArray()) { _, position ->
- when (position) {
- 0 -> startImagePicker()
- 1 -> searchImageOnWeb()
- 2 -> deleteImage()
- }
+ protected val show: MaterialDialog
+ get() = MaterialDialog(this).show {
+ cornerRadius(PreferenceUtil.getInstance(this@AbsTagEditorActivity).dialogCorner)
+ title(R.string.update_image)
+ listItems(items = items) { _, position, _ ->
+ when (position) {
+ 0 -> startImagePicker()
+ 1 -> searchImageOnWeb()
+ 2 -> deleteImage()
}
- .setNegativeButton(R.string.action_cancel, null)
- .show()
- .colorButtons()
+ }
+ }
+ protected abstract val contentViewLayout: Int
internal val albumArtist: String?
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
- } catch (e: Exception) {
- logE(e)
+ } catch (ignored: Exception) {
null
}
}
@@ -108,8 +78,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TITLE)
- } catch (e: Exception) {
- logE(e)
+ } catch (ignored: Exception) {
null
}
}
@@ -117,8 +86,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.COMPOSER)
- } catch (e: Exception) {
- logE(e)
+ } catch (ignored: Exception) {
null
}
}
@@ -127,8 +95,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM)
- } catch (e: Exception) {
- logE(e)
+ } catch (ignored: Exception) {
null
}
}
@@ -137,8 +104,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ARTIST)
- } catch (e: Exception) {
- logE(e)
+ } catch (ignored: Exception) {
null
}
}
@@ -147,8 +113,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
- } catch (e: Exception) {
- logE(e)
+ } catch (ignored: Exception) {
null
}
}
@@ -157,8 +122,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.GENRE)
- } catch (e: Exception) {
- logE(e)
+ } catch (ignored: Exception) {
null
}
}
@@ -167,8 +131,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.YEAR)
- } catch (e: Exception) {
- logE(e)
+ } catch (ignored: Exception) {
null
}
}
@@ -177,18 +140,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TRACK)
- } catch (e: Exception) {
- logE(e)
- null
- }
- }
-
- protected val discNumber: String?
- get() {
- return try {
- getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.DISC_NO)
- } catch (e: Exception) {
- logE(e)
+ } catch (ignored: Exception) {
null
}
}
@@ -197,8 +149,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.LYRICS)
- } catch (e: Exception) {
- logE(e)
+ } catch (ignored: Exception) {
null
}
}
@@ -216,58 +167,62 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
)
}
return null
- } catch (e: Exception) {
- logE(e)
+ } catch (ignored: Exception) {
return null
}
}
- private val pickArtworkImage =
- registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
- loadImageFromFile(uri)
- }
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- _binding = bindingInflater.invoke(layoutInflater)
- setContentView(binding.root)
- setTaskDescriptionColorAuto()
+ setContentView(contentViewLayout)
saveFab = findViewById(R.id.saveTags)
getIntentExtras()
songPaths = getSongPaths()
- logD(songPaths?.size)
if (songPaths!!.isEmpty()) {
finish()
+ return
}
+
setUpViews()
- launcher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
- if (it.resultCode == Activity.RESULT_OK) {
- writeToFiles(getSongUris(), cacheFiles)
- }
- }
+
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setTaskDescriptionColorAuto()
}
private fun setUpViews() {
+ setUpScrollView()
setUpFab()
setUpImageView()
}
+ private fun setUpScrollView() {
+ //observableScrollView.setScrollViewCallbacks(observableScrollViewCallbacks);
+ }
+
private lateinit var items: List
private fun setUpImageView() {
loadCurrentImage()
items = listOf(
- getString(R.string.pick_from_local_storage),
- getString(R.string.web_search),
- getString(R.string.remove_cover)
+ getString(code.name.monkey.retromusic.R.string.pick_from_local_storage),
+ getString(code.name.monkey.retromusic.R.string.web_search),
+ getString(code.name.monkey.retromusic.R.string.remove_cover)
)
- editorImage.setOnClickListener { show }
+ editorImage?.setOnClickListener { show }
}
private fun startImagePicker() {
- pickArtworkImage.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
+ val intent = Intent(Intent.ACTION_GET_CONTENT)
+ intent.type = "image/*"
+ startActivityForResult(
+ Intent.createChooser(
+ intent,
+ getString(code.name.monkey.retromusic.R.string.pick_from_local_storage)
+ ), REQUEST_CODE_SELECT_IMAGE
+ )
}
protected abstract fun loadCurrentImage()
@@ -277,12 +232,26 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
protected abstract fun deleteImage()
private fun setUpFab() {
- saveFab.accentColor()
+ saveFab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
+ ColorStateList.valueOf(
+ MaterialValueHelper.getPrimaryTextColor(
+ this,
+ ColorUtil.isColorLight(
+ ThemeStore.accentColor(
+ this
+ )
+ )
+ )
+ ).apply {
+ saveFab.setTextColor(this)
+ saveFab.iconTint = this
+ }
saveFab.apply {
scaleX = 0f
scaleY = 0f
isEnabled = false
setOnClickListener { save() }
+ TintHelper.setTintAuto(this, ThemeStore.accentColor(this@AbsTagEditorActivity), true)
}
}
@@ -291,14 +260,12 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
private fun getIntentExtras() {
val intentExtras = intent.extras
if (intentExtras != null) {
- id = intentExtras.getLong(EXTRA_ID)
+ id = intentExtras.getInt(EXTRA_ID)
}
}
protected abstract fun getSongPaths(): List
- protected abstract fun getSongUris(): List
-
protected fun searchWebFor(vararg keys: String) {
val stringBuilder = StringBuilder()
for (key in keys) {
@@ -315,13 +282,27 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
- onBackPressedDispatcher.onBackPressed()
+ super.onBackPressed()
return true
}
}
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()
}
@@ -340,7 +321,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) {
if (bitmap == null) {
- editorImage.setImageResource(R.drawable.default_audio_art)
+ editorImage.setImageResource(drawable.default_audio_art)
} else {
editorImage.setImageBitmap(bitmap)
}
@@ -352,106 +333,86 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
}
protected fun writeValuesToFiles(
- fieldKeyValueMap: Map,
- artworkInfo: ArtworkInfo?
+ fieldKeyValueMap: Map, artworkInfo: ArtworkInfo?
) {
- hideSoftKeyboard()
+ RetroUtil.hideSoftKeyboard(this)
hideFab()
- logD(fieldKeyValueMap)
- GlobalScope.launch {
- if (VersionUtils.hasR()) {
- cacheFiles = TagWriter.writeTagsToFilesR(
- this@AbsTagEditorActivity, AudioTagInfo(
- songPaths,
- fieldKeyValueMap,
- artworkInfo
- )
- )
- if (cacheFiles.isNotEmpty()) {
- val pendingIntent =
- MediaStore.createWriteRequest(contentResolver, getSongUris())
- launcher.launch(IntentSenderRequest.Builder(pendingIntent).build())
- }
- } else {
- TagWriter.writeTagsToFiles(
- this@AbsTagEditorActivity, AudioTagInfo(
- songPaths,
- fieldKeyValueMap,
- artworkInfo
+ savedSongPaths = getSongPaths()
+ savedTags = fieldKeyValueMap
+ savedArtworkInfo = artworkInfo
+
+ if (!SAFUtil.isSAFRequired(savedSongPaths)) {
+ writeTags(savedSongPaths)
+ } else {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ if (SAFUtil.isSDCardAccessGranted(this)) {
+ writeTags(savedSongPaths)
+ } else {
+ startActivityForResult(
+ Intent(this, SAFGuideActivity::class.java),
+ SAFGuideActivity.REQUEST_CODE_SAF_GUIDE
)
- )
+ }
}
}
}
private fun writeTags(paths: List?) {
- GlobalScope.launch {
- if (VersionUtils.hasR()) {
- cacheFiles = TagWriter.writeTagsToFilesR(
- this@AbsTagEditorActivity, AudioTagInfo(
- paths,
- savedTags,
- savedArtworkInfo
- )
- )
- val pendingIntent = MediaStore.createWriteRequest(contentResolver, getSongUris())
+ WriteTagsAsyncTask(this).execute(
+ WriteTagsAsyncTask.LoadingInfo(
+ paths,
+ savedTags,
+ savedArtworkInfo
+ )
+ )
+ }
- launcher.launch(IntentSenderRequest.Builder(pendingIntent).build())
- } else {
- TagWriter.writeTagsToFiles(
- this@AbsTagEditorActivity, AudioTagInfo(
- paths,
- savedTags,
- savedArtworkInfo
- )
- )
+ override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
+ super.onActivityResult(requestCode, resultCode, intent)
+ when (requestCode) {
+ REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
+ intent?.data?.let {
+ loadImageFromFile(it)
+ }
+ }
+ SAFGuideActivity.REQUEST_CODE_SAF_GUIDE -> {
+ SAFUtil.openTreePicker(this)
+ }
+ SAFUtil.REQUEST_SAF_PICK_TREE -> {
+ if (resultCode == Activity.RESULT_OK) {
+ SAFUtil.saveTreeUri(this, intent)
+ writeTags(savedSongPaths)
+ }
+ }
+ SAFUtil.REQUEST_SAF_PICK_FILE -> {
+ if (resultCode == Activity.RESULT_OK) {
+ writeTags(Collections.singletonList(currentSongPath + SAFUtil.SEPARATOR + intent!!.dataString))
+ }
}
}
}
- private lateinit var audioFile: AudioFile
+ protected abstract fun loadImageFromFile(selectedFile: Uri?)
private fun getAudioFile(path: String): AudioFile {
return try {
- if (!this::audioFile.isInitialized) {
- audioFile = AudioFileIO.read(File(path))
- }
- audioFile
+ AudioFileIO.read(File(path))
} catch (e: Exception) {
Log.e(TAG, "Could not read audio file $path", e)
AudioFile()
}
}
- private fun writeToFiles(songUris: List, cacheFiles: List) {
- 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()
- }
- }
+ class ArtworkInfo constructor(val albumId: Int, val artwork: Bitmap?)
companion object {
+
const val EXTRA_ID = "extra_id"
const val EXTRA_PALETTE = "extra_palette"
private val TAG = AbsTagEditorActivity::class.java.simpleName
private const val REQUEST_CODE_SELECT_IMAGE = 1000
}
+
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt
index 2c9013657..b29684045 100755
--- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt
@@ -1,17 +1,3 @@
-/*
- * 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.tageditor
import android.app.Activity
@@ -22,36 +8,31 @@ 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 androidx.core.widget.doAfterTextChanged
-import code.name.monkey.appthemehelper.util.MaterialValueHelper
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.appthemehelper.util.MaterialUtil
import code.name.monkey.retromusic.R
-import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding
-import code.name.monkey.retromusic.extensions.*
-import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
+import code.name.monkey.retromusic.extensions.appHandleColor
+import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder
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.loaders.AlbumLoader
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 code.name.monkey.retromusic.util.logD
import com.bumptech.glide.Glide
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 com.bumptech.glide.request.animation.GlideAnimation
+import com.bumptech.glide.request.target.SimpleTarget
+import kotlinx.android.synthetic.main.activity_album_tag_editor.*
import org.jaudiotagger.tag.FieldKey
import java.util.*
-class AlbumTagEditorActivity : AbsTagEditorActivity() {
-
- override val bindingInflater: (LayoutInflater) -> ActivityAlbumTagEditorBinding =
- ActivityAlbumTagEditorBinding::inflate
+class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
+ override val contentViewLayout: Int
+ get() = R.layout.activity_album_tag_editor
private fun windowEnterTransition() {
val slide = Slide()
@@ -63,19 +44,54 @@ class AlbumTagEditorActivity : AbsTagEditorActivity() {
+ override fun onResourceReady(
+ resource: BitmapPaletteWrapper?,
+ glideAnimation: GlideAnimation?
+ ) {
+ 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() {
- setSupportActionBar(binding.toolbar)
- binding.appBarLayout?.statusBarForeground =
- MaterialShapeDrawable.createWithElevationOverlay(this)
+ toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
+ setSupportActionBar(toolbar)
}
override fun onCreate(savedInstanceState: Bundle?) {
+ setDrawUnderStatusBar()
super.onCreate(savedInstanceState)
window.sharedElementsUseOverlay = true
- binding.imageContainer.transitionName = getString(R.string.transition_album_art)
+ imageContainer?.transitionName = "${getString(R.string.transition_album_art)}_$id"
windowEnterTransition()
setUpViews()
setupToolbar()
@@ -84,23 +100,22 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(binding.editorImage) {
- override fun onResourceReady(
- resource: BitmapPaletteWrapper,
- transition: Transition?
- ) {
- 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::class.java)
- 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] = 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()
+ fieldKeyValueMap[FieldKey.ALBUM] = 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()
writeValuesToFiles(
fieldKeyValueMap,
- when {
- deleteAlbumArt -> ArtworkInfo(id, null)
- albumArtBitmap == null -> null
- else -> ArtworkInfo(id, albumArtBitmap!!)
- }
+ if (deleteAlbumArt) ArtworkInfo(id, null)
+ else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!)
)
}
override fun getSongPaths(): List {
- return repository.albumById(id).songs
- .map(Song::data)
+ val songs = AlbumLoader.getAlbum(this, id).songs
+ val paths = ArrayList(songs!!.size)
+ for (song in songs) {
+ paths.add(song.data)
+ }
+ return paths
}
- override fun getSongUris(): List = repository.albumById(id).songs.map {
- MusicUtil.getSongFileUri(it.id)
+ 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 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
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt
index 93dd8a44f..d99bb70cd 100755
--- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt
@@ -1,214 +1,112 @@
-/*
- * 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.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.view.LayoutInflater
-import android.widget.ImageView
-import android.widget.Toast
-import androidx.core.widget.doAfterTextChanged
-import code.name.monkey.appthemehelper.util.MaterialValueHelper
+import android.text.Editable
+import android.text.TextWatcher
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.appthemehelper.util.MaterialUtil
import code.name.monkey.retromusic.R
-import code.name.monkey.retromusic.databinding.ActivitySongTagEditorBinding
-import code.name.monkey.retromusic.extensions.*
-import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
-import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
-import code.name.monkey.retromusic.model.ArtworkInfo
-import code.name.monkey.retromusic.repository.SongRepository
-import code.name.monkey.retromusic.util.ImageUtil
-import code.name.monkey.retromusic.util.MusicUtil
-import code.name.monkey.retromusic.util.RetroColorUtil
-import code.name.monkey.retromusic.util.logD
-import com.bumptech.glide.Glide
-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 code.name.monkey.retromusic.extensions.appHandleColor
+import code.name.monkey.retromusic.loaders.SongLoader
+import kotlinx.android.synthetic.main.activity_song_tag_editor.*
import org.jaudiotagger.tag.FieldKey
-import org.koin.android.ext.android.inject
import java.util.*
-class SongTagEditorActivity : AbsTagEditorActivity() {
+class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
- override val bindingInflater: (LayoutInflater) -> ActivitySongTagEditorBinding =
- ActivitySongTagEditorBinding::inflate
-
-
- private val songRepository by inject()
-
- private var albumArtBitmap: Bitmap? = null
- private var deleteAlbumArt: Boolean = false
+ override val contentViewLayout: Int
+ get() = R.layout.activity_song_tag_editor
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+
+ setNoImageMode()
setUpViews()
- setSupportActionBar(binding.toolbar)
- binding.appBarLayout?.statusBarForeground =
- MaterialShapeDrawable.createWithElevationOverlay(this)
+ toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
+ setSupportActionBar(toolbar)
}
- @SuppressLint("ClickableViewAccessibility")
private fun setUpViews() {
fillViewsWithFileTags()
- 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)
+ 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.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() }
+ 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)
}
private fun fillViewsWithFileTags() {
- 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)
- logD(songTitle + songYear)
+ 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)
}
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::class.java)
- 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!!)
- }
- )
+ 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)
}
- override fun getSongPaths(): List = listOf(songRepository.song(id).data)
-
- override fun getSongUris(): List = listOf(MusicUtil.getSongFileUri(id))
+ override fun getSongPaths(): List {
+ val paths = ArrayList(1)
+ paths.add(SongLoader.getSong(this, id).data)
+ return paths
+ }
override fun loadImageFromFile(selectedFile: Uri?) {
- Glide.with(this@SongTagEditorActivity)
- .asBitmapPalette()
- .load(selectedFile)
- .diskCacheStrategy(DiskCacheStrategy.NONE)
- .skipMemoryCache(true)
- .into(object : ImageViewTarget(binding.editorImage) {
- override fun onResourceReady(
- resource: BitmapPaletteWrapper,
- transition: Transition?
- ) {
- 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 onLoadFailed(errorDrawable: Drawable?) {
- super.onLoadFailed(errorDrawable)
- showToast(R.string.error_load_failed, Toast.LENGTH_LONG)
- }
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
+ }
- override fun setResource(resource: BitmapPaletteWrapper?) {}
- })
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+ }
+
+ override fun afterTextChanged(s: Editable) {
+ dataChanged()
}
companion object {
val TAG: String = SongTagEditorActivity::class.java.simpleName
}
-
- override val editorImage: ImageView
- get() = binding.editorImage
}
+
+
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/TagWriter.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/TagWriter.kt
deleted file mode 100644
index b9d5f28dd..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/TagWriter.kt
+++ /dev/null
@@ -1,204 +0,0 @@
-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.R
-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.FieldDataInvalidException
-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?) {
- if (toBeScanned.isNullOrEmpty()) {
- 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) {
- 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: FieldDataInvalidException) {
- withContext(Dispatchers.Main) {
- context.showToast(R.string.could_not_write_tags_to_file)
- }
- return@withContext listOf()
- } 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)
- }
- }
-
- @RequiresApi(Build.VERSION_CODES.R)
- suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List =
- withContext(Dispatchers.IO) {
- val cacheFiles = mutableListOf()
- 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: FieldDataInvalidException) {
- withContext(Dispatchers.Main) {
- context.showToast(R.string.could_not_write_tags_to_file)
- }
- return@withContext listOf()
- } 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)
- }
- cacheFiles
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/WriteTagsAsyncTask.java b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/WriteTagsAsyncTask.java
new file mode 100644
index 000000000..81a8a7a59
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/WriteTagsAsyncTask.java
@@ -0,0 +1,193 @@
+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.net.Uri;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+
+import org.jaudiotagger.audio.AudioFile;
+import org.jaudiotagger.audio.AudioFileIO;
+import org.jaudiotagger.tag.FieldKey;
+import org.jaudiotagger.tag.Tag;
+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.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import code.name.monkey.retromusic.R;
+import code.name.monkey.retromusic.misc.DialogAsyncTask;
+import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener;
+import code.name.monkey.retromusic.util.MusicUtil;
+import code.name.monkey.retromusic.util.SAFUtil;
+
+public class WriteTagsAsyncTask extends
+ DialogAsyncTask {
+
+ private WeakReference activity;
+
+ public WriteTagsAsyncTask(@NonNull Activity activity) {
+ super(activity);
+ this.activity = new WeakReference<>(activity);
+ }
+
+ @NonNull
+ @Override
+ protected Dialog createDialog(@NonNull Context context) {
+
+ return new MaterialAlertDialogBuilder(context)
+ .setTitle(R.string.saving_changes)
+ .setCancelable(false)
+ .setView(R.layout.loading)
+ .create();
+ }
+
+ @Override
+ protected String[] doInBackground(LoadingInfo... params) {
+ try {
+ LoadingInfo info = params[0];
+
+ Artwork artwork = null;
+ File albumArtFile = null;
+ if (info.artworkInfo != null && info.artworkInfo.getArtwork() != null) {
+ try {
+ albumArtFile = MusicUtil.createAlbumArtFile().getCanonicalFile();
+ info.artworkInfo.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.filePaths) {
+ publishProgress(++counter, info.filePaths.size());
+ try {
+ Uri safUri = null;
+ if (filePath.contains(SAFUtil.SEPARATOR)) {
+ String[] fragments = filePath.split(SAFUtil.SEPARATOR);
+ filePath = fragments[0];
+ safUri = Uri.parse(fragments[1]);
+ }
+
+ AudioFile audioFile = AudioFileIO.read(new File(filePath));
+ Tag tag = audioFile.getTagOrCreateAndSetDefault();
+
+ if (info.fieldKeyValueMap != null) {
+ for (Map.Entry entry : info.fieldKeyValueMap.entrySet()) {
+ try {
+ tag.setField(entry.getKey(), entry.getValue());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ if (info.artworkInfo != null) {
+ if (info.artworkInfo.getArtwork() == null) {
+ tag.deleteArtworkField();
+ deletedArtwork = true;
+ } else if (artwork != null) {
+ tag.deleteArtworkField();
+ tag.setField(artwork);
+ wroteArtwork = true;
+ }
+ }
+
+ Activity activity = this.activity.get();
+ SAFUtil.write(activity, audioFile, safUri);
+
+ } catch (@NonNull Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ Context context = getContext();
+ if (context != null) {
+ if (wroteArtwork) {
+ MusicUtil.insertAlbumArt(context, info.artworkInfo.getAlbumId(), albumArtFile.getPath());
+ } else if (deletedArtwork) {
+ MusicUtil.deleteAlbumArt(context, info.artworkInfo.getAlbumId());
+ }
+ }
+
+ Collection paths = info.filePaths;
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
+ paths = new ArrayList<>(info.filePaths.size());
+ for (String path : info.filePaths) {
+ if (path.contains(SAFUtil.SEPARATOR)) {
+ path = path.split(SAFUtil.SEPARATOR)[0];
+ }
+ paths.add(path);
+ }
+ }
+
+ return paths.toArray(new String[paths.size()]);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @Override
+ protected void onCancelled(String[] toBeScanned) {
+ super.onCancelled(toBeScanned);
+ scan(toBeScanned);
+ }
+
+ @Override
+ protected void onPostExecute(String[] toBeScanned) {
+ super.onPostExecute(toBeScanned);
+ scan(toBeScanned);
+ }
+
+ @Override
+ protected void onProgressUpdate(@NonNull Dialog dialog, Integer... values) {
+ super.onProgressUpdate(dialog, values);
+ //((MaterialDialog) dialog).setMaxProgress(values[1]);
+ //((MaterialDialog) dialog).setProgress(values[0]);
+ }
+
+ private void scan(String[] toBeScanned) {
+ Activity activity = this.activity.get();
+ if (activity != null) {
+ MediaScannerConnection.scanFile(activity, toBeScanned, null,
+ new UpdateToastMediaScannerCompletionListener(activity, toBeScanned));
+ }
+ }
+
+ public static class LoadingInfo {
+
+ @Nullable
+ final Map fieldKeyValueMap;
+
+ final Collection filePaths;
+
+ @Nullable
+ private AbsTagEditorActivity.ArtworkInfo artworkInfo;
+
+ public LoadingInfo(Collection filePaths,
+ @Nullable Map fieldKeyValueMap,
+ @Nullable AbsTagEditorActivity.ArtworkInfo artworkInfo) {
+ this.filePaths = filePaths;
+ this.fieldKeyValueMap = fieldKeyValueMap;
+ this.artworkInfo = artworkInfo;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.java b/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.java
new file mode 100644
index 000000000..8c169ef6f
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.java
@@ -0,0 +1,138 @@
+/*
+ * 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 com.google.android.material.checkbox.MaterialCheckBox;
+
+import java.util.List;
+
+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;
+
+public class CategoryInfoAdapter extends RecyclerView.Adapter
+ implements SwipeAndDragHelper.ActionCompletionContract {
+
+ private List categoryInfos;
+ private ItemTouchHelper touchHelper;
+
+ public CategoryInfoAdapter(@NonNull List categoryInfos) {
+ this.categoryInfos = categoryInfos;
+ SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(this);
+ touchHelper = new ItemTouchHelper(swipeAndDragHelper);
+ }
+
+ public void attachToRecyclerView(RecyclerView recyclerView) {
+ touchHelper.attachToRecyclerView(recyclerView);
+ }
+
+ @NonNull
+ public List getCategoryInfos() {
+ return categoryInfos;
+ }
+
+ public void setCategoryInfos(@NonNull List 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.visible);
+ holder.title.setText(holder.title.getResources().getString(categoryInfo.category.stringRes));
+
+ holder.itemView.setOnClickListener(v -> {
+ if (!(categoryInfo.visible && isLastCheckedCategory(categoryInfo))) {
+ categoryInfo.visible = !categoryInfo.visible;
+ holder.checkBox.setChecked(categoryInfo.visible);
+ } 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.visible) {
+ for (CategoryInfo c : categoryInfos) {
+ if (c != categoryInfo && c.visible) {
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.kt
deleted file mode 100644
index dd5cda1d3..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.kt
+++ /dev/null
@@ -1,116 +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 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(),
- ActionCompletionContract {
- var categoryInfos: MutableList =
- 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)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/ContributorAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/ContributorAdapter.kt
new file mode 100644
index 000000000..dcd0f7bd0
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/ContributorAdapter.kt
@@ -0,0 +1,78 @@
+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.model.Contributor
+import code.name.monkey.retromusic.util.RetroUtil.openUrl
+import code.name.monkey.retromusic.views.RetroShapeableImageView
+import com.bumptech.glide.Glide
+
+class ContributorAdapter(
+ private var contributors: List
+) : RecyclerView.Adapter() {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ return if (viewType == HEADER) {
+ ViewHolder(
+ LayoutInflater.from(parent.context).inflate(
+ R.layout.item_contributor_header,
+ parent,
+ false
+ )
+ )
+ } else ViewHolder(
+ LayoutInflater.from(parent.context).inflate(
+ R.layout.item_contributor,
+ parent,
+ false
+ )
+ )
+ }
+
+ companion object {
+ const val HEADER: Int = 0
+ const val ITEM: Int = 1
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return if (position == 0) {
+ HEADER
+ } else {
+ ITEM
+ }
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val contributor = contributors[position]
+ holder.bindData(contributor)
+ holder.itemView.setOnClickListener {
+ 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
+ Glide.with(image.context)
+ .load(contributor.profileImage)
+ .error(R.drawable.ic_account_white_24dp)
+ .placeholder(R.drawable.ic_account_white_24dp)
+ .dontAnimate()
+ .into(image)
+ }
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt
index b139a0af8..570912a30 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt
@@ -1,37 +1,14 @@
-/*
- * 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.annotation.SuppressLint
+import android.app.Activity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-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.databinding.ItemGenreBinding
-import code.name.monkey.retromusic.glide.RetroGlideExtension
-import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
-import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
-import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
-import code.name.monkey.retromusic.interfaces.IGenreClickListener
+import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import code.name.monkey.retromusic.model.Genre
-import code.name.monkey.retromusic.util.MusicUtil
-import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
-import com.bumptech.glide.Glide
+import code.name.monkey.retromusic.util.NavigationUtil
import java.util.*
/**
@@ -39,74 +16,41 @@ import java.util.*
*/
class GenreAdapter(
- private val activity: FragmentActivity,
+ private val activity: Activity,
var dataSet: List,
- private val listener: IGenreClickListener
+ private val mItemLayoutRes: Int
) : RecyclerView.Adapter() {
- init {
- this.setHasStableIds(true)
- }
-
- override fun getItemId(position: Int): Long {
- return dataSet[position].id
- }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
- return ViewHolder(ItemGenreBinding.inflate(LayoutInflater.from(activity), parent, false))
+ return ViewHolder(LayoutInflater.from(activity).inflate(mItemLayoutRes, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val genre = dataSet[position]
- holder.binding.title.text = genre.name
- holder.binding.text.text = String.format(
+ holder.title?.text = genre.name
+ holder.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)
- Glide.with(activity)
- .asBitmapPalette()
- .songCoverOptions(genreSong)
- .load(RetroGlideExtension.getSongModel(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) {
dataSet = list
notifyDataSetChanged()
}
- inner class ViewHolder(val binding: ItemGenreBinding) : RecyclerView.ViewHolder(binding.root),
- View.OnClickListener {
+ inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
override fun onClick(v: View?) {
- listener.onClickGenre(dataSet[layoutPosition], itemView)
- }
-
- init {
- itemView.setOnClickListener(this)
+ super.onClick(v)
+ val genre = dataSet[layoutPosition]
+ NavigationUtil.goToGenre(activity, genre)
}
}
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt
index e3f809b5f..c186a14b7 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt
@@ -1,47 +1,31 @@
-/*
- * 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.annotation.SuppressLint
+import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.annotation.IntDef
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.retromusic.*
-import code.name.monkey.retromusic.adapter.album.AlbumAdapter
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.adapter.album.AlbumFullWidthAdapter
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
import code.name.monkey.retromusic.adapter.song.SongAdapter
-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.extensions.show
+import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.model.Home
-import code.name.monkey.retromusic.model.Song
+import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.util.PreferenceUtil
-class HomeAdapter(private val activity: AppCompatActivity) :
- RecyclerView.Adapter(), IArtistClickListener, IAlbumClickListener {
+class HomeAdapter(
+ private val activity: AppCompatActivity,
+ private val displayMetrics: DisplayMetrics
+) : RecyclerView.Adapter() {
private var list = listOf()
@@ -50,75 +34,44 @@ class HomeAdapter(private val activity: AppCompatActivity) :
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
- val layout =
- LayoutInflater.from(activity).inflate(R.layout.section_recycler_view, parent, false)
+ val layout = LayoutInflater.from(activity)
+ .inflate(R.layout.section_recycler_view, parent, false)
return when (viewType) {
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
- FAVOURITES -> PlaylistViewHolder(layout)
- TOP_ALBUMS, RECENT_ALBUMS -> AlbumViewHolder(layout)
+ PLAYLISTS -> PlaylistViewHolder(layout)
else -> {
- ArtistViewHolder(layout)
+ AlbumViewHolder(
+ LayoutInflater.from(activity).inflate(
+ R.layout.metal_section_recycler_view,
+ parent,
+ false
+ )
+ )
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
- val home = list[position]
when (getItemViewType(position)) {
RECENT_ALBUMS -> {
val viewHolder = holder as AlbumViewHolder
- viewHolder.bindView(home)
- viewHolder.clickableArea.setOnClickListener {
- it.findFragment().setSharedAxisXTransitions()
- activity.findNavController(R.id.fragment_container).navigate(
- R.id.detailListFragment,
- bundleOf("type" to RECENT_ALBUMS)
- )
- }
+ viewHolder.bindView(list[position].arrayList.toAlbums(), R.string.recent_albums)
}
TOP_ALBUMS -> {
val viewHolder = holder as AlbumViewHolder
- viewHolder.bindView(home)
- viewHolder.clickableArea.setOnClickListener {
- it.findFragment().setSharedAxisXTransitions()
- activity.findNavController(R.id.fragment_container).navigate(
- R.id.detailListFragment,
- bundleOf("type" to TOP_ALBUMS)
- )
- }
+ viewHolder.bindView(list[position].arrayList.toAlbums(), R.string.top_albums)
}
RECENT_ARTISTS -> {
val viewHolder = holder as ArtistViewHolder
- viewHolder.bindView(home)
- viewHolder.clickableArea.setOnClickListener {
- it.findFragment().setSharedAxisXTransitions()
- activity.findNavController(R.id.fragment_container).navigate(
- R.id.detailListFragment,
- bundleOf("type" to RECENT_ARTISTS)
- )
- }
+ viewHolder.bindView(list[position].arrayList.toArtists(), R.string.recent_artists)
}
TOP_ARTISTS -> {
val viewHolder = holder as ArtistViewHolder
- viewHolder.bindView(home)
- viewHolder.clickableArea.setOnClickListener {
- it.findFragment().setSharedAxisXTransitions()
- activity.findNavController(R.id.fragment_container).navigate(
- R.id.detailListFragment,
- bundleOf("type" to TOP_ARTISTS)
- )
- }
+ viewHolder.bindView(list[position].arrayList.toArtists(), R.string.top_artists)
}
- FAVOURITES -> {
+ PLAYLISTS -> {
val viewHolder = holder as PlaylistViewHolder
- viewHolder.bindView(home)
- viewHolder.clickableArea.setOnClickListener {
- it.findFragment().setSharedAxisXTransitions()
- activity.findNavController(R.id.fragment_container).navigate(
- R.id.detailListFragment,
- bundleOf("type" to FAVOURITES)
- )
- }
+ viewHolder.bindView(list[position].arrayList.toPlaylist(), R.string.favorites)
}
}
}
@@ -127,87 +80,102 @@ class HomeAdapter(private val activity: AppCompatActivity) :
return list.size
}
- @SuppressLint("NotifyDataSetChanged")
fun swapData(sections: List) {
list = sections
notifyDataSetChanged()
}
- @Suppress("UNCHECKED_CAST")
+ companion object {
+
+ @IntDef(RECENT_ALBUMS, TOP_ALBUMS, RECENT_ARTISTS, TOP_ARTISTS, PLAYLISTS)
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class HomeSection
+
+ const val RECENT_ALBUMS = 3
+ const val TOP_ALBUMS = 1
+ const val RECENT_ARTISTS = 2
+ const val TOP_ARTISTS = 0
+ const val PLAYLISTS = 4
+ }
+
private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) {
- fun bindView(home: Home) {
- title.setText(home.titleRes)
- recyclerView.apply {
- adapter = albumAdapter(home.arrayList as List)
- layoutManager = gridLayoutManager()
+ fun bindView(list: ArrayList, titleRes: Int) {
+ if (list.isNotEmpty()) {
+ recyclerView.apply {
+ show()
+ adapter = AlbumFullWidthAdapter(activity, list, displayMetrics)
+ }
+ title.text = activity.getString(titleRes)
}
}
}
- @Suppress("UNCHECKED_CAST")
- private inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) {
- fun bindView(home: Home) {
- title.setText(home.titleRes)
- recyclerView.apply {
- layoutManager = linearLayoutManager()
- adapter = artistsAdapter(home.arrayList as List)
+ inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) {
+ fun bindView(list: ArrayList, titleRes: Int) {
+ if (list.isNotEmpty()) {
+ recyclerView.apply {
+ show()
+ layoutManager =
+ LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
+ val artistAdapter = ArtistAdapter(
+ activity,
+ list,
+ PreferenceUtil.getInstance(activity).getHomeGridStyle(activity),
+ null
+ )
+ adapter = artistAdapter
+ }
+ title.text = activity.getString(titleRes)
}
}
}
- @Suppress("UNCHECKED_CAST")
private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) {
- fun bindView(home: Home) {
- title.setText(home.titleRes)
- recyclerView.apply {
- val songAdapter = SongAdapter(
- activity,
- home.arrayList as MutableList,
- R.layout.item_favourite_card
- )
- layoutManager = linearLayoutManager()
- adapter = songAdapter
+ fun bindView(arrayList: ArrayList, titleRes: Int) {
+ if (arrayList.isNotEmpty()) {
+ val songs = PlaylistSongsLoader.getPlaylistSongList(activity, arrayList[0])
+ if (songs.isNotEmpty()) {
+ recyclerView.apply {
+ show()
+ val songAdapter =
+ SongAdapter(activity, songs, R.layout.item_album_card, null)
+ layoutManager =
+ GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false)
+ adapter = songAdapter
+ }
+ title.text = activity.getString(titleRes)
+ }
}
}
}
- open class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ open inner class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) {
val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView)
val title: AppCompatTextView = itemView.findViewById(R.id.title)
- val clickableArea: ViewGroup = itemView.findViewById(R.id.clickable_area)
- }
-
- private fun artistsAdapter(artists: List) =
- ArtistAdapter(activity, artists, PreferenceUtil.homeArtistGridStyle, this)
-
- private fun albumAdapter(albums: List) =
- AlbumAdapter(activity, albums, PreferenceUtil.homeAlbumGridStyle, this)
-
- private fun gridLayoutManager() =
- GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false)
-
- private fun linearLayoutManager() =
- LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
-
- override fun onArtist(artistId: Long, view: View) {
- activity.findNavController(R.id.fragment_container).navigate(
- R.id.artistDetailsFragment,
- bundleOf(EXTRA_ARTIST_ID to artistId),
- null,
- FragmentNavigatorExtras(
- view to artistId.toString()
- )
- )
- }
-
- override fun onAlbumClick(albumId: Long, view: View) {
- activity.findNavController(R.id.fragment_container).navigate(
- R.id.albumDetailsFragment,
- bundleOf(EXTRA_ALBUM_ID to albumId),
- null,
- FragmentNavigatorExtras(
- view to albumId.toString()
- )
- )
}
}
+
+private fun ArrayList.toAlbums(): ArrayList {
+ val arrayList = ArrayList()
+ for (x in this) {
+ arrayList.add(x as Album)
+ }
+ return arrayList
+}
+
+private fun ArrayList.toArtists(): ArrayList {
+ val arrayList = ArrayList()
+ for (x in this) {
+ arrayList.add(x as Artist)
+ }
+ return arrayList
+}
+
+private fun ArrayList.toPlaylist(): ArrayList {
+ val arrayList = ArrayList()
+ for (x in this) {
+ arrayList.add(x as Playlist)
+ }
+ return arrayList
+}
+
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/SearchAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/SearchAdapter.kt
index 22a60bd95..0b257978d 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/SearchAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/SearchAdapter.kt
@@ -1,176 +1,122 @@
-/*
- * 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.annotation.SuppressLint
+import android.app.ActivityOptions
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.core.os.bundleOf
-import androidx.core.view.isGone
-import androidx.core.view.isInvisible
-import androidx.core.view.isVisible
-import androidx.fragment.app.FragmentActivity
-import androidx.navigation.findNavController
+import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.ThemeStore
-import code.name.monkey.retromusic.*
+import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
-import code.name.monkey.retromusic.db.PlaylistWithSongs
-import code.name.monkey.retromusic.glide.RetroGlideExtension
-import code.name.monkey.retromusic.glide.RetroGlideExtension.albumCoverOptions
-import code.name.monkey.retromusic.glide.RetroGlideExtension.artistImageOptions
-import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
+import code.name.monkey.retromusic.glide.AlbumGlideRequest
+import code.name.monkey.retromusic.glide.ArtistGlideRequest
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
-import code.name.monkey.retromusic.model.Album
-import code.name.monkey.retromusic.model.Artist
-import code.name.monkey.retromusic.model.Genre
-import code.name.monkey.retromusic.model.Song
+import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
+import code.name.monkey.retromusic.model.*
+import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
import code.name.monkey.retromusic.util.MusicUtil
+import code.name.monkey.retromusic.util.NavigationUtil
import com.bumptech.glide.Glide
-import java.util.*
+import android.util.Pair as UtilPair
class SearchAdapter(
- private val activity: FragmentActivity,
- private var dataSet: List
+ private val activity: AppCompatActivity,
+ private var dataSet: List?
) : RecyclerView.Adapter() {
- @SuppressLint("NotifyDataSetChanged")
- fun swapDataSet(dataSet: List) {
+ fun swapDataSet(dataSet: MutableList) {
this.dataSet = dataSet
notifyDataSetChanged()
}
override fun getItemViewType(position: Int): Int {
- if (dataSet[position] is Album) return ALBUM
- 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 PlaylistWithSongs) return PLAYLIST
- return if (dataSet[position] is Song) SONG else HEADER
+ if (dataSet!![position] is Album) return ALBUM
+ if (dataSet!![position] is Artist) return ARTIST
+ if (dataSet!![position] is Genre) return GENRE
+ if (dataSet!![position] is Playlist) return PLAYLIST
+ return if (dataSet!![position] is Song) SONG else HEADER
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
- return when (viewType) {
- HEADER -> ViewHolder(
- LayoutInflater.from(activity).inflate(
- R.layout.sub_header,
- parent,
- false
- ), viewType
- )
-
- ALBUM, ARTIST, ALBUM_ARTIST -> ViewHolder(
- LayoutInflater.from(activity).inflate(
- R.layout.item_list_big,
- parent,
- false
- ), viewType
- )
-
- else -> ViewHolder(
+ return if (viewType == HEADER) ViewHolder(
+ LayoutInflater.from(activity).inflate(
+ R.layout.sub_header,
+ parent,
+ false
+ ), viewType
+ )
+ else
+ ViewHolder(
LayoutInflater.from(activity).inflate(R.layout.item_list, parent, false),
viewType
)
- }
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
when (getItemViewType(position)) {
ALBUM -> {
- holder.imageTextContainer?.isVisible = true
- val album = dataSet[position] as Album
+ val album = dataSet?.get(position) as Album
holder.title?.text = album.title
holder.text?.text = album.artistName
- Glide.with(activity).asDrawable().albumCoverOptions(album.safeGetFirstSong())
- .load(RetroGlideExtension.getSongModel(album.safeGetFirstSong()))
- .into(holder.image!!)
+ AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
+ .checkIgnoreMediaStore(activity).build().into(holder.image)
}
-
ARTIST -> {
- holder.imageTextContainer?.isVisible = true
- val artist = dataSet[position] as Artist
+ val artist = dataSet?.get(position) as Artist
holder.title?.text = artist.name
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
- Glide.with(activity).asDrawable().artistImageOptions(artist).load(
- RetroGlideExtension.getArtistModel(artist)
- ).into(holder.image!!)
+ ArtistGlideRequest.Builder.from(Glide.with(activity), artist).build()
+ .into(holder.image)
}
-
SONG -> {
- holder.imageTextContainer?.isVisible = true
- val song = dataSet[position] as Song
+ val song = dataSet?.get(position) as Song
holder.title?.text = song.title
holder.text?.text = song.albumName
- Glide.with(activity).asDrawable().songCoverOptions(song)
- .load(RetroGlideExtension.getSongModel(song)).into(holder.image!!)
}
-
GENRE -> {
- val genre = dataSet[position] as Genre
+ val genre = dataSet?.get(position) as Genre
holder.title?.text = genre.name
- holder.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
- )
- )
}
-
PLAYLIST -> {
- val playlist = dataSet[position] as PlaylistWithSongs
- holder.title?.text = playlist.playlistEntity.playlistName
- //holder.text?.text = MusicUtil.playlistInfoString(activity, playlist.songs)
+ val playlist = dataSet?.get(position) as Playlist
+ holder.title?.text = playlist.name
+ holder.text?.text = MusicUtil.getPlaylistInfoString(activity, getSongs(playlist))
}
-
- ALBUM_ARTIST -> {
- holder.imageTextContainer?.isVisible = true
- val artist = dataSet[position] as Artist
- holder.title?.text = artist.name
- holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
- Glide.with(activity).asDrawable().artistImageOptions(artist).load(
- RetroGlideExtension.getArtistModel(artist)
- ).into(holder.image!!)
- }
-
else -> {
- holder.title?.text = dataSet[position].toString()
+ holder.title?.text = dataSet?.get(position).toString()
holder.title?.setTextColor(ThemeStore.accentColor(activity))
}
}
}
+ private fun getSongs(playlist: Playlist): java.util.ArrayList {
+ val songs = java.util.ArrayList()
+ if (playlist is AbsSmartPlaylist) {
+ songs.addAll(playlist.getSongs(activity))
+ } else {
+ songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
+ }
+ return songs
+ }
+
override fun getItemCount(): Int {
- return dataSet.size
+ return dataSet!!.size
}
inner class ViewHolder(itemView: View, itemViewType: Int) : MediaEntryViewHolder(itemView) {
init {
itemView.setOnLongClickListener(null)
- imageTextContainer?.isInvisible = true
+
if (itemViewType == SONG) {
- imageTextContainer?.isGone = true
- menu?.isVisible = true
+ menu?.visibility = View.VISIBLE
menu?.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) {
override val song: Song
- get() = dataSet[layoutPosition] as Song
+ get() = dataSet!![layoutPosition] as Song
})
} else {
- menu?.isVisible = false
+ menu?.visibility = View.GONE
}
when (itemViewType) {
@@ -178,52 +124,38 @@ class SearchAdapter(
ARTIST -> setImageTransitionName(activity.getString(R.string.transition_artist_image))
else -> {
val container = itemView.findViewById(R.id.imageContainer)
- container?.isVisible = false
+ container?.visibility = View.GONE
}
}
}
override fun onClick(v: View?) {
- val item = dataSet[layoutPosition]
+ val item = dataSet!![layoutPosition]
when (itemViewType) {
ALBUM -> {
- activity.findNavController(R.id.fragment_container).navigate(
- R.id.albumDetailsFragment,
- bundleOf(EXTRA_ALBUM_ID to (item as Album).id)
+ val options = ActivityOptions.makeSceneTransitionAnimation(
+ activity,
+ UtilPair.create(image, activity.getString(R.string.transition_album_art))
)
+ NavigationUtil.goToAlbumOptions(activity, (item as Album).id, options)
}
-
ARTIST -> {
- activity.findNavController(R.id.fragment_container).navigate(
- R.id.artistDetailsFragment,
- bundleOf(EXTRA_ARTIST_ID to (item as Artist).id)
+ val options = ActivityOptions.makeSceneTransitionAnimation(
+ activity,
+ UtilPair.create(image, activity.getString(R.string.transition_artist_image))
)
+ NavigationUtil.goToArtistOptions(activity, (item as Artist).id, options)
}
-
- 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,
- bundleOf(EXTRA_GENRE to (item as Genre))
- )
+ NavigationUtil.goToGenre(activity, item as Genre)
}
-
PLAYLIST -> {
- activity.findNavController(R.id.fragment_container).navigate(
- R.id.playlistDetailsFragment,
- bundleOf(EXTRA_PLAYLIST_ID to (item as PlaylistWithSongs).playlistEntity.playListId)
- )
+ NavigationUtil.goToPlaylistNew(activity, item as Playlist)
}
-
SONG -> {
- MusicPlayerRemote.playNext(item as Song)
- MusicPlayerRemote.playNextSong()
+ val playList = ArrayList()
+ playList.add(item as Song)
+ MusicPlayerRemote.openQueue(playList, 0, true)
}
}
}
@@ -236,6 +168,5 @@ class SearchAdapter(
private const val SONG = 3
private const val GENRE = 4
private const val PLAYLIST = 5
- private const val ALBUM_ARTIST = 6
}
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt
index 4005c6e6c..4909ad372 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt
@@ -1,17 +1,3 @@
-/*
- * 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.graphics.PorterDuff
@@ -20,31 +6,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.extensions.getTintedDrawable
-import code.name.monkey.retromusic.glide.RetroGlideExtension
+import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
-import code.name.monkey.retromusic.interfaces.ICallbacks
+import code.name.monkey.retromusic.interfaces.CabHolder
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 java.util.*
import kotlin.math.log10
import kotlin.math.pow
class SongFileAdapter(
- override val activity: AppCompatActivity,
+ private val activity: AppCompatActivity,
private var dataSet: List,
private val itemLayoutRes: Int,
- private val iCallbacks: ICallbacks?
+ private val callbacks: Callbacks?,
+ cabHolder: CabHolder?
) : AbsMultiSelectAdapter(
- activity, R.menu.menu_media_selection
+ activity, cabHolder, R.menu.menu_media_selection
), PopupTextProvider {
init {
@@ -76,7 +63,7 @@ class SongFileAdapter(
if (holder.itemViewType == FILE) {
holder.text?.text = getFileText(file)
} else {
- holder.text?.isVisible = false
+ holder.text?.visibility = View.GONE
}
}
@@ -94,28 +81,30 @@ class SongFileAdapter(
}
private fun loadFileImage(file: File, holder: ViewHolder) {
- val iconColor = ATHUtil.resolveColor(activity, androidx.appcompat.R.attr.colorControlNormal)
+ val iconColor = ATHUtil.resolveColor(activity, R.attr.colorControlNormal)
if (file.isDirectory) {
holder.image?.let {
it.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN)
- it.setImageResource(R.drawable.ic_folder)
+ it.setImageResource(R.drawable.ic_folder_white_24dp)
}
holder.imageTextContainer?.setCardBackgroundColor(
ATHUtil.resolveColor(
activity,
- com.google.android.material.R.attr.colorSurface
+ R.attr.colorSurface
)
)
} else {
- val error = activity.getTintedDrawable(R.drawable.ic_audio_file, iconColor)
+ val error = RetroUtil.getTintedVectorDrawable(
+ activity, R.drawable.ic_file_music_white_24dp, iconColor
+ )
Glide.with(activity)
.load(AudioFileCover(file.path))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.error(error)
.placeholder(error)
- .transition(RetroGlideExtension.getDefaultTransition())
+ .animate(android.R.anim.fade_in)
.signature(MediaStoreSignature("", file.lastModified(), 0))
- .into(holder.image!!)
+ .into(holder.image)
}
}
@@ -123,35 +112,43 @@ class SongFileAdapter(
return dataSet.size
}
- override fun getIdentifier(position: Int): File {
+ override fun getIdentifier(position: Int): File? {
return dataSet[position]
}
- override fun getName(model: File): String {
- return getFileTitle(model)
+ override fun getName(`object`: File): String {
+ return getFileTitle(`object`)
}
- override fun onMultipleItemAction(menuItem: MenuItem, selection: List) {
- if (iCallbacks == null) return
- iCallbacks.onMultipleItemAction(menuItem, selection as ArrayList)
+ override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) {
+ if (callbacks == null) return
+ callbacks.onMultipleItemAction(menuItem, selection)
}
override fun getPopupText(position: Int): String {
- return if (position >= dataSet.lastIndex) "" else getSectionName(position)
+ return getSectionName(position)
}
private fun getSectionName(position: Int): String {
return MusicUtil.getSectionName(dataSet[position].name)
}
- inner class ViewHolder(itemView: View) : code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder(itemView) {
+ interface Callbacks {
+ fun onFileSelected(file: File)
+
+ fun onFileMenuClicked(file: File, view: View)
+
+ fun onMultipleItemAction(item: MenuItem, files: ArrayList)
+ }
+
+ inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
init {
- if (menu != null && iCallbacks != null) {
+ if (menu != null && callbacks != null) {
menu?.setOnClickListener { v ->
val position = layoutPosition
if (isPositionInRange(position)) {
- iCallbacks.onFileMenuClicked(dataSet[position], v)
+ callbacks.onFileMenuClicked(dataSet[position], v)
}
}
}
@@ -166,7 +163,7 @@ class SongFileAdapter(
if (isInQuickSelectMode) {
toggleChecked(position)
} else {
- iCallbacks?.onFileSelected(dataSet[position])
+ callbacks?.onFileSelected(dataSet[position])
}
}
}
@@ -193,4 +190,4 @@ class SongFileAdapter(
return DecimalFormat("#,##0.##").format(size / 1024.0.pow(digitGroups.toDouble())) + " " + units[digitGroups]
}
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/StorageAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/StorageAdapter.kt
deleted file mode 100644
index 98c2c632f..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/StorageAdapter.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-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,
- val storageClickListener: StorageClickListener
-) :
- RecyclerView.Adapter() {
-
- 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
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/TranslatorsAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/TranslatorsAdapter.kt
new file mode 100644
index 000000000..90585c364
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/TranslatorsAdapter.kt
@@ -0,0 +1,52 @@
+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
+) : RecyclerView.Adapter() {
+
+ 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()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt
index 160af7e47..33ccf01f7 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt
@@ -1,51 +1,41 @@
-/*
- * 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.album
+import android.app.ActivityOptions
import android.content.res.ColorStateList
+import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
-import androidx.core.view.isVisible
-import androidx.fragment.app.FragmentActivity
+import androidx.appcompat.app.AppCompatActivity
+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.adapter.base.AbsMultiSelectAdapter
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
-import code.name.monkey.retromusic.glide.RetroGlideExtension
-import code.name.monkey.retromusic.glide.RetroGlideExtension.albumCoverOptions
-import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
+import code.name.monkey.retromusic.glide.AlbumGlideRequest
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
+import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.SortOrder
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
-import code.name.monkey.retromusic.interfaces.IAlbumClickListener
+import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil
+import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import com.bumptech.glide.Glide
import me.zhanghai.android.fastscroll.PopupTextProvider
open class AlbumAdapter(
- override val activity: FragmentActivity,
+ protected val activity: AppCompatActivity,
var dataSet: List,
- var itemLayoutRes: Int,
- val listener: IAlbumClickListener?
+ protected var itemLayoutRes: Int,
+ cabHolder: CabHolder?
) : AbsMultiSelectAdapter(
activity,
+ cabHolder,
R.menu.menu_media_selection
), PopupTextProvider {
@@ -67,18 +57,12 @@ 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.albumArtist.let {
- if (it.isNullOrEmpty()) {
- album.artistName
- } else {
- it
- }
- }
+ return album.artistName
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
@@ -87,39 +71,54 @@ 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()
+ holder.playSongs?.setOnClickListener {
+ album.songs?.let { songs ->
+ MusicPlayerRemote.openQueue(
+ songs,
+ 0,
+ true
+ )
+ }
}
loadAlbumCover(album, holder)
}
- protected open fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) {
+ protected open fun setColors(color: Int, holder: ViewHolder) {
if (holder.paletteColorContainer != null) {
- holder.title?.setTextColor(color.primaryTextColor)
- holder.text?.setTextColor(color.secondaryTextColor)
- holder.paletteColorContainer?.setBackgroundColor(color.backgroundColor)
+ holder.title?.setTextColor(
+ MaterialValueHelper.getPrimaryTextColor(
+ activity,
+ ColorUtil.isColorLight(color)
+ )
+ )
+ holder.text?.setTextColor(
+ MaterialValueHelper.getSecondaryTextColor(
+ activity,
+ ColorUtil.isColorLight(color)
+ )
+ )
+ holder.paletteColorContainer?.setBackgroundColor(color)
}
- holder.mask?.backgroundTintList = ColorStateList.valueOf(color.primaryTextColor)
- holder.imageContainerCard?.setCardBackgroundColor(color.backgroundColor)
+ holder.mask?.backgroundTintList = ColorStateList.valueOf(color)
}
protected open fun loadAlbumCover(album: Album, holder: ViewHolder) {
if (holder.image == null) {
return
}
- val song = album.safeGetFirstSong()
- Glide.with(activity)
- .asBitmapPalette()
- .albumCoverOptions(song)
- //.checkIgnoreMediaStore()
- .load(RetroGlideExtension.getSongModel(song))
+
+ AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
+ .checkIgnoreMediaStore(activity)
+ .generatePalette(activity)
+ .build()
.into(object : RetroMusicColoredTarget(holder.image!!) {
+ override fun onLoadCleared(placeholder: Drawable?) {
+ super.onLoadCleared(placeholder)
+ //setColors(defaultFooterColor, holder)
+ }
+
override fun onColorReady(colors: MediaNotificationProcessor) {
- setColors(colors, holder)
+ setColors(colors.backgroundColor, holder)
}
})
}
@@ -129,28 +128,27 @@ open class AlbumAdapter(
}
override fun getItemId(position: Int): Long {
- return dataSet[position].id
+ return dataSet[position].id.toLong()
}
override fun getIdentifier(position: Int): Album? {
return dataSet[position]
}
- override fun getName(model: Album): String {
- return model.title
+ override fun getName(album: Album): String {
+ return album.title!!
}
override fun onMultipleItemAction(
- menuItem: MenuItem,
- selection: List
+ menuItem: MenuItem, selection: ArrayList
) {
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
}
- private fun getSongList(albums: List): List {
+ private fun getSongList(albums: List): ArrayList {
val songs = ArrayList()
for (album in albums) {
- songs.addAll(album.songs)
+ songs.addAll(album.songs!!)
}
return songs
}
@@ -161,22 +159,23 @@ open class AlbumAdapter(
private fun getSectionName(position: Int): String {
var sectionName: String? = null
- when (PreferenceUtil.albumSortOrder) {
+ when (PreferenceUtil.getInstance(activity).albumSortOrder) {
SortOrder.AlbumSortOrder.ALBUM_A_Z, SortOrder.AlbumSortOrder.ALBUM_Z_A -> sectionName =
dataSet[position].title
-
- SortOrder.AlbumSortOrder.ALBUM_ARTIST -> sectionName = dataSet[position].albumArtist
+ SortOrder.AlbumSortOrder.ALBUM_ARTIST -> sectionName = dataSet[position].artistName
SortOrder.AlbumSortOrder.ALBUM_YEAR -> return MusicUtil.getYearString(
dataSet[position].year
)
}
+
return MusicUtil.getSectionName(sectionName)
}
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
init {
- menu?.isVisible = false
+ setImageTransitionName(activity.getString(R.string.transition_album_art))
+ menu?.visibility = View.GONE
}
override fun onClick(v: View?) {
@@ -184,14 +183,22 @@ open class AlbumAdapter(
if (isInQuickSelectMode) {
toggleChecked(layoutPosition)
} else {
- image?.let {
- listener?.onAlbumClick(dataSet[layoutPosition].id, imageContainer ?: it)
- }
+ val activityOptions = ActivityOptions.makeSceneTransitionAnimation(
+ activity,
+ imageContainerCard ?: image,
+ "${activity.getString(R.string.transition_album_art)}_${dataSet[layoutPosition].id}"
+ )
+ NavigationUtil.goToAlbumOptions(
+ activity,
+ dataSet[layoutPosition].id,
+ activityOptions
+ )
}
}
override fun onLongClick(v: View?): Boolean {
- return toggleChecked(layoutPosition)
+ toggleChecked(layoutPosition)
+ return super.onLongClick(v)
}
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt
index 5a0a7d3cd..1b77d3ec3 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt
@@ -1,17 +1,3 @@
-/*
- * 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.album
import android.os.Bundle
@@ -19,31 +5,18 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
-import androidx.core.os.BundleCompat
-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.RetroGlideExtension
-import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
-import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
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
-import kotlinx.coroutines.withContext
class AlbumCoverPagerAdapter(
fragmentManager: FragmentManager,
@@ -88,17 +61,31 @@ class AlbumCoverPagerAdapter(
class AlbumCoverFragment : Fragment() {
+ lateinit var albumCover: ImageView
private var isColorReady: Boolean = false
- private lateinit var color: MediaNotificationProcessor
+ private var color: Int = 0
private lateinit var song: Song
private var colorReceiver: ColorReceiver? = null
private var request: Int = 0
- private val mainActivity get() = activity as MainActivity
+
+ private val layout: Int
+ get() {
+ return when (PreferenceUtil.getInstance(requireContext()).albumCoverStyle) {
+ AlbumCoverStyle.NORMAL -> R.layout.fragment_album_cover
+ 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.FULL_CARD -> R.layout.fragment_album_full_card_cover
+ else -> R.layout.fragment_album_cover
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (arguments != null) {
- song = BundleCompat.getParcelable(requireArguments(), SONG_ARG, Song::class.java)!!
+ song = requireArguments().getParcelable(SONG_ARG)!!
}
}
@@ -107,58 +94,22 @@ class AlbumCoverPagerAdapter(
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
- val view = inflater.inflate(getLayoutWithPlayerTheme(), container, false)
- view.setOnClickListener {
- if (mainActivity.getBottomSheetBehavior().state == STATE_EXPANDED) {
- showLyricsDialog()
- }
+ val finalLayout = when {
+ PreferenceUtil.getInstance(requireContext())
+ .carouselEffect() -> R.layout.fragment_album_carousel_cover
+ else -> layout
+ }
+ val view = inflater.inflate(finalLayout, container, false)
+ albumCover = view.findViewById(R.id.player_image)
+ albumCover.setOnClickListener {
+ NavigationUtil.goToLyrics(requireActivity())
}
return view
}
- private fun showLyricsDialog() {
- lifecycleScope.launch(Dispatchers.IO) {
- val data: String? = MusicUtil.getLyrics(song)
- withContext(Dispatchers.Main) {
- MaterialAlertDialogBuilder(
- requireContext(),
- com.google.android.material.R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
- ).apply {
- setTitle(song.title)
- setMessage(if (data.isNullOrEmpty()) "No lyrics found" else data)
- setNegativeButton(R.string.synced_lyrics) { _, _ ->
- goToLyrics(requireActivity())
- }
- show()
- }
- }
- }
- }
-
- 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
- } else {
- when (PreferenceUtil.albumCoverStyle) {
- AlbumCoverStyle.Normal -> R.layout.fragment_album_cover
- 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.Full -> R.layout.fragment_album_full_cover
- AlbumCoverStyle.FullCard -> R.layout.fragment_album_full_card_cover
- }
- }
- }
- }
- }
-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- loadAlbumCover(albumCover = view.findViewById(R.id.player_image))
+ loadAlbumCover()
}
override fun onDestroyView() {
@@ -166,21 +117,18 @@ class AlbumCoverPagerAdapter(
colorReceiver = null
}
- private fun loadAlbumCover(albumCover: ImageView) {
- Glide.with(this)
- .asBitmapPalette()
- .songCoverOptions(song)
- //.checkIgnoreMediaStore()
- .load(RetroGlideExtension.getSongModel(song))
- .dontAnimate()
+ private fun loadAlbumCover() {
+ SongGlideRequest.Builder.from(Glide.with(requireContext()), song)
+ .checkIgnoreMediaStore(requireContext())
+ .generatePalette(requireContext()).build()
.into(object : RetroMusicColoredTarget(albumCover) {
override fun onColorReady(colors: MediaNotificationProcessor) {
- setColor(colors)
+ setColor(colors.backgroundColor)
}
})
}
- private fun setColor(color: MediaNotificationProcessor) {
+ private fun setColor(color: Int) {
this.color = color
isColorReady = true
if (colorReceiver != null) {
@@ -199,7 +147,7 @@ class AlbumCoverPagerAdapter(
}
interface ColorReceiver {
- fun onColorReady(color: MediaNotificationProcessor, request: Int)
+ fun onColorReady(color: Int, request: Int)
}
companion object {
@@ -208,7 +156,9 @@ class AlbumCoverPagerAdapter(
fun newInstance(song: Song): AlbumCoverFragment {
val frag = AlbumCoverFragment()
- frag.arguments = bundleOf(SONG_ARG to song)
+ val args = Bundle()
+ args.putParcelable(SONG_ARG, song)
+ frag.arguments = args
return frag
}
}
@@ -218,3 +168,4 @@ class AlbumCoverPagerAdapter(
val TAG: String = AlbumCoverPagerAdapter::class.java.simpleName
}
}
+
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumFullWidthAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumFullWidthAdapter.kt
new file mode 100644
index 000000000..0bea5f5be
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumFullWidthAdapter.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017. Alexander Bilchuk
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package code.name.monkey.retromusic.adapter.album
+
+import android.app.Activity
+import android.app.ActivityOptions
+import android.util.DisplayMetrics
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import code.name.monkey.retromusic.R
+import code.name.monkey.retromusic.glide.AlbumGlideRequest
+import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
+import code.name.monkey.retromusic.helper.MusicPlayerRemote
+import code.name.monkey.retromusic.model.Album
+import code.name.monkey.retromusic.util.NavigationUtil
+import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
+import code.name.monkey.retromusic.views.MetalRecyclerViewPager
+import com.bumptech.glide.Glide
+
+class AlbumFullWidthAdapter(
+ private val activity: Activity,
+ private val dataSet: List,
+ metrics: DisplayMetrics
+) : MetalRecyclerViewPager.MetalAdapter(metrics) {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FullMetalViewHolder {
+ return FullMetalViewHolder(
+ LayoutInflater.from(parent.context).inflate(
+ R.layout.pager_item,
+ parent,
+ false
+ )
+ )
+ }
+
+ override fun onBindViewHolder(holder: FullMetalViewHolder, position: Int) {
+ // don't forget about calling supper.onBindViewHolder!
+ super.onBindViewHolder(holder, position)
+ val album = dataSet[position]
+ holder.title?.text = getAlbumTitle(album)
+ holder.text?.text = getAlbumText(album)
+ holder.playSongs?.setOnClickListener {
+ album.songs?.let { songs ->
+ MusicPlayerRemote.openQueue(
+ songs,
+ 0,
+ true
+ )
+ }
+ }
+ loadAlbumCover(album, holder)
+ }
+
+ private fun getAlbumTitle(album: Album): String? {
+ return album.title
+ }
+
+ private fun getAlbumText(album: Album): String? {
+ return album.artistName
+ }
+
+ private fun loadAlbumCover(album: Album, holder: FullMetalViewHolder) {
+ if (holder.image == null) {
+ return
+ }
+ AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
+ .checkIgnoreMediaStore(activity)
+ .generatePalette(activity)
+ .build()
+ .into(object : RetroMusicColoredTarget(holder.image!!) {
+ override fun onColorReady(colors: MediaNotificationProcessor) {
+
+ }
+ })
+ }
+
+ override fun getItemCount(): Int {
+ return dataSet.size
+ }
+
+ inner class FullMetalViewHolder(itemView: View) :
+ MetalRecyclerViewPager.MetalViewHolder(itemView) {
+
+ override fun onClick(v: View?) {
+ val activityOptions = ActivityOptions.makeSceneTransitionAnimation(
+ activity,
+ imageContainerCard ?: image,
+ "${activity.getString(R.string.transition_album_art)}_${dataSet[layoutPosition].id}"
+ )
+ NavigationUtil.goToAlbumOptions(activity, dataSet[layoutPosition].id, activityOptions)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/album/HorizontalAlbumAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/album/HorizontalAlbumAdapter.kt
index 7a9f7fa6e..04c4deb80 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/album/HorizontalAlbumAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/HorizontalAlbumAdapter.kt
@@ -1,39 +1,25 @@
-/*
- * 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.album
+import android.graphics.drawable.Drawable
import android.view.View
import android.view.ViewGroup
-import androidx.fragment.app.FragmentActivity
-import code.name.monkey.retromusic.glide.RetroGlideExtension
-import code.name.monkey.retromusic.glide.RetroGlideExtension.albumCoverOptions
-import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
+import androidx.appcompat.app.AppCompatActivity
+import code.name.monkey.appthemehelper.util.ATHUtil
+import code.name.monkey.retromusic.glide.AlbumGlideRequest
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.helper.HorizontalAdapterHelper
-import code.name.monkey.retromusic.interfaces.IAlbumClickListener
+import code.name.monkey.retromusic.interfaces.CabHolder
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,
+ activity: AppCompatActivity,
dataSet: List,
- albumClickListener: IAlbumClickListener
+ cabHolder: CabHolder?
) : AlbumAdapter(
- activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, albumClickListener
+ activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, cabHolder
) {
override fun createViewHolder(view: View, viewType: Int): ViewHolder {
@@ -42,30 +28,35 @@ class HorizontalAlbumAdapter(
return ViewHolder(view)
}
- override fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) {
- // holder.title?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorPrimary))
- // holder.text?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorSecondary))
+ override fun setColors(color: Int, holder: ViewHolder) {
+ holder.title?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorPrimary))
+ holder.text?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorSecondary))
}
override fun loadAlbumCover(album: Album, holder: ViewHolder) {
if (holder.image == null) return
- Glide.with(activity)
- .asBitmapPalette()
- .albumCoverOptions(album.safeGetFirstSong())
- .load(RetroGlideExtension.getSongModel(album.safeGetFirstSong()))
+ AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
+ .checkIgnoreMediaStore(activity)
+ .generatePalette(activity)
+ .build()
.into(object : RetroMusicColoredTarget(holder.image!!) {
+ override fun onLoadCleared(placeholder: Drawable?) {
+ super.onLoadCleared(placeholder)
+ setColors(albumArtistFooterColor, holder)
+ }
+
override fun onColorReady(colors: MediaNotificationProcessor) {
- setColors(colors, holder)
+ setColors(colors.backgroundColor,holder)
}
})
}
- override fun getAlbumText(album: Album): String {
+ override fun getAlbumText(album: Album): String? {
return MusicUtil.getYearString(album.year)
}
override fun getItemViewType(position: Int): Int {
- return HorizontalAdapterHelper.getItemViewType(position, itemCount)
+ return HorizontalAdapterHelper.getItemViewtype(position, itemCount)
}
override fun getItemCount(): Int {
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt
index e1ea2ca4c..e80b3ac94 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt
@@ -1,79 +1,56 @@
-/*
- * 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.artist
-import android.annotation.SuppressLint
+import android.app.ActivityOptions
import android.content.res.ColorStateList
-import android.content.res.Resources
+import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
-import androidx.core.view.isVisible
-import androidx.fragment.app.FragmentActivity
+import androidx.appcompat.app.AppCompatActivity
+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.adapter.base.AbsMultiSelectAdapter
+import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import code.name.monkey.retromusic.extensions.hide
-import code.name.monkey.retromusic.glide.RetroGlideExtension
-import code.name.monkey.retromusic.glide.RetroGlideExtension.artistImageOptions
-import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
+import code.name.monkey.retromusic.glide.ArtistGlideRequest
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.CabHolder
import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil
-import code.name.monkey.retromusic.util.PreferenceUtil
+import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import com.bumptech.glide.Glide
import me.zhanghai.android.fastscroll.PopupTextProvider
+import java.util.*
class ArtistAdapter(
- override val activity: FragmentActivity,
+ val activity: AppCompatActivity,
var dataSet: List,
var itemLayoutRes: Int,
- val IArtistClickListener: IArtistClickListener,
- val IAlbumArtistClickListener: IAlbumArtistClickListener? = null
-) : AbsMultiSelectAdapter(activity, R.menu.menu_media_selection),
- PopupTextProvider {
-
- var albumArtistsOnly = false
+ cabHolder: CabHolder?
+) : AbsMultiSelectAdapter(
+ activity, cabHolder, R.menu.menu_media_selection
+), PopupTextProvider {
init {
this.setHasStableIds(true)
}
- @SuppressLint("NotifyDataSetChanged")
fun swapDataSet(dataSet: List) {
this.dataSet = dataSet
notifyDataSetChanged()
- albumArtistsOnly = PreferenceUtil.albumArtistsOnly
}
override fun getItemId(position: Int): Long {
- return dataSet[position].id
+ return dataSet[position].id.toLong()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
- val view =
- try {
- LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)
- } catch (e: Resources.NotFoundException) {
- LayoutInflater.from(activity).inflate(R.layout.item_grid_circle, parent, false)
- }
+ val view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)
return createViewHolder(view)
}
@@ -87,37 +64,39 @@ 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)
}
- private fun setColors(processor: MediaNotificationProcessor, holder: ViewHolder) {
- holder.mask?.backgroundTintList = ColorStateList.valueOf(processor.primaryTextColor)
+ fun setColors(color: Int, holder: ViewHolder) {
if (holder.paletteColorContainer != null) {
- holder.paletteColorContainer?.setBackgroundColor(processor.backgroundColor)
- holder.title?.setTextColor(processor.primaryTextColor)
+ holder.paletteColorContainer?.setBackgroundColor(color)
+ holder.title?.setTextColor(
+ MaterialValueHelper.getPrimaryTextColor(
+ activity, ColorUtil.isColorLight(
+ color
+ )
+ )
+ )
}
- holder.imageContainerCard?.setCardBackgroundColor(processor.backgroundColor)
+ holder.imageContainerCard?.setCardBackgroundColor(color)
+ holder.mask?.backgroundTintList = ColorStateList.valueOf(color)
}
private fun loadArtistImage(artist: Artist, holder: ViewHolder) {
if (holder.image == null) {
return
}
- Glide.with(activity)
- .asBitmapPalette()
- .artistImageOptions(artist)
- .load(RetroGlideExtension.getArtistModel(artist))
- .transition(RetroGlideExtension.getDefaultTransition())
+ ArtistGlideRequest.Builder.from(Glide.with(activity), artist)
+ .generatePalette(activity)
+ .build()
.into(object : RetroMusicColoredTarget(holder.image!!) {
+ override fun onLoadCleared(placeholder: Drawable?) {
+ super.onLoadCleared(placeholder)
+ setColors(defaultFooterColor, holder)
+ }
+
override fun onColorReady(colors: MediaNotificationProcessor) {
- setColors(colors, holder)
+ setColors(colors.backgroundColor,holder)
}
})
}
@@ -126,22 +105,21 @@ class ArtistAdapter(
return dataSet.size
}
- override fun getIdentifier(position: Int): Artist {
+ override fun getIdentifier(position: Int): Artist? {
return dataSet[position]
}
- override fun getName(model: Artist): String {
- return model.name
+ override fun getName(artist: Artist): String {
+ return artist.name
}
override fun onMultipleItemAction(
- menuItem: MenuItem,
- selection: List
+ menuItem: MenuItem, selection: ArrayList
) {
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
}
- private fun getSongList(artists: List): List {
+ private fun getSongList(artists: List): ArrayList {
val songs = ArrayList()
for (artist in artists) {
songs.addAll(artist.songs) // maybe async in future?
@@ -157,10 +135,11 @@ class ArtistAdapter(
return MusicUtil.getSectionName(dataSet[position].name)
}
- inner class ViewHolder(itemView: View) : code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder(itemView) {
+ inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
init {
- menu?.isVisible = false
+ setImageTransitionName(activity.getString(R.string.transition_artist_image))
+ menu?.visibility = View.GONE
}
override fun onClick(v: View?) {
@@ -168,19 +147,20 @@ class ArtistAdapter(
if (isInQuickSelectMode) {
toggleChecked(layoutPosition)
} else {
- val artist = dataSet[layoutPosition]
- image?.let {
- if (albumArtistsOnly && IAlbumArtistClickListener != null) {
- IAlbumArtistClickListener.onAlbumArtist(artist.name, imageContainer ?: it)
- } else {
- IArtistClickListener.onArtist(artist.id, imageContainer ?: it)
- }
- }
+ val activityOptions = ActivityOptions.makeSceneTransitionAnimation(
+ activity,
+ imageContainerCard ?: image,
+ "${activity.getString(R.string.transition_artist_image)}_${dataSet[layoutPosition].id}"
+ )
+ NavigationUtil.goToArtistOptions(
+ activity, dataSet[layoutPosition].id, activityOptions
+ )
}
}
override fun onLongClick(v: View?): Boolean {
- return toggleChecked(layoutPosition)
+ toggleChecked(layoutPosition)
+ return super.onLongClick(v)
}
}
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/backup/BackupAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/backup/BackupAdapter.kt
deleted file mode 100644
index adfcecf1b..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/backup/BackupAdapter.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-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,
- val backupClickedListener: BackupClickedListener
-) : RecyclerView.Adapter() {
-
- 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) {
- 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
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.java b/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.java
new file mode 100644
index 000000000..2aebe6c92
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.java
@@ -0,0 +1,133 @@
+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 com.afollestad.materialcab.MaterialCab;
+
+import java.util.ArrayList;
+
+import code.name.monkey.retromusic.R;
+import code.name.monkey.retromusic.interfaces.CabHolder;
+
+
+public abstract class AbsMultiSelectAdapter extends RecyclerView.Adapter
+ implements MaterialCab.Callback {
+
+ @Nullable
+ private final CabHolder cabHolder;
+ private final Context context;
+ private MaterialCab cab;
+ private ArrayList checked;
+ private int menuRes;
+
+ public AbsMultiSelectAdapter(@NonNull Context context, @Nullable CabHolder cabHolder, @MenuRes int menuRes) {
+ this.cabHolder = cabHolder;
+ 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 (cabHolder != 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, ArrayList selection);
+
+ protected void setMultiSelectMenuRes(@MenuRes int menuRes) {
+ this.menuRes = menuRes;
+ }
+
+ protected boolean toggleChecked(final int position) {
+ if (cabHolder != 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 (cabHolder != null) {
+ if (cab == null || !cab.isActive()) {
+ cab = cabHolder.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));
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.kt
deleted file mode 100644
index bb7b8491c..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-package code.name.monkey.retromusic.adapter.base
-
-import android.graphics.Color
-import android.view.ActionMode
-import android.view.Menu
-import android.view.MenuItem
-import androidx.activity.OnBackPressedCallback
-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.databinding.NumberRollViewBinding
-import code.name.monkey.retromusic.views.NumberRollView
-
-abstract class AbsMultiSelectAdapter(
- open val activity: FragmentActivity, @MenuRes menuRes: Int,
-) : RecyclerView.Adapter(), ActionMode.Callback {
- var actionMode: ActionMode? = null
- private val checked: MutableList
- private var menuRes: Int
-
- override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
- val inflater = mode?.menuInflater
- inflater?.inflate(menuRes, menu)
- return true
- }
-
- override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
- return false
- }
-
- override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
- if (item?.itemId == R.id.action_multi_select_adapter_check_all) {
- checkAll()
- } else {
- onMultipleItemAction(item!!, ArrayList(checked))
- actionMode?.finish()
- clearChecked()
- }
- return true
- }
-
- override fun onDestroyActionMode(mode: ActionMode?) {
- clearChecked()
- activity.window.statusBarColor = when {
- VersionUtils.hasMarshmallow() -> Color.TRANSPARENT
- else -> Color.BLACK
- }
- actionMode = null
- onBackPressedCallback.remove()
- }
-
- private fun checkAll() {
- if (actionMode != 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() = actionMode != null
-
- protected abstract fun onMultipleItemAction(menuItem: MenuItem, selection: List)
- protected fun setMultiSelectMenuRes(@MenuRes menuRes: Int) {
- this.menuRes = menuRes
- }
-
- protected fun toggleChecked(position: Int): Boolean {
- val identifier = getIdentifier(position) ?: return false
- if (!checked.remove(identifier)) {
- checked.add(identifier)
- }
- notifyItemChanged(position)
- updateCab()
- return true
- }
-
- private fun clearChecked() {
- checked.clear()
- notifyDataSetChanged()
- }
-
- private fun updateCab() {
- if (actionMode == null) {
- actionMode = activity.startActionMode(this)?.apply {
- customView = NumberRollViewBinding.inflate(activity.layoutInflater).root
- }
- activity.onBackPressedDispatcher.addCallback(onBackPressedCallback)
- }
- val size = checked.size
- when {
- size <= 0 -> {
- actionMode?.finish()
- }
- else -> {
- actionMode?.customView?.findViewById(R.id.selection_mode_number)
- ?.setNumber(size, true)
- }
- }
- }
-
- init {
- checked = ArrayList()
- this.menuRes = menuRes
- }
-
- private val onBackPressedCallback = object : OnBackPressedCallback(true) {
- override fun handleOnBackPressed() {
- if (actionMode != null) {
- actionMode?.finish()
- remove()
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java b/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java
index d68186c0f..42a76d011 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java
@@ -16,13 +16,14 @@ package code.name.monkey.retromusic.adapter.base;
import android.graphics.Color;
import android.view.View;
-import android.widget.FrameLayout;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
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 com.google.android.material.card.MaterialCardView;
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableSwipeableItemViewHolder;
@@ -42,10 +43,13 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
public ImageView image;
@Nullable
- public MaterialCardView imageContainerCard;
+ public ImageView playerImage;
@Nullable
- public FrameLayout imageContainer;
+ public ViewGroup imageContainer;
+
+ @Nullable
+ public MaterialCardView imageContainerCard;
@Nullable
public TextView imageText;
@@ -57,16 +61,19 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
public View mask;
@Nullable
- public AppCompatImageView menu;
+ public View menu;
@Nullable
public View paletteColorContainer;
@Nullable
- public TextView text;
+ public ImageButton playSongs;
@Nullable
- public TextView text2;
+ public RecyclerView recyclerView;
+
+ @Nullable
+ public TextView text;
@Nullable
public TextView time;
@@ -78,20 +85,22 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
super(itemView);
title = itemView.findViewById(R.id.title);
text = itemView.findViewById(R.id.text);
- text2 = itemView.findViewById(R.id.text2);
image = itemView.findViewById(R.id.image);
+ playerImage = itemView.findViewById(R.id.player_image);
time = itemView.findViewById(R.id.time);
imageText = itemView.findViewById(R.id.imageText);
+ imageContainer = itemView.findViewById(R.id.imageContainer);
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);
+ playSongs = itemView.findViewById(R.id.playSongs);
dummyContainer = itemView.findViewById(R.id.dummy_view);
if (imageContainerCard != null) {
@@ -101,7 +110,6 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
itemView.setOnLongClickListener(this);
}
- @Nullable
@Override
public View getSwipeableContainerView() {
return null;
@@ -109,6 +117,7 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
@Override
public void onClick(View v) {
+
}
@Override
@@ -117,12 +126,11 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
}
public void setImageTransitionName(@NonNull String transitionName) {
- itemView.setTransitionName(transitionName);
- /* if (imageContainerCard != null) {
- imageContainerCard.setTransitionName(transitionName);
- }
- if (image != null) {
- image.setTransitionName(transitionName);
- }*/
+ if (imageContainerCard != null) {
+ imageContainerCard.setTransitionName(transitionName);
+ }
+ if (image != null) {
+ image.setTransitionName(transitionName);
+ }
}
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/LegacyPlaylistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/LegacyPlaylistAdapter.kt
deleted file mode 100644
index 19d681ecf..000000000
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/LegacyPlaylistAdapter.kt
+++ /dev/null
@@ -1,65 +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.playlist
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.fragment.app.FragmentActivity
-import androidx.recyclerview.widget.RecyclerView
-import code.name.monkey.retromusic.model.Playlist
-import code.name.monkey.retromusic.util.MusicUtil
-
-class LegacyPlaylistAdapter(
- private val activity: FragmentActivity,
- private var list: List,
- private val layoutRes: Int,
- private val playlistClickListener: PlaylistClickListener
-) :
- RecyclerView.Adapter() {
-
- fun swapData(list: List) {
- this.list = list
- notifyDataSetChanged()
- }
-
- class ViewHolder(itemView: View) : code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder(itemView)
-
- override fun onCreateViewHolder(
- parent: ViewGroup,
- viewType: Int
- ): ViewHolder {
- return ViewHolder(
- LayoutInflater.from(parent.context).inflate(layoutRes, parent, false)
- )
- }
-
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- val playlist: Playlist = list[position]
- holder.title?.text = playlist.name
- holder.text?.text = MusicUtil.getPlaylistInfoString(activity, playlist.getSongs())
- holder.itemView.setOnClickListener {
- playlistClickListener.onPlaylistClick(playlist)
- }
- }
-
- override fun getItemCount(): Int {
- return list.size
- }
-
- interface PlaylistClickListener {
- fun onPlaylistClick(playlist: Playlist)
- }
-}
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt
index 6e5b0c001..a550a1fb3 100755
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt
@@ -1,70 +1,64 @@
-/*
- * 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.playlist
+import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.Drawable
+import android.os.AsyncTask
+import android.text.TextUtils
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
-import androidx.core.view.isGone
-import androidx.core.view.setPadding
-import androidx.fragment.app.FragmentActivity
+import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
-import code.name.monkey.retromusic.db.PlaylistEntity
-import code.name.monkey.retromusic.db.PlaylistWithSongs
-import code.name.monkey.retromusic.db.toSongs
-import code.name.monkey.retromusic.extensions.dipToPix
-import code.name.monkey.retromusic.glide.RetroGlideExtension.playlistOptions
-import code.name.monkey.retromusic.glide.playlistPreview.PlaylistPreview
-import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder
+import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
+import code.name.monkey.retromusic.dialogs.ClearSmartPlaylistDialog
+import code.name.monkey.retromusic.dialogs.DeletePlaylistDialog
+import code.name.monkey.retromusic.extensions.hide
+import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
-import code.name.monkey.retromusic.interfaces.IPlaylistClickListener
+import code.name.monkey.retromusic.interfaces.CabHolder
+import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
+import code.name.monkey.retromusic.model.AbsCustomPlaylist
+import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.model.Song
+import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
+import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist
+import code.name.monkey.retromusic.util.AutoGeneratedPlaylistBitmap
import code.name.monkey.retromusic.util.MusicUtil
-import code.name.monkey.retromusic.util.PreferenceUtil
-import com.bumptech.glide.Glide
-import me.zhanghai.android.fastscroll.PopupTextProvider
+import code.name.monkey.retromusic.util.NavigationUtil
+import code.name.monkey.retromusic.util.RetroColorUtil
+import java.util.*
class PlaylistAdapter(
- override val activity: FragmentActivity,
- var dataSet: List,
+ private val activity: AppCompatActivity,
+ var dataSet: List,
private var itemLayoutRes: Int,
- private val listener: IPlaylistClickListener
-) : AbsMultiSelectAdapter(
+ cabHolder: CabHolder?
+) : AbsMultiSelectAdapter(
activity,
+ cabHolder,
R.menu.menu_playlists_selection
-), PopupTextProvider {
+) {
+
init {
setHasStableIds(true)
}
- fun swapDataSet(dataSet: List) {
+ fun swapDataSet(dataSet: List) {
this.dataSet = dataSet
notifyDataSetChanged()
}
override fun getItemId(position: Int): Long {
- return dataSet[position].playlistEntity.playListId
+ return dataSet[position].id.toLong()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@@ -72,66 +66,83 @@ class PlaylistAdapter(
return createViewHolder(view)
}
- private fun createViewHolder(view: View): ViewHolder {
+ fun createViewHolder(view: View): ViewHolder {
return ViewHolder(view)
}
- private fun getPlaylistTitle(playlist: PlaylistEntity): String {
- return playlist.playlistName.ifEmpty { "-" }
+ private fun getPlaylistTitle(playlist: Playlist): String {
+ return if (TextUtils.isEmpty(playlist.name)) "-" else playlist.name
}
- 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)
+ private fun getPlaylistText(playlist: Playlist): String {
+ return MusicUtil.getPlaylistInfoString(activity, getSongs(playlist))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val playlist = dataSet[position]
holder.itemView.isActivated = isChecked(playlist)
- holder.title?.text = getPlaylistTitle(playlist.playlistEntity)
+ holder.title?.text = getPlaylistTitle(playlist)
holder.text?.text = getPlaylistText(playlist)
- holder.menu?.isGone = isChecked(playlist)
- if (itemLayoutRes == R.layout.item_list) {
- holder.image?.setPadding(activity.dipToPix(8F).toInt())
- holder.image?.setImageDrawable(getIconRes())
+ holder.image?.setImageDrawable(getIconRes(playlist))
+ val isChecked = isChecked(playlist)
+ if (isChecked) {
+ holder.menu?.hide()
} else {
- Glide.with(activity)
- .load(PlaylistPreview(playlist))
- .playlistOptions()
- .into(holder.image!!)
+ holder.menu?.show()
}
+ //PlaylistBitmapLoader(this, holder, playlist).execute()
}
- private fun getIconRes(): Drawable = TintHelper.createTintedDrawable(
- activity,
- R.drawable.ic_playlist_play,
- ATHUtil.resolveColor(activity, android.R.attr.colorControlNormal)
- )
+ private fun getIconRes(playlist: Playlist): Drawable {
+ return if (MusicUtil.isFavoritePlaylist(activity, playlist))
+ TintHelper.createTintedDrawable(
+ activity,
+ R.drawable.ic_favorite_white_24dp,
+ ThemeStore.accentColor(activity)
+ )
+ else TintHelper.createTintedDrawable(
+ activity,
+ R.drawable.ic_playlist_play_white_24dp,
+ ATHUtil.resolveColor(activity, R.attr.colorControlNormal)
+ )
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return if (dataSet[position] is AbsSmartPlaylist) SMART_PLAYLIST else DEFAULT_PLAYLIST
+ }
override fun getItemCount(): Int {
return dataSet.size
}
- override fun getIdentifier(position: Int): PlaylistWithSongs {
+ override fun getIdentifier(position: Int): Playlist? {
return dataSet[position]
}
- override fun getName(model: PlaylistWithSongs): String {
- return model.playlistEntity.playlistName
+ override fun getName(playlist: Playlist): String {
+ return playlist.name
}
- override fun onMultipleItemAction(menuItem: MenuItem, selection: List) {
+ override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) {
when (menuItem.itemId) {
+ R.id.action_delete_playlist -> {
+ var i = 0
+ while (i < selection.size) {
+ val playlist = selection[i]
+ if (playlist is AbsSmartPlaylist) {
+ ClearSmartPlaylistDialog.create(playlist).show(
+ activity.supportFragmentManager, "CLEAR_PLAYLIST_" + playlist.name
+ )
+ selection.remove(playlist)
+ i--
+ }
+ i++
+ }
+ if (selection.size > 0) {
+ DeletePlaylistDialog.create(selection)
+ .show(activity.supportFragmentManager, "DELETE_PLAYLIST")
+ }
+ }
else -> SongsMenuHelper.handleMenuClick(
activity,
getSongList(selection),
@@ -140,21 +151,60 @@ class PlaylistAdapter(
}
}
- private fun getSongList(playlists: List): List {
- val songs = mutableListOf()
- playlists.forEach {
- songs.addAll(it.songs.toSongs())
+ private fun getSongList(playlists: List): ArrayList {
+ val songs = ArrayList()
+ for (playlist in playlists) {
+ if (playlist is AbsCustomPlaylist) {
+ songs.addAll(playlist.getSongs(activity))
+ } else {
+ songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
+ }
}
return songs
}
- inner class ViewHolder(itemView: View) : code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder(itemView) {
+ private fun getSongs(playlist: Playlist): ArrayList {
+ val songs = ArrayList()
+ if (playlist is AbsSmartPlaylist) {
+ songs.addAll(playlist.getSongs(activity))
+ } else {
+ songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
+ }
+ return 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 playlist = dataSet[layoutPosition]
val popupMenu = PopupMenu(activity, view)
- popupMenu.inflate(R.menu.menu_item_playlist)
+ popupMenu.inflate(
+ if (itemViewType == SMART_PLAYLIST) R.menu.menu_item_smart_playlist
+ else R.menu.menu_item_playlist
+ )
+ if (playlist is LastAddedPlaylist) {
+ popupMenu.menu.findItem(R.id.action_clear_playlist).isVisible = false
+ }
popupMenu.setOnMenuItemClickListener { item ->
- PlaylistMenuHelper.handleMenuClick(activity, dataSet[layoutPosition], item)
+ if (item.itemId == R.id.action_clear_playlist) {
+ if (playlist is AbsSmartPlaylist) {
+ ClearSmartPlaylistDialog.create(playlist).show(
+ activity.supportFragmentManager,
+ "CLEAR_SMART_PLAYLIST_" + playlist.name
+ )
+ return@setOnMenuItemClickListener true
+ }
+ }
+ PlaylistMenuHelper.handleMenuClick(
+ activity, dataSet[layoutPosition], item
+ )
}
popupMenu.show()
}
@@ -169,8 +219,8 @@ class PlaylistAdapter(
if (isInQuickSelectMode) {
toggleChecked(layoutPosition)
} else {
- itemView.transitionName = "playlist"
- listener.onPlaylistClick(dataSet[layoutPosition], itemView)
+ val playlist = dataSet[layoutPosition]
+ NavigationUtil.goToPlaylistNew(activity, playlist)
}
}
@@ -180,7 +230,33 @@ class PlaylistAdapter(
}
}
+ class PlaylistBitmapLoader(
+ private var adapter: PlaylistAdapter,
+ private var viewHolder: ViewHolder,
+ private var playlist: Playlist
+ ) : AsyncTask() {
+
+ override fun doInBackground(vararg params: Void?): Bitmap {
+ val songs = PlaylistSongsLoader.getPlaylistSongList(adapter.activity, playlist)
+ return AutoGeneratedPlaylistBitmap.getBitmap(adapter.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(adapter.activity, R.attr.colorSurface)
+ )
+ viewHolder.paletteColorContainer?.setBackgroundColor(color)
+ }
+ }
+
companion object {
val TAG: String = PlaylistAdapter::class.java.simpleName
+ private const val SMART_PLAYLIST = 0
+ private const val DEFAULT_PLAYLIST = 1
}
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/AbsOffsetSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/AbsOffsetSongAdapter.kt
index 2d5fd724f..d1141341c 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/AbsOffsetSongAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/AbsOffsetSongAdapter.kt
@@ -1,33 +1,21 @@
-/*
- * 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.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
-import androidx.fragment.app.FragmentActivity
+import androidx.appcompat.app.AppCompatActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote
+import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.model.Song
abstract class AbsOffsetSongAdapter(
- activity: FragmentActivity,
+ activity: AppCompatActivity,
dataSet: MutableList,
- @LayoutRes itemLayoutRes: Int
-) : SongAdapter(activity, dataSet, itemLayoutRes) {
+ @LayoutRes itemLayoutRes: Int,
+ cabHolder: CabHolder?
+) : SongAdapter(activity, dataSet, itemLayoutRes, cabHolder) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongAdapter.ViewHolder {
if (viewType == OFFSET_ITEM) {
@@ -88,4 +76,4 @@ abstract class AbsOffsetSongAdapter(
const val OFFSET_ITEM = 0
const val SONG = 1
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt
index bf85fe28b..74a2f7ebd 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt
@@ -1,121 +1,82 @@
-/*
- * 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.core.view.isVisible
-import androidx.fragment.app.FragmentActivity
-import androidx.lifecycle.lifecycleScope
+import androidx.appcompat.app.AppCompatActivity
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.db.toSongsEntity
-import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog
-import code.name.monkey.retromusic.fragments.LibraryViewModel
+import code.name.monkey.retromusic.R.menu
+import code.name.monkey.retromusic.dialogs.RemoveFromPlaylistDialog
+import code.name.monkey.retromusic.interfaces.CabHolder
+import code.name.monkey.retromusic.model.PlaylistSong
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.ViewUtil
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 kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import org.koin.androidx.viewmodel.ext.android.viewModel
+import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags
class OrderablePlaylistSongAdapter(
- private val playlistId: Long,
- activity: FragmentActivity,
- dataSet: MutableList,
+ activity: AppCompatActivity,
+ dataSet: ArrayList,
itemLayoutRes: Int,
-) : SongAdapter(activity, dataSet, itemLayoutRes),
- DraggableItemAdapter {
-
- val libraryViewModel: LibraryViewModel by activity.viewModel()
+ cabHolder: CabHolder?,
+ private val onMoveItemListener: OnMoveItemListener?
+) : PlaylistSongAdapter(
+ activity, dataSet, itemLayoutRes, cabHolder
+), DraggableItemAdapter {
init {
- 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 dataSet[position].id
+ setMultiSelectMenuRes(menu.menu_playlists_songs_selection)
}
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
return ViewHolder(view)
}
- override fun onMultipleItemAction(menuItem: MenuItem, selection: List) {
- when (menuItem.itemId) {
- R.id.action_remove_from_playlist -> RemoveSongFromPlaylistDialog.create(
- selection.toSongsEntity(
- playlistId
- )
- )
- .show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
+ override fun getItemId(position: Int): Long {
+ var positionFinal = position
+ positionFinal--
- else -> super.onMultipleItemAction(menuItem, selection)
+ var long: Long = 0
+ if (positionFinal < 0) {
+ long = -2
+ } else {
+ if (dataSet[positionFinal] is PlaylistSong) {
+ long = (dataSet[positionFinal] as PlaylistSong).idInPlayList.toLong()
+ }
}
+ return long
}
- 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 onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) {
+ when (menuItem.itemId) {
+ R.id.action_remove_from_playlist -> {
+ RemoveFromPlaylistDialog.create(selection as ArrayList)
+ .show(activity.supportFragmentManager, "ADD_PLAYLIST")
+ return
}
-
- override fun onSongMenuItemClick(item: MenuItem): Boolean {
- when (item.itemId) {
- R.id.action_remove_from_playlist -> {
- RemoveSongFromPlaylistDialog.create(song.toSongEntity(playlistId))
- .show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
- return true
- }
- }
- return super.onSongMenuItemClick(item)
- }
-
- init {
- dragView?.isVisible = true
}
+ super.onMultipleItemAction(menuItem, selection)
}
override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean {
- if (isInQuickSelectMode) {
- return false
- }
- return ViewUtil.hitTest(holder.imageText!!, x, y) || ViewUtil.hitTest(
- holder.dragView!!,
- x,
- y
- )
+ 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) {
- dataSet.add(toPosition, dataSet.removeAt(fromPosition))
- }
-
- override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange? {
- return null
+ if (onMoveItemListener != null && fromPosition != toPosition) {
+ onMoveItemListener.onMoveItem(fromPosition - 1, toPosition - 1)
+ }
}
override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean {
- return true
+ return dropPosition > 0
}
override fun onItemDragStarted(position: Int) {
@@ -126,9 +87,53 @@ class OrderablePlaylistSongAdapter(
notifyDataSetChanged()
}
- fun saveSongs(playlistEntity: PlaylistEntity) {
- activity.lifecycleScope.launch(Dispatchers.IO) {
- libraryViewModel.insertSongs(dataSet.toSongsEntity(playlistEntity))
+ interface OnMoveItemListener {
+ fun onMoveItem(fromPosition: Int, toPosition: Int)
+ }
+
+ inner class ViewHolder(itemView: View) : PlaylistSongAdapter.ViewHolder(itemView),
+ DraggableItemViewHolder {
+ @DraggableItemStateFlags
+ private var mDragStateFlags: Int = 0
+
+ override var songMenuRes: Int
+ get() = R.menu.menu_item_playlist_song
+ set(value) {
+ 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 -> {
+ RemoveFromPlaylistDialog.create(song as PlaylistSong)
+ .show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
+ return true
+ }
+ }
+ return super.onSongMenuItemClick(item)
+ }
+
+ @DraggableItemStateFlags
+ override fun getDragStateFlags(): Int {
+ return mDragStateFlags
+ }
+
+ override fun setDragStateFlags(@DraggableItemStateFlags flags: Int) {
+ mDragStateFlags = flags
}
}
+
+ companion object {
+ val TAG: String = OrderablePlaylistSongAdapter::class.java.simpleName
+ }
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt
index 2d387be44..7f8f8ffb1 100644
--- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt
@@ -1,26 +1,11 @@
-/*
- * 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.graphics.PorterDuff.Mode
import android.view.MenuItem
import android.view.View
-import androidx.core.view.isVisible
-import androidx.fragment.app.FragmentActivity
+import android.widget.ImageView
+import androidx.appcompat.app.AppCompatActivity
import code.name.monkey.retromusic.R
-import code.name.monkey.retromusic.glide.RetroGlideExtension
-import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicPlayerRemote.isPlaying
import code.name.monkey.retromusic.helper.MusicPlayerRemote.playNextSong
@@ -28,7 +13,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 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
@@ -37,18 +21,21 @@ import com.h6ah4i.android.widget.advrecyclerview.swipeable.SwipeableItemConstant
import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultAction
import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultActionDefault
import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultActionRemoveItem
+import com.h6ah4i.android.widget.advrecyclerview.swipeable.annotation.SwipeableItemResults
import me.zhanghai.android.fastscroll.PopupTextProvider
class PlayingQueueAdapter(
- activity: FragmentActivity,
+ activity: AppCompatActivity,
dataSet: MutableList