(...);
-}
--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/index.html b/app/src/main/assets/index.html
new file mode 100644
index 000000000..037d52fad
--- /dev/null
+++ b/app/src/main/assets/index.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/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/retro-changelog.html b/app/src/main/assets/retro-changelog.html
index b6b1c411a..c3771c482 100644
--- a/app/src/main/assets/retro-changelog.html
+++ b/app/src/main/assets/retro-changelog.html
@@ -1,466 +1 @@
-
-
-
-
-
-
-
-
-
-
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
-
- Changed profile form image to icon
- New what's new screen
- Added In-App language changer, where you can select language
-
-
Improved
-
- Improved loading of Songs, Albums, Artists, Genres, Playlists
-
-
-
\ No newline at end of file
+
v3.5.010
Added grid size and layout change for songs list
Removed menu options (No use of having only settings)
Added settings option to toolbar
Updated internal library
v3.4.970
Added default cover with accent color
Added date modified option to sort songs
Added toggle bluetooth connection to play on device as soon as it connected
Major code refactor for library tabs
Folder option now in available in tabs
Improved drive mode page
Improved fit, full, card themes for status visibility
Fix tablet version of app
Fix Album and Artist details toolbar full width for better accessibility
Updated internal libraries
v3.4.900
Added playlist search
Added Drive mode
Added Album and Artist layout change option in library
Added Show more album and artist information in details
Added Pixel style scroller bar
Added current now playing share
Fix font issues and colors in some places
Improved Full now playing theme
If you see entire app white or dark or black select same theme in settings to fix
FAQ's
*If you face any UI related issues you clear app data and cache, if its
not working try to
uninstall and install again.
\ No newline at end of file
diff --git a/app/src/main/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/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..f938222dd
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/AboutActivity.kt
@@ -0,0 +1,173 @@
+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.LayoutMode
+import com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.bottomsheets.BottomSheet
+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 assetJsonData: 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)
+ translators.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 -> showChangeLogOptions()
+ R.id.openSource -> NavigationUtil.goToOpenSource(this)
+ R.id.bugReportLink -> NavigationUtil.bugReport(this)
+ R.id.translators -> openUrl("");
+ }
+ }
+
+ private fun showChangeLogOptions() {
+ MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).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>(assetJsonData, 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..7be3cbeb0
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/AlbumDetailsActivity.kt
@@ -0,0 +1,396 @@
+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 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
+ 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(color: Int) {
+ }
+ })
+ }
+
+ 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(color: Int) {
+ setColors(color)
+ }
+ })
+ }
+
+ 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..e0fdbe4a0
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/ArtistDetailActivity.kt
@@ -0,0 +1,347 @@
+package code.name.monkey.retromusic.activities
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.text.Html
+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 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))
+ )
+
+ 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(color: Int) {
+ setColors(color)
+ }
+ })
+ }
+
+ 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..d21171294
--- /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("index.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..5af09dfe6 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,54 +1,47 @@
-/*
- * 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
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 +54,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 +66,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 +88,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) {
- override fun onColorReady(colors: MediaNotificationProcessor) {
- fragment?.setColor(colors)
+ SongGlideRequest.Builder.from(Glide.with(this), song).checkIgnoreMediaStore(this)
+ .generatePalette(this).build().dontAnimate()
+ .into(object : RetroMusicColoredTarget(image) {
+ override fun onColorReady(color: Int) {
+ fragment?.setDark(color)
}
})
}
-}
+}
\ 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..091b8881f
--- /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, BottomSheet(LayoutMode.WRAP_CONTENT))
+ .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, BottomSheet(LayoutMode.WRAP_CONTENT)
+ ).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..ea69fb81b
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.java
@@ -0,0 +1,780 @@
+/*
+ * 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.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.textview.MaterialTextView;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+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.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment;
+import code.name.monkey.retromusic.fragments.mainactivity.AlbumsFragment;
+import code.name.monkey.retromusic.fragments.mainactivity.ArtistsFragment;
+import code.name.monkey.retromusic.fragments.mainactivity.BannerHomeFragment;
+import code.name.monkey.retromusic.fragments.mainactivity.FoldersFragment;
+import code.name.monkey.retromusic.fragments.mainactivity.GenresFragment;
+import code.name.monkey.retromusic.fragments.mainactivity.PlayingQueueFragment;
+import code.name.monkey.retromusic.fragments.mainactivity.PlaylistsFragment;
+import code.name.monkey.retromusic.fragments.mainactivity.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 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);
+ @Nullable
+ MainActivityFragmentCallbacks currentFragment;
+ private boolean blockRequestPermissions = false;
+ private MaterialCab cab;
+ private AppBarLayout mAppBarLayout;
+ private MaterialTextView mAppTitle;
+ private Toolbar mToolbar;
+ private MaterialCardView mToolbarContainer;
+
+ @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();
+ }
+
+ @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();
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ registerReceiver(mBroadcastReceiver, mIntentFilter);
+ PreferenceUtil.getInstance(this).registerOnSharedPreferenceChangedListener(this);
+
+ if (getIntent().hasExtra(EXPAND_PANEL) && PreferenceUtil.getInstance(this).isExpandPanel()) {
+ if (getIntent().getBooleanExtra(EXPAND_PANEL, false)) {
+ expandPanel();
+ getIntent().putExtra(EXPAND_PANEL, false);
+ }
+ }
+ }
+
+ @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);
+ Log.i(TAG, "run: " + Thread.currentThread().getName());
+ mAppTitle.setVisibility(View.GONE);
+ setTitle(R.string.action_search);
+ }
+ }, 3000);
+
+ Locale[] locales = Locale.getAvailableLocales();
+
+ for (Locale l : locales) {
+ Log.d(TAG, "setupToolbar: " + l.toLanguageTag());
+ }
+ }
+}
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/fragments/queue/PlayingQueueFragment.kt b/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt
similarity index 55%
rename from app/src/main/java/code/name/monkey/retromusic/fragments/queue/PlayingQueueFragment.kt
rename to app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt
index 0526ade15..d68e2b96f 100644
--- a/app/src/main/java/code/name/monkey/retromusic/fragments/queue/PlayingQueueFragment.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt
@@ -1,47 +1,28 @@
-/*
- * 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.fragments.queue
+package code.name.monkey.retromusic.activities
import android.content.res.ColorStateList
import android.os.Bundle
-import android.view.View
-import androidx.navigation.fragment.findNavController
+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.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
-import code.name.monkey.retromusic.activities.MainActivity
+import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
-import code.name.monkey.retromusic.databinding.FragmentPlayingQueueBinding
-import code.name.monkey.retromusic.extensions.accentColor
-import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.util.MusicUtil
-import code.name.monkey.retromusic.util.ThemedFastScroller
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
+import kotlinx.android.synthetic.main.activity_playing_queue.*
-class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_queue) {
+open class PlayingQueueActivity : AbsMusicServiceActivity() {
- private var _binding: FragmentPlayingQueueBinding? = null
- private val binding get() = _binding!!
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
@@ -49,9 +30,6 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
private var playingQueueAdapter: PlayingQueueAdapter? = null
private lateinit var linearLayoutManager: LinearLayoutManager
- val mainActivity: MainActivity
- get() = activity as MainActivity
-
private fun getUpNextAndQueueTime(): String {
val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position)
return MusicUtil.buildInfoString(
@@ -60,18 +38,32 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
)
}
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- _binding = FragmentPlayingQueueBinding.bind(view)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setDrawUnderStatusBar()
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_playing_queue)
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setTaskDescriptionColorAuto()
+ setLightNavigationBar(true)
setupToolbar()
setUpRecyclerView()
- binding.clearQueue.setOnClickListener {
+ clearQueue.setOnClickListener {
MusicPlayerRemote.clearQueue()
}
checkForPadding()
- mainActivity.collapsePanel()
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ return when (item.itemId) {
+ android.R.id.home -> {
+ onBackPressed()
+ true
+ }
+ else -> super.onOptionsItemSelected(item)
+ }
}
private fun setUpRecyclerView() {
@@ -79,39 +71,39 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
recyclerViewDragDropManager = RecyclerViewDragDropManager()
recyclerViewSwipeManager = RecyclerViewSwipeManager()
+ val animator = DraggableItemAnimator()
+ animator.supportsChangeAnimations = false
+
playingQueueAdapter = PlayingQueueAdapter(
- requireActivity(),
- MusicPlayerRemote.playingQueue.toMutableList(),
+ this,
+ MusicPlayerRemote.playingQueue,
MusicPlayerRemote.position,
R.layout.item_queue
)
wrappedAdapter = recyclerViewDragDropManager?.createWrappedAdapter(playingQueueAdapter!!)
wrappedAdapter = wrappedAdapter?.let { recyclerViewSwipeManager?.createWrappedAdapter(it) }
- linearLayoutManager = LinearLayoutManager(requireContext())
+ linearLayoutManager = LinearLayoutManager(this)
-
- binding.recyclerView.apply {
- layoutManager = linearLayoutManager
- adapter = wrappedAdapter
- itemAnimator = DraggableItemAnimator()
- recyclerViewTouchActionGuardManager?.attachRecyclerView(this)
- recyclerViewDragDropManager?.attachRecyclerView(this)
- recyclerViewSwipeManager?.attachRecyclerView(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)
- binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0) {
- binding.clearQueue.shrink()
+ clearQueue.shrink()
} else if (dy < 0) {
- binding.clearQueue.extend()
+ clearQueue.extend()
}
}
})
- ThemedFastScroller.create(binding.recyclerView)
+ //ViewUtil.setUpFastScrollRecyclerViewColor(this, recyclerView)
}
private fun checkForPadding() {
@@ -119,7 +111,7 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
override fun onQueueChanged() {
if (MusicPlayerRemote.playingQueue.isEmpty()) {
- findNavController().navigateUp()
+ finish()
return
}
checkForPadding()
@@ -133,7 +125,7 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
}
private fun updateCurrentSong() {
- binding.appBarLayout.toolbar.subtitle = getUpNextAndQueueTime()
+ toolbar.subtitle = getUpNextAndQueueTime()
}
override fun onPlayingMetaChanged() {
@@ -143,15 +135,16 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
private fun updateQueuePosition() {
playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
resetToCurrentPosition()
- binding.appBarLayout.toolbar.subtitle = getUpNextAndQueueTime()
+ toolbar.subtitle = getUpNextAndQueueTime()
}
private fun updateQueue() {
playingQueueAdapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position)
+ resetToCurrentPosition()
}
private fun resetToCurrentPosition() {
- binding.recyclerView.stopScroll()
+ recyclerView.stopScroll()
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
}
@@ -177,33 +170,20 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
}
playingQueueAdapter = null
super.onDestroy()
- if (MusicPlayerRemote.playingQueue.isNotEmpty())
- mainActivity.expandPanel()
}
private fun setupToolbar() {
- binding.appBarLayout.toolbar.subtitle = getUpNextAndQueueTime()
- binding.appBarLayout.toolbar.isTitleCentered = false
- binding.clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor())
+ 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(
- requireContext(),
- ColorUtil.isColorLight(accentColor())
- )
+ MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor))
).apply {
- binding.clearQueue.setTextColor(this)
- binding.clearQueue.iconTint = this
- }
- binding.appBarLayout.pinWhenScrolled()
- binding.appBarLayout.toolbar.apply {
- setNavigationOnClickListener {
- findNavController().navigateUp()
- }
- setTitle(R.string.now_playing_queue)
- setTitleTextAppearance(context, R.style.ToolbarTextAppearanceNormal)
- setNavigationIcon(R.drawable.ic_arrow_back)
- ToolbarContentTintHelper.colorBackButton(this)
+ 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..f2a5ee8e1
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/PurchaseActivity.kt
@@ -0,0 +1,155 @@
+package code.name.monkey.retromusic.activities
+
+import android.content.Intent
+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.util.ATHUtil
+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)
+ setStatusbarColorAuto()
+ setNavigationbarColorAuto()
+ setLightNavigationBar(true)
+ toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
+ setSupportActionBar(toolbar)
+
+ restoreButton.isEnabled = false
+ purchaseButton.isEnabled = false
+
+ billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
+
+ MaterialUtil.setTint(restoreButton, false)
+ 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)
+ }
+ }
+
+ 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..ae6334708 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,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.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 +47,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) {
- override fun onColorReady(colors: MediaNotificationProcessor) {
- setColors(colors.backgroundColor)
+ SongGlideRequest.Builder.from(Glide.with(this), songFinal)
+ .checkIgnoreMediaStore(this@ShareInstagramStory)
+ .generatePalette(this@ShareInstagramStory)
+ .build()
+ .into(object : RetroMusicColoredTarget(image) {
+ override fun onColorReady(color: Int) {
+ val isColorLight = ColorUtil.isColorLight(color)
+ setColors(isColorLight, color)
}
})
- 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..739bd7713
--- /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, BottomSheet(LayoutMode.WRAP_CONTENT)).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, BottomSheet(LayoutMode.WRAP_CONTENT)).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..631db15ca
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewActivity.java
@@ -0,0 +1,102 @@
+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 code.name.monkey.appthemehelper.ThemeStore;
+import code.name.monkey.appthemehelper.util.ATHUtil;
+import code.name.monkey.appthemehelper.util.ColorUtil;
+import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
+import code.name.monkey.retromusic.R;
+import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
+import code.name.monkey.retromusic.util.PreferenceUtil;
+
+public class WhatsNewActivity extends AbsBaseActivity {
+
+ AppBarLayout appBarLayout;
+
+ Toolbar toolbar;
+
+ WebView webView;
+
+ private static 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
+ }
+
+ 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 = findViewById(R.id.webView);
+ toolbar = findViewById(R.id.toolbar);
+ 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 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");
+ }
+ 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..3a84f55bc 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,226 +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.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.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.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 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")
- }
}
}
}
}
- fun getBottomSheetBehavior() = bottomSheetBehavior
-
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)
- 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 +114,264 @@ 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()
+ 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..ed170e63b 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, BottomSheet(LayoutMode.WRAP_CONTENT)).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..426eafb1f
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.java
@@ -0,0 +1,141 @@
+/*
+ * 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 {
+
+ MaterialCheckBox checkBox;
+
+ View dragView;
+
+ 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..459660716
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/TranslatorsAdapter.kt
@@ -0,0 +1,57 @@
+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
+import code.name.monkey.retromusic.views.RetroShapeableImageView
+import com.bumptech.glide.Glide
+
+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
+ Glide.with(image.context)
+ .load(contributor.profileImage)
+ .error(R.drawable.ic_account_white_24dp)
+ .placeholder(R.drawable.ic_account_white_24dp)
+ .dontAnimate()
+ .into(image)
+ }
+ }
+}
\ 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..4cdd335b2 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,40 @@
-/*
- * 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 +56,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 +70,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 onColorReady(colors: MediaNotificationProcessor) {
- setColors(colors, holder)
+ override fun onLoadCleared(placeholder: Drawable?) {
+ super.onLoadCleared(placeholder)
+ //setColors(defaultFooterColor, holder)
+ }
+
+ override fun onColorReady(color: Int) {
+ setColors(color, holder)
}
})
}
@@ -129,28 +127,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 +158,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 +182,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..441a65884 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,35 +5,22 @@ 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
+import java.util.*
class AlbumCoverPagerAdapter(
fragmentManager: FragmentManager,
- private val dataSet: List
+ private val dataSet: ArrayList
) : CustomFragmentStatePagerAdapter(fragmentManager) {
private var currentColorReceiver: AlbumCoverFragment.ColorReceiver? = null
@@ -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,21 @@ 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 +116,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)
+ override fun onColorReady(color: Int) {
+ setColor(color)
}
})
}
- private fun setColor(color: MediaNotificationProcessor) {
+ private fun setColor(color: Int) {
this.color = color
isColorReady = true
if (colorReceiver != null) {
@@ -199,7 +146,7 @@ class AlbumCoverPagerAdapter(
}
interface ColorReceiver {
- fun onColorReady(color: MediaNotificationProcessor, request: Int)
+ fun onColorReady(color: Int, request: Int)
}
companion object {
@@ -208,7 +155,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 +167,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..8c3b9df8e
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumFullWidthAdapter.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.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(color: Int) {
+ }
+ })
+ }
+
+ 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..e81502135 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,24 @@
-/*
- * 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 +27,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 onColorReady(colors: MediaNotificationProcessor) {
- setColors(colors, holder)
+ override fun onLoadCleared(placeholder: Drawable?) {
+ super.onLoadCleared(placeholder)
+ setColors(albumArtistFooterColor, holder)
+ }
+
+ override fun onColorReady(color: Int) {
+ setColors(color, 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..e54aa8332 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,55 @@
-/*
- * 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.color.MediaNotificationProcessor
+import code.name.monkey.retromusic.util.NavigationUtil
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 +63,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 onColorReady(colors: MediaNotificationProcessor) {
- setColors(colors, holder)
+ override fun onLoadCleared(placeholder: Drawable?) {
+ super.onLoadCleared(placeholder)
+ setColors(defaultFooterColor, holder)
+ }
+
+ override fun onColorReady(color: Int) {
+ setColors(color, holder)
}
})
}
@@ -126,22 +104,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 +134,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 +146,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..f98fa1110
--- /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..deba43f35 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,22 @@ 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
+import java.util.*
class PlayingQueueAdapter(
- activity: FragmentActivity,
- dataSet: MutableList,
+ activity: AppCompatActivity,
+ dataSet: ArrayList,
private var current: Int,
- itemLayoutRes: Int,
-) : SongAdapter(activity, dataSet, itemLayoutRes),
- DraggableItemAdapter