Compare commits

...

205 Commits

Author SHA1 Message Date
Andrew Rabert 4c1bb5375d Cease development 2022-10-14 13:24:20 -04:00
Andrew Rabert f1a9686eb4 Use latest java 2022-03-31 22:11:06 -04:00
Andrew Rabert 279adc166b Update com.github.hannesa2:AndroidSlidingUpPanel to 4.4.1 2022-03-31 22:05:16 -04:00
Andrew Rabert c20a64ab15 Update deps 2022-03-31 21:59:05 -04:00
Andrew Rabert aa4c877046
Merge pull request #105 from masipcat/master
Fix search for some backends
2021-07-14 11:49:28 -04:00
Jordi Masip 09c5d7ee51 Fix search artist for some backends 2021-07-10 18:09:40 +02:00
Andrew Rabert 27b0dc742c AppCompat updates 2021-05-23 14:56:08 -04:00
Andrew Rabert 63df26cdfd Optimize imports 2021-05-23 14:49:37 -04:00
Andrew Rabert 49f15f2a0d Remove unused 2021-05-23 14:48:02 -04:00
Andrew Rabert 2c3c9ede14 update deps 2021-05-23 14:45:21 -04:00
Andrew Rabert ef2e19ebf2 Update deps 2021-05-09 13:18:16 -04:00
Andrew Rabert 4c5406c916 Update deps 2021-02-05 20:10:37 -05:00
Andrew Rabert 072a6d7870 Updates 2020-11-01 19:03:33 -05:00
Andrew Rabert 5ae04b3643
Merge pull request #93 from Refactor4Green/Refactor4Green
Improve energy efficiency by applying Cache Energy Pattern
2020-10-08 17:32:49 -04:00
Andrew Rabert 79ea6cb082
Merge pull request #96 from rerobins/master
Allow User Trusted Credentials
2020-10-08 17:30:34 -04:00
Robert Robinson 8cfae03550 Allow User Trusted Credentials
Subsonic/Ampache are self-hosted applications and should need to be able to support a user defined root certificate if that is installed on the android device.  The default of android applications is to ignore the user trusted credentials.  This patch overrides this by allowing certificates that are signed by certificates added by the user to their device.

See: https://developer.android.com/training/articles/security-config
2020-10-06 20:46:33 -06:00
Andrew Rabert 298a06ab5b optimize images
`oxipng --zopfli --opt max --strip all`
2020-09-21 16:53:03 -04:00
anasofiagribeiro 41d17f960e Refactor4Green - Cache 2020-07-29 10:42:04 +01:00
Andrew Rabert a861e035ab
Merge pull request #90 from the-salami/dark-mode
change day/night themes to support dark mode setting on Android 10+
2020-07-09 18:54:56 -04:00
Morgan Lim 4598f849ff remove unused import added by IDE 2020-07-09 17:03:47 -04:00
Morgan Lim d37a72b2a0 Remove default "light" value for theme in settings_appearance.xml. Change getTheme logic to give a default of day/night on Android 10+. 2020-07-09 17:01:34 -04:00
Andrew Rabert e102dea3d3
Merge pull request #88 from the-salami/widget-fix
fix invisible back/forward buttons on widgets (#81)
2020-07-09 16:02:58 -04:00
Andrew Rabert a69b1385c0
Merge pull request #89 from the-salami/notification-icon
change notification icon to vector audinaut logo
2020-07-09 16:02:52 -04:00
Morgan Lim f506bfb14a Rename Day/Night and Day/Black to "Dynamic". On v28/Android Pie and lower, themes still change based on time of day. On Android 10 and up, theme changes in accordance with system dark mode setting. 2020-07-09 15:54:27 -04:00
Morgan Lim 0f525befca change notification small icon to a white version of the audinaut logo instead of a generic play icon. add additional info to metadata when constructing notification to add basic support for android R's new persistent media controls
(cherry picked from commit ad5615d2bd43b7afe388e08a6a2df07472656b89)
2020-07-09 15:20:35 -04:00
Morgan Lim d3d6186fb7 define button images for forward/back in widget provider so they aren't invisible 2020-07-09 14:35:44 -04:00
Andrew Rabert d10ea92edd Remove unnecessary getUser call 2020-05-22 17:53:44 -04:00
Andrew Rabert d1f1331f35 Fix timeout pref 2020-04-30 13:13:25 -04:00
Andrew Rabert 4fd53e74c5 Release 0.5.1 2020-04-30 13:02:24 -04:00
Andrew Rabert 6704e05da2 Update deps 2020-04-30 12:56:41 -04:00
Andrew Rabert e79aff9e98 Update deps 2020-04-30 12:56:14 -04:00
Andrew Rabert ffa048177e
Merge pull request #66 from emaiannone/refactoring/aDoctor
Refactor Internal Setter smell in RecyclingImageView class
2020-04-30 12:55:58 -04:00
Andrew Rabert f6d308c37c
Merge pull request #78 from simao/bugfix/71
Use settings from preferences for readTimeout
2020-04-30 12:54:53 -04:00
Simão Mata 3c17e91ca8 Use settings from preferences for readTimeout 2020-04-30 16:26:32 +02:00
Andrew Rabert ec083cd79d
Merge pull request #73 from simao/sort-artists-offline-ci
Sort artists using case insensitive comparison when offline
2020-04-13 21:58:00 -04:00
Simão Mata cfae0d30b5 Sort artists using case insensitive comparison when offline
Signed-off-by: Simão Mata <simao.mata@here.com>
2020-04-03 15:33:55 +02:00
Andrew Rabert ecdae8c6e3 Fix switching to playlist on resume
Remove a bunch of unecessary code
2020-03-15 17:06:47 -04:00
Andrew Rabert 76efc2c62c Merge branch 'master' of github:nvllsvm/audinaut 2020-03-15 16:20:55 -04:00
Andrew Rabert 26848852ac
Merge pull request #72 from simao/start-scan-option
Start scan option
2020-03-14 11:36:40 -04:00
Simão Mata 377f21d732 Add option to force media scan on server 2020-03-14 10:41:34 +01:00
Simão Mata 8af307da28 Guard for null theme when getting them res 2020-03-14 10:40:13 +01:00
Andrew Rabert 3d7b173cb1 Update deps 2020-03-13 11:30:59 -04:00
Andrew Rabert e7ea8d4dfc
Merge pull request #67 from dddddd-mmmmmm/issue-25
#25 Add null check to fix crash whilst offline
2020-01-26 18:43:30 -05:00
dddddd-mmmmmm e2280304e4 #25 Add null check to fix crash whilst offline 2020-01-23 16:21:24 +11:00
emaiannone 061c318e05 Refactor Internal Setter smell in RecyclingImageView class 2020-01-19 09:59:20 +01:00
Andrew Rabert 8fc54ac68c Release version 0.5.0 2020-01-15 15:52:48 -05:00
Andrew Rabert 8f4d3be90b Updates 2020-01-15 15:43:28 -05:00
Andrew Rabert a6a784037e
Merge pull request #65 from dddddd-mmmmmm/issue-26
issue-26: Add support for p= authentication method
2020-01-15 15:38:49 -05:00
dddddd-mmmmmm 2ed89ab72f Fix authMethod preference check 2020-01-14 09:14:32 +11:00
Andrew Rabert d5b56eecdd
Merge pull request #64 from dddddd-mmmmmm/issue-48
issue-48: hide keyboard when switching to now playing
2020-01-13 11:01:02 -05:00
dddddd-mmmmmm 81086c4a3a Call hideKeyboard when Panel begins to slide. 2020-01-13 23:36:55 +11:00
dddddd-mmmmmm 02d94e1a6c Add hideKeyboard util method. 2020-01-13 23:36:35 +11:00
dddddd-mmmmmm 5141eb6e81 Use p= authentication parameter when authMethod is false. 2020-01-13 22:08:01 +11:00
dddddd-mmmmmm 4254c8ad9f Add authMethod preference 2020-01-13 22:06:45 +11:00
Andrew Rabert bf9e9ad1e6
Merge pull request #63 from mlim15/mediaStyle-clean
Implement glennguy's MediaStyle notifications
2020-01-11 17:54:39 -05:00
Andrew Rabert 02a8b1909a
Merge pull request #62 from mlim15/master
Fix missing now playing icon on light theme
2020-01-11 17:51:53 -05:00
Morgan Lim ff4a1f3b13 Remove unused import 2020-01-11 04:13:32 -05:00
Morgan Lim 153ccdd46a Manually define duration metadata as -1 to ensure mediastyle notification does not produce unimplemented seek bar on Android 10 2020-01-11 04:12:02 -05:00
Morgan Lim 507c9008aa Colorize MediaStyle notification from album art 2020-01-11 02:33:54 -05:00
Morgan Lim 35a112509e Implement glennguy's MediaStyle notifications daneren2005/Subsonic#914 2020-01-11 02:33:45 -05:00
Andrew Rabert af89b8ea76
Merge pull request #61 from mlim15/vector-adaptive-icon
Change adaptive icon to use vector with shadow
2020-01-11 01:58:17 -05:00
Morgan Lim 0a84e9376a Fix missing now playing icon on light theme 2020-01-10 23:51:57 -05:00
Morgan Lim 717cab5dd5 Change adaptive icon to use vector with shadow 2020-01-10 20:55:02 -05:00
Andrew Rabert 13810a07a5 Revert "Extremely clumsy initial dark mode "support". This does nothing"
This reverts commit cf4ab6bf05.
2020-01-09 17:47:22 -05:00
Andrew Rabert b147cc10dc
Merge pull request #60 from mlim15/bitrate-option
Add 24Kbps (equivalent to Spotify low) and 48Kbps options
2020-01-09 17:45:21 -05:00
mlim15 357cbd2ba1 Add 24Kbps (equivalent to Spotify low) and 48Kbps options for more fine-grained bitrate options 2020-01-09 13:44:19 -05:00
Andrew Rabert 435d770716
Merge pull request #58 from mlim15/location-fix
Add ACCESS_FINE_LOCATION permission to fix LAN access on Android 10
2020-01-09 12:54:28 -05:00
Andrew Rabert 4503ae4133
Merge pull request #56 from mlim15/dark-notifications
Ensure that widget controls change color properly when dark mode is enabled
2020-01-09 12:52:51 -05:00
Andrew Rabert d43cfb1ce3
Merge pull request #59 from mlim15/adaptive-icon
Adaptive icon
2020-01-09 12:52:44 -05:00
mlim15 cc0faa19ce Add ACCESS_FINE_LOCATION permission to fix LAN access on Android 10 2020-01-09 06:33:44 -05:00
mlim15 8078eeff74 Remove smudge from mdpi adaptiveicon 2020-01-09 06:30:21 -05:00
mlim15 d176ed4036 Add initial support for android adaptive icons. TODO: Add backdrop shadow layer with vectors to adaptive icon 2020-01-09 06:00:43 -05:00
mlim15 cf4ab6bf05 Extremely clumsy initial dark mode "support". This does nothing
but ensure that widget controls change color properly when dark mode
is enabled. TODO: Instead of duplicating images, enable DayNight theme
support and use the dynamic colors from that. This would also mean
the app could change to dark by default when system Dark Mode is enabled.
2020-01-09 05:50:20 -05:00
Andrew Rabert 0239248b23 Release version 0.4.1 2019-12-28 11:06:25 -05:00
Andrew Rabert 8ee8858098 Revert "Stop looping over unplayable songs"
This reverts commit 22c98d9606.
2019-12-28 11:04:37 -05:00
Andrew Rabert 23054ffab0 Release version 0.4.0 2019-12-22 15:29:17 -05:00
Andrew Rabert f623ad2b63 Fix background gradient when changing themes 2019-12-22 15:22:10 -05:00
Andrew Rabert 4eac05e57f Use light gradient with light theme 2019-12-22 14:58:58 -05:00
Andrew Rabert 1c269aac53 Simplifiy themes (2) 2019-12-22 14:26:29 -05:00
Andrew Rabert c5c4185e2f Simplifiy themes 2019-12-22 14:17:26 -05:00
Andrew Rabert f56c976de7 Theme tweaks (4) 2019-12-21 15:35:56 -05:00
Andrew Rabert 572024da52 Theme tweaks (3) 2019-12-21 15:07:18 -05:00
Andrew Rabert 97507a25a1 Remove color action bar 2019-12-21 14:45:58 -05:00
Andrew Rabert e7628a97c7 Fix text not scrolling 2019-12-21 12:46:35 -05:00
Andrew Rabert f8a9d8ad34 Theme tweaks 2019-12-21 12:36:34 -05:00
Andrew Rabert 92c41fd463 Update deps 2019-12-21 11:41:30 -05:00
Andrew Rabert 22c98d9606 Stop looping over unplayable songs 2019-12-13 00:25:51 -05:00
Andrew Rabert bf3233d6b2 Theme tweaks 2019-12-12 23:41:43 -05:00
Andrew Rabert 93658f32d3 Repeat one tweaks 2019-12-12 23:36:03 -05:00
Andrew Rabert c0ee2d908e Clarify support for Subsonic API 2019-12-01 16:35:14 -05:00
Andrew Rabert e4febf7260 Update blurred album cover 2019-11-27 19:41:29 -05:00
Andrew Rabert 70f6e22be7 Restart track when playlist has one song and FF pressed 2019-11-27 18:33:03 -05:00
Andrew Rabert 54d6269a9e Add opus support to offline mode 2019-11-27 18:15:16 -05:00
Andrew Rabert eaef36849a Revert "Add Now Playing to drawer"
This reverts commit 89ede8ff47.
2019-11-27 18:12:51 -05:00
Andrew Rabert e28a70a6e6 Revert "Remove slide up panel"
This reverts commit a6fa354622.
2019-11-27 18:12:42 -05:00
Andrew Rabert 2ca504d7e9 Revert "Remove unused"
This reverts commit a334494186.
2019-11-27 18:12:16 -05:00
Andrew Rabert 5e34f2d8e9 Revert "Switch to Now Playing when selecting media"
This reverts commit 687f64c115.
2019-11-27 18:11:54 -05:00
Andrew Rabert 9e57cf1433 Fix HTTP support 2019-09-01 18:27:06 -04:00
Andrew Rabert 687f64c115 Switch to Now Playing when selecting media
Back stack is currently broken
2019-08-28 00:24:27 -04:00
Andrew Rabert 3ebb48682b Fix settings
Regression introduced in 66db8db769fef79016430d29d754591fa9b82609#diff-42baa789cd56fcc4c53bd06c910ff285
2019-08-25 23:55:32 -04:00
Andrew Rabert a334494186 Remove unused 2019-08-25 22:58:31 -04:00
Andrew Rabert d43cca9436 Theme tweaks 2019-08-25 22:41:33 -04:00
Andrew Rabert a6fa354622 Remove slide up panel 2019-08-25 22:32:21 -04:00
Andrew Rabert 89ede8ff47 Add Now Playing to drawer 2019-08-25 22:02:59 -04:00
Andrew Rabert 3243f320b1 Stop logging exceptions to text files
Log cat is sufficent
2019-08-25 16:00:38 -04:00
Andrew Rabert e12fbf68ad Fix widget PendingIntents on Oreo+
Thanks to KBerstene https://github.com/daneren2005/Subsonic/pull/957
2019-08-24 23:18:21 -04:00
Andrew Rabert b943580fa8 Optimize imports 2019-08-24 23:06:03 -04:00
Andrew Rabert a24faf6fae Code cleanup 2019-08-24 23:03:36 -04:00
Andrew Rabert 5144f82051 Remove raster graphics and probably broken
No clue if the search provider works and I don't care
2019-08-24 22:29:45 -04:00
Andrew Rabert 63fc23d9fb Remove resources for API < 21 2019-08-24 22:21:52 -04:00
Andrew Rabert 11223bd44d Make play control buttons transparent 2019-08-24 22:02:06 -04:00
Andrew Rabert 17091f8f86 Fix missing notification pause button when not persist 2019-08-24 22:01:55 -04:00
Andrew Rabert 3648f64fa4 Use vector images
Thanks to vrih https://github.com/daneren2005/Subsonic/pull/919
2019-08-24 21:48:17 -04:00
Andrew Rabert b0750949f0 Update slidinguppanel 2019-08-24 14:29:54 -04:00
Andrew Rabert 66db8db769 Migrate to AppCompat 2019-08-24 14:28:41 -04:00
Andrew Rabert d62e010894 Migrate to AndroidX 2019-08-24 14:24:07 -04:00
Andrew Rabert 39a7c39b94 Update support lib 2019-08-24 14:19:51 -04:00
Andrew Rabert 963aace2c5 Replace slidinguppanel with maintained fork 2019-08-24 14:18:30 -04:00
Andrew Rabert 2e49bcadb2 Remove unused 2019-08-24 14:15:10 -04:00
Andrew Rabert f3b91bff2d Update deps 2019-08-24 14:14:53 -04:00
Andrew Rabert 9d9ff7b728 Update Gradle 2019-08-24 13:59:45 -04:00
Andrew Rabert ca15682a5e Update now playing layout 2019-03-21 22:49:20 -04:00
Andrew Rabert b213a99e7c Use text color on seekbar 2019-03-18 23:20:03 -04:00
Andrew Rabert cf579043f2 Add padding above seekbar
Eliminates seek handle cutoff
2019-03-18 23:13:15 -04:00
Andrew Rabert c6729e427b Increase cover background blur amount
This is really hacky, but looks incredible. Good enough for now.
2019-03-18 21:17:48 -04:00
Andrew Rabert 514eb00797 Release version 0.3.3 2019-03-17 13:20:36 -04:00
Andrew Rabert 5fc74d7cfc Update CHANGELOG 2019-03-17 13:18:40 -04:00
Andrew Rabert 3dffd84f18 Resize logo canvas 2019-03-17 00:37:26 -04:00
Andrew Rabert 49d5c007d6 Update deps 2019-03-17 00:19:42 -04:00
Andrew Rabert 8e2bdadef5 Update deps 2019-03-17 00:06:51 -04:00
Andrew Rabert 9acf64c42b Disable swipe up/down seek gestures 2019-03-16 23:24:01 -04:00
Andrew Rabert 02ffb3b8f0 Disable tap of album art causing toggle to playlist
Way too annoying when trying to use swipe gestures.
The swipe gestures to change tracks and seek remain. All these should be
configurable.
2019-03-14 19:47:15 -04:00
Andrew Rabert f4fbdc2bb1 Update CHANGELOG 2019-03-07 22:23:46 -05:00
Andrew Rabert 625f7ba97e
Merge pull request #43 from dddddd-mmmmmm/master
#37 Use query parameters instead of body
2019-03-07 22:19:25 -05:00
dddddd-mmmmmm 01d4b270cf Add FOREGROUND_SERVICE permission as required for target SDK version 28 (Pie). 2019-03-01 15:42:05 +11:00
dddddd-mmmmmm 05ce1509ec Fix broken getRESTUrl() calls from other Util classes. 2019-03-01 15:37:52 +11:00
dddddd-mmmmmm 13ffabdb20 nvllsvm/audinaut#37 Use query parameters instead of form bodies. 2019-03-01 15:28:58 +11:00
dddddd-mmmmmm 2015039c3d Add support for adding parameters to the Rest URL. 2019-03-01 10:10:16 +11:00
dddddd-mmmmmm 2301e2e015 Fix typo in folder structure. audinuat -> audinaut 2019-02-27 12:23:10 +11:00
Andrew Rabert ae55b32ac5 Automatic cleanup 2019-01-22 00:08:49 -05:00
Andrew Rabert b86cc33bbf Updafe formatting 2019-01-22 00:06:40 -05:00
Andrew Rabert 94bf3cbb57 Update gradle 2019-01-22 00:04:14 -05:00
Andrew Rabert 20cc63f152 Update versions 2019-01-18 21:38:32 -05:00
Andrew Rabert e5702c1761 Misc refactor 2018-12-03 20:08:27 -05:00
Andrew Rabert 665bbb3788 Update okhttp 2018-12-03 19:46:40 -05:00
Andrew Rabert 7b1f690d5f Bump min sdk to 21 2018-12-03 19:46:04 -05:00
Andrew Rabert cd6a3b0054 Update build tools 2018-12-03 19:45:13 -05:00
Andrew Rabert 3a4fda9b7c Build updates 2018-12-03 19:43:48 -05:00
Andrew Rabert 81a1a44b87 Release version 0.3.2 2018-07-05 10:57:55 -04:00
Andrew Rabert b6f0d7ccc9 Update gradle plugin 2018-07-03 18:51:01 -04:00
Andrew Rabert 9614cf9445 Stop hiding playlist on resume 2018-07-03 18:35:28 -04:00
Andrew Rabert f1fe5a087b Show save and delete playlist functions only in menu 2018-07-03 17:57:05 -04:00
Andrew Rabert c7f1fa8665 Optimize conditional 2018-07-03 17:55:41 -04:00
Andrew Rabert e7953958da Consolidate now playing menu 2018-07-03 17:55:02 -04:00
Andrew Rabert 115f211e9f Update changelog 2018-07-03 17:46:41 -04:00
Andrew Rabert d3a317dc9e Prevent now playing from closing during resume 2018-07-03 17:44:09 -04:00
Andrew Rabert 2054ce5427 Add helper to determine if now playing is open 2018-07-03 17:41:47 -04:00
Andrew Rabert 405fc8cafd Use helper function to close now playing 2018-07-03 17:34:58 -04:00
Andrew Rabert 59c814cf16 Release version 0.3.1 2018-06-03 19:19:06 -04:00
Andrew Rabert 09e01d5641 Consolidate now playing layouts 2018-06-03 19:18:33 -04:00
Andrew Rabert be2c7d7e90 Release version 0.3.0 2018-05-12 11:35:28 -04:00
Andrew Rabert ec15274984 Add blurred album art toggle 2018-05-12 11:27:03 -04:00
Andrew Rabert 092e2b4f6b Update CHANGELOG for next release 2018-05-12 10:40:47 -04:00
Andrew Rabert 49173bc090 Remove unused 2018-04-24 21:17:11 -04:00
Andrew Rabert 7eeca7af1b Remove invalid permission 2018-04-24 21:14:47 -04:00
Andrew Rabert 6990fe825e Remove unused resources 2018-04-24 21:04:48 -04:00
Andrew Rabert 07aa43aed8 Remove redundant 2018-04-24 21:02:24 -04:00
Andrew Rabert 06aadeb776 Remove unneeded weight 2018-04-24 21:01:49 -04:00
Andrew Rabert bc0157c1b1 Replace HashMap with SparseArray 2018-04-24 20:54:27 -04:00
Andrew Rabert b75db5bb86 Fix deprecated MenuItemCompat usage 2018-04-24 20:44:36 -04:00
Andrew Rabert f34753dfc1 Fix deprecated getWidth usage 2018-04-24 20:43:14 -04:00
Andrew Rabert 904186baef Change var scope to final 2018-04-24 20:38:38 -04:00
Andrew Rabert d84327db02 Remove unreachable 2018-04-24 20:35:30 -04:00
Andrew Rabert d8ee4d012c Remove unused 2018-04-24 20:24:15 -04:00
Andrew Rabert 0c67244749 Simplify conditionals 2018-04-24 20:20:20 -04:00
Andrew Rabert ff8862c723 Use lambda 2018-04-24 20:10:11 -04:00
Andrew Rabert 99c03d59b3 Remove unreachable statements 2018-04-24 20:08:00 -04:00
Andrew Rabert 9f3ecd5e8e Remove redundant var 2018-04-24 19:59:34 -04:00
Andrew Rabert 82a8d0fff8 Replace conditional with compare method 2018-04-24 19:58:58 -04:00
Andrew Rabert bb9e3a506e Replace if with switch 2018-04-24 19:58:40 -04:00
Andrew Rabert 4fed3bd3a3 Fix redundant addAll 2018-04-24 00:35:58 -04:00
Andrew Rabert b7b90ea8d5 Remove unused imports 2018-04-24 00:34:59 -04:00
Andrew Rabert 561eb4add9 Eliminate redundant method 2018-04-24 00:24:46 -04:00
Andrew Rabert 7e151ce5ae Eliminate redundant method 2018-04-24 00:18:01 -04:00
Andrew Rabert 4a167d3548 Consolidate playNow usage 2018-04-24 00:12:29 -04:00
Andrew Rabert 345b717f86 Fix deprecated getActionView(MenuItem) 2018-04-24 00:10:09 -04:00
Andrew Rabert 72424659ab Remove unnecessary method 2018-04-24 00:07:09 -04:00
Andrew Rabert 7d1e55321a Fix deprecated method usage 2018-04-24 00:04:41 -04:00
Andrew Rabert a4396eb375 Remove unused method 2018-04-23 23:47:50 -04:00
Andrew Rabert 8222a3f449 Consolidate onBackPressed logic 2018-04-23 23:40:06 -04:00
Andrew Rabert 0a1cc75cc4 Remove setContentView override 2018-04-23 23:31:55 -04:00
Andrew Rabert 81b562a2cb Stop closing now playing on ambiguous intent 2018-04-23 23:03:32 -04:00
Andrew Rabert 7bb5b0f91d Move onCreatePost to onCreate and eliminate functions 2018-04-23 22:58:54 -04:00
Andrew Rabert 8f0a27bfec Remove redundant return 2018-04-23 22:49:55 -04:00
Andrew Rabert 727219a1ca Cleanup empty override style 2018-04-23 22:34:35 -04:00
Andrew Rabert 7cc4495d19 Simplify redundant conditional 2018-04-23 22:23:38 -04:00
Andrew Rabert 7b0edf77d6 Cleanup loadSession 2018-04-23 22:22:27 -04:00
Andrew Rabert a6571d2c4c Remove unchanging preference 2018-04-23 22:18:51 -04:00
Andrew Rabert e4f0ea25a3 Upgrade Gradle plugin 2018-04-23 20:54:54 -04:00
Andrew Rabert dbe49846d9 Downgrade to Gradle 4.5
"Configuration on demand is not supported"
2018-04-23 20:53:46 -04:00
Andrew Rabert b1d0e91b8a Fill empty space with blurred cover art 2018-03-28 01:35:49 -04:00
Andrew Rabert be1dc7d8c9 Center cover art on now playing screen 2018-03-27 23:58:19 -04:00
Andrew Rabert c7a49ee88e IDE-fueled mega refactor 2018-03-26 00:45:41 -04:00
Andrew Rabert 1396ff5e48 Gradle - Java 1.8 2018-03-24 20:44:51 -04:00
Andrew Rabert 7e56cba6e5 Remove extra whitespace from artist line 2018-03-24 17:38:51 -04:00
682 changed files with 7726 additions and 11208 deletions

6
.gitignore vendored
View File

@ -37,11 +37,7 @@ captures/
# Intellij
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/dictionaries
.idea/libraries
.idea
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
</project>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Audinaut.iml" filepath="$PROJECT_DIR$/Audinaut.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>
</project>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,6 +1,64 @@
Changelog
=========
## Version 0.5.1
_2020-04-30_
* Add option to force server-side media scan
* Change to local artist sorting (case-insensitive)
* Fix crash while offline (#25)
* Fix read timeout not being respected
* Fix switching to playlist on app resume
## Version 0.5.0
_2020-01-15_
* Add 24kbps and 48kbps options
* Add adaptive icon
* Add support for p= authentication
* Change to MediaStyle playback notification
* Fix SSID selection
* Fix keyboard being visible when switching to now playing
* Fix now playing icon when using light theme
## Version 0.4.1
_2019-12-28_
* Revert attempt to fix infinite loop as it sometimes deleted valid files.
## Version 0.4.0
_2019-12-22_
* Add support for .opus files
* Fix HTTP support
* Fix infinite loop when playing contains only invalid files
* Overhaul themes
* Replace raster images with vector images
## Version 0.3.3
_2019-03-17_
* Fix [Funkwhale](https://funkwhale.audio/) (Subsonic API) support
* Use query parameters instead of body
* Disable most "now playing" swipe gestures - too sensitive and often
accidentally triggered.
Only swipe left/right to change track ramains.
## Version 0.3.2
_2018-07-05_
* Prevent now playing from closing upon resuming
* Only show save and delete playlist functions in menu
* Stop hiding playlist on resume
## Version 0.3.1
_2018-06-03_
* Fix crash on now playing & playlist while in landscape
## Version 0.3.0
_2018-05-12_
* Center cover art on now playing screen
* Use blurred cover art to fill empty space on now playing screen
* Fix extra whitespace on now playing while offline
## Version 0.2.5
_2018-03-24_
* Fix pausing playback on disconnect on API 26+

View File

@ -1,7 +1,14 @@
# Audinaut
⚠ No longer maintained.
Although not a direct replacement, I've since moved onto syncing an Opus version
of my entire library to my phone using [harmonize](https://github.com/nvllsvm/harmonize)
and [Syncthing](https://syncthing.net/).
---
<img src="audinaut.png" align="left" width="200" hspace="10" vspace="10">
An [Airsonic] client for Android.
A Subsonic client for Android.
<a href="https://f-droid.org/app/net.nullsum.audinaut">
@ -12,5 +19,3 @@ An [Airsonic] client for Android.
<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Get it on Google Play" height="80" />
</a>
[Airsonic]: https://airsonic.github.io

View File

@ -2,21 +2,16 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 27
buildToolsVersion "27.0.3"
compileSdkVersion 31
buildToolsVersion "31.0.0"
defaultConfig {
applicationId "net.nullsum.audinaut"
minSdkVersion 19
targetSdkVersion 27
versionCode 194
versionName '0.2.5'
minSdkVersion 21
targetSdkVersion 30
versionCode 202
versionName '0.5.1'
setProperty("archivesBaseName", "Audinaut $versionName")
resConfigs "de", "es", "fr", "hu", "nl", "pt-rPT", "ru", "sv"
}
packagingOptions {
exclude 'META-INF/beans.xml'
}
lintOptions {
@ -31,15 +26,16 @@ android {
dependencies {
implementation 'com.esotericsoftware:kryo:4.0.2'
implementation "com.android.support:design:$android_support_version"
implementation 'com.sothree.slidinguppanel:library:3.3.1'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.google.android.material:material:1.5.0'
implementation 'com.github.hannesa2:AndroidSlidingUpPanel:4.4.1'
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "androidx.media:media:1.5.0"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
}
buildscript {
ext.kotlin_version = '1.2.31'
ext.android_support_version = '27.1.0'
ext.kotlin_version = '1.6.0'
repositories {
mavenCentral()
}

View File

@ -1,100 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.nullsum.audinaut"
android:installLocation="internalOnly">
package="net.nullsum.audinaut"
android:installLocation="internalOnly">
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="net.nullsum.audinaut"
android:label="Tests" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission
android:name="android.permission.READ_PHONE_STATE"
android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" android:maxSdkVersion="22"/>
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-feature
android:name="android.hardware.bluetooth"
android:required="false" />
<uses-feature
android:name="android.hardware.microphone"
android:required="false" />
<uses-feature
android:name="android.hardware.wifi"
android:required="false" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
<uses-feature android:name="android.hardware.microphone" android:required="false" />
<uses-feature android:name="android.hardware.wifi" android:required="false" />
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<supports-screens android:anyDensity="true" android:xlargeScreens="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true"/>
<application android:label="@string/common.appname"
<application
android:backupAgent="net.nullsum.audinaut.util.SettingsBackupAgent"
android:icon="@drawable/launch"
android:label="@string/common.appname"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/LaunchScreen">
<uses-library android:name="android.test.runner" />
<activity android:name="net.nullsum.audinaut.activity.SubsonicFragmentActivity"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTask">
<activity
android:name="net.nullsum.audinaut.activity.SubsonicFragmentActivity"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="net.nullsum.audinaut.activity.SettingsActivity"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTask"/>
<activity
android:name="net.nullsum.audinaut.activity.SettingsActivity"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTask" />
<activity android:name="net.nullsum.audinaut.activity.VoiceQueryReceiverActivity"
android:launchMode="singleTask">
<activity
android:name="net.nullsum.audinaut.activity.VoiceQueryReceiverActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name="net.nullsum.audinaut.activity.QueryReceiverActivity"
android:launchMode="singleTask">
<activity
android:name="net.nullsum.audinaut.activity.QueryReceiverActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.SEARCH"/>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
<activity
android:name="net.nullsum.audinaut.activity.EditPlayActionActivity"
android:label="@string/tasker.start_playing"
android:icon="@drawable/launch">
android:icon="@drawable/launch"
android:label="@string/tasker.start_playing">
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING" />
</intent-filter>
</activity>
<service android:name=".service.DownloadService"
android:label="Audinaut Playback Service"/>
<service
android:name=".service.DownloadService"
android:label="Audinaut Playback Service" />
<service android:name="net.nullsum.audinaut.service.sync.AuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<service android:name="net.nullsum.audinaut.service.HeadphoneListenerService"
android:label="Audinaut Headphone Listener"/>
<receiver
android:name="net.nullsum.audinaut.receiver.BootReceiver">
<service
android:name="net.nullsum.audinaut.service.HeadphoneListenerService"
android:label="Audinaut Headphone Listener" />
<receiver android:name="net.nullsum.audinaut.receiver.BootReceiver">
<intent-filter>
<action
android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
@ -106,7 +129,7 @@
<receiver android:name="net.nullsum.audinaut.receiver.A2dpIntentReceiver">
<intent-filter>
<action android:name="android.music.playstatusrequest"/>
<action android:name="android.music.playstatusrequest" />
</intent-filter>
</receiver>
@ -114,48 +137,59 @@
android:name="net.nullsum.audinaut.provider.AudinautWidget4x1"
android:label="@string/widget.4x1">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x1"/>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget4x1" />
</receiver>
<receiver
android:name="net.nullsum.audinaut.provider.AudinautWidget4x2"
android:label="@string/widget.4x2">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x2"/>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget4x2" />
</receiver>
<receiver
android:name="net.nullsum.audinaut.provider.AudinautWidget4x3"
android:label="@string/widget.4x3">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x3"/>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget4x3" />
</receiver>
<receiver
android:name="net.nullsum.audinaut.provider.AudinautWidget4x4"
android:label="@string/widget.4x4">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget4x4"/>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget4x4" />
</receiver>
<receiver
android:name="net.nullsum.audinaut.receiver.PlayActionReceiver">
android:name="net.nullsum.audinaut.receiver.PlayActionReceiver"
android:permission="">
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
</intent-filter>
</receiver>
<provider android:name="net.nullsum.audinaut.provider.AudinautSearchProvider"
android:authorities="net.nullsum.audinaut.provider.AudinautSearchProvider"/>
<provider
android:name="net.nullsum.audinaut.provider.AudinautSearchProvider"
android:authorities="net.nullsum.audinaut.provider.AudinautSearchProvider" />
<meta-data android:name="android.app.default_searchable"
android:value="net.nullsum.audinaut.activity.QueryReceiverActivity"/>
<meta-data
android:name="android.app.default_searchable"
android:value="net.nullsum.audinaut.activity.QueryReceiverActivity" />
</application>
</manifest>

View File

@ -15,13 +15,8 @@
package net.nullsum.audinaut.activity;
import android.app.Activity;
import android.support.v7.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.widget.DrawerLayout;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@ -29,12 +24,12 @@ import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import java.util.ArrayList;
import java.util.List;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.drawerlayout.widget.DrawerLayout;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.Genre;
@ -45,6 +40,9 @@ import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.LoadingTask;
import net.nullsum.audinaut.util.Util;
import java.util.ArrayList;
import java.util.List;
public class EditPlayActionActivity extends SubsonicActivity {
private CheckBox shuffleCheckbox;
private CheckBox startYearCheckbox;
@ -61,39 +59,26 @@ public class EditPlayActionActivity extends SubsonicActivity {
super.onCreate(savedInstanceState);
setTitle(R.string.tasker_start_playing_title);
setContentView(R.layout.edit_play_action);
final Activity context = this;
final AppCompatActivity context = this;
doNothing = context.getResources().getString(R.string.tasker_edit_do_nothing);
shuffleCheckbox = (CheckBox) findViewById(R.id.edit_shuffle_checkbox);
shuffleCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton view, boolean isChecked) {
startYearCheckbox.setEnabled(isChecked);
endYearCheckbox.setEnabled(isChecked);
genreButton.setEnabled(isChecked);
}
shuffleCheckbox = findViewById(R.id.edit_shuffle_checkbox);
shuffleCheckbox.setOnCheckedChangeListener((view, isChecked) -> {
startYearCheckbox.setEnabled(isChecked);
endYearCheckbox.setEnabled(isChecked);
genreButton.setEnabled(isChecked);
});
startYearCheckbox = (CheckBox) findViewById(R.id.edit_start_year_checkbox);
startYearBox = (EditText) findViewById(R.id.edit_start_year);
startYearCheckbox = findViewById(R.id.edit_start_year_checkbox);
startYearBox = findViewById(R.id.edit_start_year);
// Disable/enable number box if checked
startYearCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton view, boolean isChecked) {
startYearBox.setEnabled(isChecked);
}
});
startYearCheckbox.setOnCheckedChangeListener((view, isChecked) -> startYearBox.setEnabled(isChecked));
endYearCheckbox = (CheckBox) findViewById(R.id.edit_end_year_checkbox);
endYearBox = (EditText) findViewById(R.id.edit_end_year);
endYearCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton view, boolean isChecked) {
endYearBox.setEnabled(isChecked);
}
});
endYearCheckbox = findViewById(R.id.edit_end_year_checkbox);
endYearBox = findViewById(R.id.edit_end_year);
endYearCheckbox.setOnCheckedChangeListener((view, isChecked) -> endYearBox.setEnabled(isChecked));
genreButton = (Button) findViewById(R.id.edit_genre_spinner);
genreButton = findViewById(R.id.edit_genre_spinner);
genreButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new LoadingTask<List<Genre>>(context, true) {
@ -105,24 +90,22 @@ public class EditPlayActionActivity extends SubsonicActivity {
@Override
protected void done(final List<Genre> genres) {
List<String> names = new ArrayList<String>();
List<String> names = new ArrayList<>();
String blank = context.getResources().getString(R.string.select_genre_blank);
names.add(doNothing);
names.add(blank);
for(Genre genre: genres) {
for (Genre genre : genres) {
names.add(genre.getName());
}
final List<String> finalNames = names;
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.shuffle_pick_genre)
.setItems(names.toArray(new CharSequence[names.size()]), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if(which == 1) {
genreButton.setText("");
} else {
genreButton.setText(finalNames.get(which));
}
.setItems(names.toArray(new CharSequence[names.size()]), (dialog, which) -> {
if (which == 1) {
genreButton.setText("");
} else {
genreButton.setText(finalNames.get(which));
}
});
AlertDialog dialog = builder.create();
@ -145,36 +128,36 @@ public class EditPlayActionActivity extends SubsonicActivity {
});
genreButton.setText(doNothing);
offlineSpinner = (Spinner) findViewById(R.id.edit_offline_spinner);
offlineSpinner = findViewById(R.id.edit_offline_spinner);
ArrayAdapter<CharSequence> offlineAdapter = ArrayAdapter.createFromResource(this, R.array.editServerOptions, android.R.layout.simple_spinner_item);
offlineAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
offlineSpinner.setAdapter(offlineAdapter);
// Setup default for everything
Bundle extras = getIntent().getBundleExtra(Constants.TASKER_EXTRA_BUNDLE);
if(extras != null) {
if(extras.getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE)) {
if (extras != null) {
if (extras.getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE)) {
shuffleCheckbox.setChecked(true);
}
String startYear = extras.getString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, null);
if(startYear != null) {
if (startYear != null) {
startYearCheckbox.setEnabled(true);
startYearBox.setText(startYear);
}
String endYear = extras.getString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, null);
if(endYear != null) {
if (endYear != null) {
endYearCheckbox.setEnabled(true);
endYearBox.setText(endYear);
}
String genre = extras.getString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, doNothing);
if(genre != null) {
if (genre != null) {
genreButton.setText(genre);
}
int offline = extras.getInt(Constants.PREFERENCES_KEY_OFFLINE, 0);
if(offline != 0) {
if (offline != 0) {
offlineSpinner.setSelection(offline);
}
}
@ -191,15 +174,16 @@ public class EditPlayActionActivity extends SubsonicActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == android.R.id.home) {
cancel();
return true;
} else if(item.getItemId() == R.id.menu_accept) {
accept();
return true;
} else if(item.getItemId() == R.id.menu_cancel) {
cancel();
return true;
switch (item.getItemId()) {
case android.R.id.home:
cancel();
return true;
case R.id.menu_accept:
accept();
return true;
case R.id.menu_cancel:
cancel();
return true;
}
return false;
@ -215,31 +199,32 @@ public class EditPlayActionActivity extends SubsonicActivity {
Bundle data = new Bundle();
boolean shuffle = shuffleCheckbox.isChecked();
data.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, shuffle);
if(shuffle) {
if(startYearCheckbox.isChecked()) {
if (shuffle) {
if (startYearCheckbox.isChecked()) {
data.putString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, startYearBox.getText().toString());
}
if(endYearCheckbox.isChecked()) {
if (endYearCheckbox.isChecked()) {
data.putString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, endYearBox.getText().toString());
}
String genre = genreButton.getText().toString();
if(!genre.equals(doNothing)) {
if (!genre.equals(doNothing)) {
data.putString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, genre);
}
}
int offline = offlineSpinner.getSelectedItemPosition();
if(offline != 0) {
if (offline != 0) {
data.putInt(Constants.PREFERENCES_KEY_OFFLINE, offline);
}
intent.putExtra(Constants.TASKER_EXTRA_BUNDLE, data);
setResult(Activity.RESULT_OK, intent);
setResult(AppCompatActivity.RESULT_OK, intent);
finish();
}
private void cancel() {
setResult(Activity.RESULT_CANCELED);
setResult(AppCompatActivity.RESULT_CANCELED);
finish();
}
}

View File

@ -19,26 +19,21 @@
package net.nullsum.audinaut.activity;
import android.app.Activity;
import android.app.SearchManager;
import android.content.Intent;
import android.os.Bundle;
import android.provider.SearchRecentSuggestions;
import android.util.Log;
import net.nullsum.audinaut.fragments.SubsonicFragment;
import androidx.appcompat.app.AppCompatActivity;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.provider.AudinautSearchProvider;
/**
* Receives search queries and forwards to the SearchFragment.
*
* @author Sindre Mehus
*/
public class QueryReceiverActivity extends Activity {
private static final String TAG = QueryReceiverActivity.class.getSimpleName();
public class QueryReceiverActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
@ -47,11 +42,10 @@ public class QueryReceiverActivity extends Activity {
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
doSearch();
} else if(Intent.ACTION_VIEW.equals(intent.getAction())) {
} else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
showResult(intent.getDataString(), intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
}
finish();
Util.disablePendingTransition(this);
}
private void doSearch() {
@ -63,17 +57,18 @@ public class QueryReceiverActivity extends Activity {
Util.startActivityWithoutTransition(QueryReceiverActivity.this, intent);
}
}
private void showResult(String albumId, String name) {
if (albumId != null) {
Intent intent = new Intent(this, SubsonicFragmentActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(Constants.INTENT_EXTRA_VIEW_ALBUM, true);
if(albumId.indexOf("ar-") == 0) {
if (albumId.indexOf("ar-") == 0) {
intent.putExtra(Constants.INTENT_EXTRA_NAME_ARTIST, true);
albumId = albumId.replace("ar-", "");
} else if(albumId.indexOf("so-") == 0) {
albumId = albumId.replaceFirst("ar-", "");
} else if (albumId.indexOf("so-") == 0) {
intent.putExtra(Constants.INTENT_EXTRA_SEARCH_SONG, name);
albumId = albumId.replace("so-", "");
albumId = albumId.replaceFirst("so-", "");
}
intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, albumId);
if (name != null) {

View File

@ -18,9 +18,9 @@
*/
package net.nullsum.audinaut.activity;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import androidx.appcompat.widget.Toolbar;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.fragments.PreferenceCompatFragment;
@ -28,8 +28,6 @@ import net.nullsum.audinaut.fragments.SettingsFragment;
import net.nullsum.audinaut.util.Constants;
public class SettingsActivity extends SubsonicActivity {
private static final String TAG = SettingsActivity.class.getSimpleName();
private PreferenceCompatFragment fragment;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -38,7 +36,7 @@ public class SettingsActivity extends SubsonicActivity {
setContentView(R.layout.settings_activity);
if (savedInstanceState == null) {
fragment = new SettingsFragment();
PreferenceCompatFragment fragment = new SettingsFragment();
Bundle args = new Bundle();
args.putInt(Constants.INTENT_EXTRA_FRAGMENT_TYPE, R.xml.settings);
@ -50,7 +48,7 @@ public class SettingsActivity extends SubsonicActivity {
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "").commit();
}
Toolbar mainToolbar = (Toolbar) findViewById(R.id.main_toolbar);
Toolbar mainToolbar = findViewById(R.id.main_toolbar);
setSupportActionBar(mainToolbar);
}
}

View File

@ -20,42 +20,29 @@ package net.nullsum.audinaut.activity;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Dialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState;
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import java.io.File;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.sothree.slidinguppanel.PanelSlideListener;
import com.sothree.slidinguppanel.PanelState;
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.PlayerQueue;
import net.nullsum.audinaut.domain.PlayerState;
import net.nullsum.audinaut.fragments.DownloadFragment;
import net.nullsum.audinaut.fragments.NowPlayingFragment;
@ -66,27 +53,25 @@ import net.nullsum.audinaut.fragments.SelectPlaylistFragment;
import net.nullsum.audinaut.fragments.SubsonicFragment;
import net.nullsum.audinaut.service.DownloadFile;
import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.service.MusicService;
import net.nullsum.audinaut.service.MusicServiceFactory;
import net.nullsum.audinaut.updates.Updater;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.FileUtil;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.UserUtil;
import net.nullsum.audinaut.util.Util;
import java.io.File;
import java.util.List;
/**
* Created by Scott on 10/14/13.
*/
public class SubsonicFragmentActivity extends SubsonicActivity implements DownloadService.OnSongChangedListener {
private static String TAG = SubsonicFragmentActivity.class.getSimpleName();
private static boolean infoDialogDisplayed;
private static boolean sessionInitialized = false;
private static long ALLOWED_SKEW = 30000L;
private SlidingUpPanelLayout slideUpPanel;
private SlidingUpPanelLayout.PanelSlideListener panelSlideListener;
private PanelSlideListener panelSlideListener;
private boolean isPanelClosing = false;
private boolean resuming = false;
private NowPlayingFragment nowPlayingFragment;
private SubsonicFragment secondaryFragment;
private Toolbar mainToolbar;
@ -97,9 +82,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
private TextView trackView;
private TextView artistView;
private ImageButton startButton;
private long lastBackPressTime = 0;
private DownloadFile currentPlaying;
private PlayerState currentState;
private ImageButton previousButton;
private ImageButton nextButton;
private ImageButton rewindButton;
@ -107,17 +90,15 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
@Override
public void onCreate(Bundle savedInstanceState) {
if(savedInstanceState == null) {
if (savedInstanceState == null) {
String fragmentType = getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE);
boolean firstRun = false;
if (fragmentType == null) {
fragmentType = Util.openToTab(this);
if (fragmentType != null) {
firstRun = true;
}
fragmentType = "Library";
firstRun = true;
}
if ("".equals(fragmentType) || fragmentType == null || firstRun) {
if ("".equals(fragmentType) || firstRun) {
// Initial startup stuff
if (!sessionInitialized) {
loadSession();
@ -130,25 +111,24 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
stopService(new Intent(this, DownloadService.class));
finish();
getImageLoader().clearCache();
} else if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) {
} else if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) {
getIntent().putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, "Download");
lastSelectedPosition = R.id.drawer_downloading;
}
setContentView(R.layout.abstract_fragment_activity);
if (drawerToggle != null) {
drawerToggle.setDrawerIndicatorEnabled(true);
}
if (findViewById(R.id.fragment_container) != null && savedInstanceState == null) {
String fragmentType = getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE);
if(fragmentType == null) {
fragmentType = Util.openToTab(this);
if(fragmentType != null) {
getIntent().putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, fragmentType);
lastSelectedPosition = getDrawerItemId(fragmentType);
} else {
lastSelectedPosition = R.id.drawer_library;
}
if (fragmentType == null) {
fragmentType = "Library";
getIntent().putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, fragmentType);
lastSelectedPosition = getDrawerItemId(fragmentType);
MenuItem item = drawerList.getMenu().findItem(lastSelectedPosition);
if(item != null) {
if (item != null) {
item.setChecked(true);
}
} else {
@ -156,9 +136,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
}
currentFragment = getNewFragment(fragmentType);
if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_ID)) {
if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_ID)) {
Bundle currentArguments = currentFragment.getArguments();
if(currentArguments == null) {
if (currentArguments == null) {
currentArguments = new Bundle();
}
currentArguments.putString(Constants.INTENT_EXTRA_NAME_ID, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ID));
@ -167,14 +147,14 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
currentFragment.setPrimaryFragment(true);
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "").commit();
if(getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY) != null) {
if (getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY) != null) {
SearchFragment fragment = new SearchFragment();
replaceFragment(fragment, fragment.getSupportTag());
}
// If a album type is set, switch to that album type view
String albumType = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
if(albumType != null) {
if (albumType != null) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
@ -187,18 +167,18 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
}
}
slideUpPanel = (SlidingUpPanelLayout) findViewById(R.id.slide_up_panel);
panelSlideListener = new SlidingUpPanelLayout.PanelSlideListener() {
slideUpPanel = findViewById(R.id.slide_up_panel);
panelSlideListener = new PanelSlideListener() {
@Override
public void onPanelSlide(View panel, float slideOffset) {
Util.hideKeyboard(panel);
}
@Override
public void onPanelStateChanged(View panel, PanelState previousState, PanelState newState) {
if (newState == PanelState.COLLAPSED) {
isPanelClosing = false;
if(bottomBar.getVisibility() == View.GONE) {
if (bottomBar.getVisibility() == View.GONE) {
bottomBar.setVisibility(View.VISIBLE);
nowPlayingToolbar.setVisibility(View.GONE);
nowPlayingFragment.setPrimaryFragment(false);
@ -217,7 +197,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
nowPlayingToolbar.setVisibility(View.VISIBLE);
setSupportActionBar(nowPlayingToolbar);
if(secondaryFragment == null) {
if (secondaryFragment == null) {
nowPlayingFragment.setPrimaryFragment(true);
} else {
secondaryFragment.setPrimaryFragment(true);
@ -230,24 +210,19 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
};
slideUpPanel.addPanelSlideListener(panelSlideListener);
if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD)) {
if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD)) {
// Post this later so it actually runs
handler.postDelayed(new Runnable() {
@Override
public void run() {
openNowPlaying();
}
}, 200);
handler.postDelayed(this::openNowPlaying, 200);
getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD);
}
bottomBar = findViewById(R.id.bottom_bar);
mainToolbar = (Toolbar) findViewById(R.id.main_toolbar);
nowPlayingToolbar = (Toolbar) findViewById(R.id.now_playing_toolbar);
coverArtView = (ImageView) bottomBar.findViewById(R.id.album_art);
trackView = (TextView) bottomBar.findViewById(R.id.track_name);
artistView = (TextView) bottomBar.findViewById(R.id.artist_name);
mainToolbar = findViewById(R.id.main_toolbar);
nowPlayingToolbar = findViewById(R.id.now_playing_toolbar);
coverArtView = bottomBar.findViewById(R.id.album_art);
trackView = bottomBar.findViewById(R.id.track_name);
artistView = bottomBar.findViewById(R.id.artist_name);
setSupportActionBar(mainToolbar);
@ -258,122 +233,97 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
trans.commit();
}
rewindButton = (ImageButton) findViewById(R.id.download_rewind);
rewindButton.setOnClickListener(new View.OnClickListener() {
rewindButton = findViewById(R.id.download_rewind);
rewindButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
public void onClick(View v) {
new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
protected Void doInBackground() throws Throwable {
if (getDownloadService() == null) {
return null;
}
getDownloadService().rewind();
return null;
}
}.execute();
protected Void doInBackground() {
if (getDownloadService() != null) {
getDownloadService().rewind();
}
return null;
}
});
}.execute());
previousButton = (ImageButton) findViewById(R.id.download_previous);
previousButton.setOnClickListener(new View.OnClickListener() {
previousButton = findViewById(R.id.download_previous);
previousButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
public void onClick(View v) {
new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
protected Void doInBackground() throws Throwable {
if(getDownloadService() == null) {
return null;
}
getDownloadService().previous();
return null;
}
}.execute();
protected Void doInBackground() {
if (getDownloadService() != null) {
getDownloadService().previous();
}
return null;
}
});
}.execute());
startButton = (ImageButton) findViewById(R.id.download_start);
startButton.setOnClickListener(new View.OnClickListener() {
startButton = findViewById(R.id.download_start);
startButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
public void onClick(View v) {
new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
protected Void doInBackground() throws Throwable {
PlayerState state = getDownloadService().getPlayerState();
if(state == PlayerState.STARTED) {
getDownloadService().pause();
} else {
getDownloadService().start();
}
return null;
}
}.execute();
protected Void doInBackground() {
PlayerState state = getDownloadService().getPlayerState();
if (state == PlayerState.STARTED) {
getDownloadService().pause();
} else {
getDownloadService().start();
}
return null;
}
});
}.execute());
nextButton = (ImageButton) findViewById(R.id.download_next);
nextButton.setOnClickListener(new View.OnClickListener() {
nextButton = findViewById(R.id.download_next);
nextButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
public void onClick(View v) {
new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
protected Void doInBackground() throws Throwable {
if(getDownloadService() == null) {
return null;
}
getDownloadService().next();
return null;
}
}.execute();
protected Void doInBackground() {
if (getDownloadService() != null) {
getDownloadService().next();
}
return null;
}
});
}.execute());
fastforwardButton = (ImageButton) findViewById(R.id.download_fastforward);
fastforwardButton.setOnClickListener(new View.OnClickListener() {
fastforwardButton = findViewById(R.id.download_fastforward);
fastforwardButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
public void onClick(View v) {
new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
protected Void doInBackground() throws Throwable {
if (getDownloadService() == null) {
return null;
}
protected Void doInBackground() {
if (getDownloadService() == null) {
return null;
}
getDownloadService().fastForward();
return null;
}
}.execute();
getDownloadService().fastForward();
return null;
}
});
}
}.execute());
@Override
protected void onPostCreate(Bundle bundle) {
super.onPostCreate(bundle);
if (!infoDialogDisplayed) {
infoDialogDisplayed = true;
if (Util.getRestUrl(this).contains("demo.subsonic.org")) {
Util.info(this, R.string.main_welcome_title, R.string.main_welcome_text);
}
}
showInfoDialog();
checkUpdates();
try {
String version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
int ver = Integer.parseInt(version.replace(".", ""));
Updater updater = new Updater(ver);
updater.checkUpdates(this);
} catch (Exception ignored) {
}
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if(currentFragment != null && intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY) != null) {
if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
if (currentFragment != null && intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY) != null) {
if (isNowPlayingOpen()) {
closeNowPlaying();
}
if(currentFragment instanceof SearchFragment) {
if (currentFragment instanceof SearchFragment) {
String query = intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY);
boolean autoplay = intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false);
if (query != null) {
((SearchFragment)currentFragment).search(query, autoplay);
((SearchFragment) currentFragment).search(query, autoplay);
}
getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_QUERY);
} else {
@ -382,35 +332,33 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
SearchFragment fragment = new SearchFragment();
replaceFragment(fragment, fragment.getSupportTag());
}
} else if(intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, false)) {
if(slideUpPanel.getPanelState() != SlidingUpPanelLayout.PanelState.EXPANDED) {
} else if (intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, false)) {
if (!isNowPlayingOpen()) {
openNowPlaying();
}
} else {
if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
closeNowPlaying();
}
setIntent(intent);
}
if(drawer != null) {
if (drawer != null) {
drawer.closeDrawers();
}
}
@Override
public void onResume() {
resuming = true;
super.onResume();
if(getIntent().hasExtra(Constants.INTENT_EXTRA_VIEW_ALBUM)) {
if (getIntent().hasExtra(Constants.INTENT_EXTRA_VIEW_ALBUM)) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ID, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ID));
args.putString(Constants.INTENT_EXTRA_NAME_NAME, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_NAME));
args.putString(Constants.INTENT_EXTRA_SEARCH_SONG, getIntent().getStringExtra(Constants.INTENT_EXTRA_SEARCH_SONG));
if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_ARTIST)) {
if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_ARTIST)) {
args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
}
if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID)) {
if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID)) {
args.putString(Constants.INTENT_EXTRA_NAME_CHILD_ID, getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID));
}
fragment.setArguments(args);
@ -419,21 +367,16 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
getIntent().removeExtra(Constants.INTENT_EXTRA_VIEW_ALBUM);
}
UserUtil.seedCurrentUser(this);
createAccount();
runWhenServiceAvailable(new Runnable() {
@Override
public void run() {
getDownloadService().addOnSongChangedListener(SubsonicFragmentActivity.this, true);
}
});
runWhenServiceAvailable(() -> getDownloadService().addOnSongChangedListener(SubsonicFragmentActivity.this));
resuming = false;
}
@Override
public void onPause() {
super.onPause();
DownloadService downloadService = getDownloadService();
if(downloadService != null) {
if (downloadService != null) {
downloadService.removeOnSongChangeListener(this);
}
}
@ -442,11 +385,12 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString(Constants.MAIN_NOW_PLAYING, nowPlayingFragment.getTag());
if(secondaryFragment != null) {
if (secondaryFragment != null) {
savedInstanceState.putString(Constants.MAIN_NOW_PLAYING_SECONDARY, secondaryFragment.getTag());
}
savedInstanceState.putInt(Constants.MAIN_SLIDE_PANEL_STATE, slideUpPanel.getPanelState().hashCode());
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
@ -456,7 +400,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
nowPlayingFragment = (NowPlayingFragment) fm.findFragmentByTag(id);
String secondaryId = savedInstanceState.getString(Constants.MAIN_NOW_PLAYING_SECONDARY);
if(secondaryId != null) {
if (secondaryId != null) {
secondaryFragment = (SubsonicFragment) fm.findFragmentByTag(secondaryId);
nowPlayingFragment.setPrimaryFragment(false);
@ -467,51 +411,32 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
trans.commit();
}
if(drawerToggle != null && backStack.size() > 0) {
if (drawerToggle != null && backStack.size() > 0) {
drawerToggle.setDrawerIndicatorEnabled(false);
}
if(savedInstanceState.getInt(Constants.MAIN_SLIDE_PANEL_STATE, -1) == SlidingUpPanelLayout.PanelState.EXPANDED.hashCode()) {
if (savedInstanceState.getInt(Constants.MAIN_SLIDE_PANEL_STATE, -1) == PanelState.EXPANDED.hashCode()) {
panelSlideListener.onPanelStateChanged(null, null, PanelState.EXPANDED);
}
}
@Override
public void setContentView(int viewId) {
super.setContentView(viewId);
if(drawerToggle != null){
drawerToggle.setDrawerIndicatorEnabled(true);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED && secondaryFragment == null) {
slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
} else if(onBackPressedSupport()) {
finish();
}
}
@Override
public boolean onBackPressedSupport() {
if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
removeCurrent();
return false;
if (isNowPlayingOpen()) {
if (secondaryFragment == null) {
closeNowPlaying();
} else {
removeCurrent();
}
} else {
return super.onBackPressedSupport();
super.onBackPressed();
}
}
@Override
public SubsonicFragment getCurrentFragment() {
if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
if(secondaryFragment == null) {
if (isNowPlayingOpen()) {
if (secondaryFragment == null) {
return nowPlayingFragment;
} else {
return secondaryFragment;
@ -523,7 +448,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
@Override
public void replaceFragment(SubsonicFragment fragment, int tag, boolean replaceCurrent) {
if(slideUpPanel != null && slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED && !isPanelClosing) {
if (slideUpPanel != null && isNowPlayingOpen() && !isPanelClosing) {
secondaryFragment = fragment;
nowPlayingFragment.setPrimaryFragment(false);
secondaryFragment.setPrimaryFragment(true);
@ -538,9 +463,10 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
super.replaceFragment(fragment, tag, replaceCurrent);
}
}
@Override
public void removeCurrent() {
if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED && secondaryFragment != null) {
if (isNowPlayingOpen() && secondaryFragment != null) {
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left);
trans.remove(secondaryFragment);
@ -557,7 +483,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
@Override
public void setTitle(CharSequence title) {
if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
if (isNowPlayingOpen()) {
getSupportActionBar().setTitle(title);
} else {
super.setTitle(title);
@ -568,8 +494,8 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
protected void drawerItemSelected(String fragmentType) {
super.drawerItemSelected(fragmentType);
if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
if (isNowPlayingOpen() && !resuming) {
closeNowPlaying();
}
}
@ -579,7 +505,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
// Clear existing stack
for(int i = backStack.size() - 1; i >= 0; i--) {
for (int i = backStack.size() - 1; i >= 0; i--) {
trans.remove(backStack.get(i));
}
trans.remove(currentFragment);
@ -594,62 +520,45 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
trans.commit();
supportInvalidateOptionsMenu();
recreateSpinner();
if(drawer != null) {
if (drawer != null) {
drawer.closeDrawers();
}
if(secondaryContainer != null) {
if (secondaryContainer != null) {
secondaryContainer.setVisibility(View.GONE);
}
if(drawerToggle != null) {
if (drawerToggle != null) {
drawerToggle.setDrawerIndicatorEnabled(true);
}
}
@Override
public void openNowPlaying() {
slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);
slideUpPanel.setPanelState(PanelState.EXPANDED);
}
@Override
public void closeNowPlaying() {
slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
slideUpPanel.setPanelState(PanelState.COLLAPSED);
isPanelClosing = true;
}
private SubsonicFragment getNewFragment(String fragmentType) {
if("Artist".equals(fragmentType)) {
return new SelectArtistFragment();
} else if("Playlist".equals(fragmentType)) {
return new SelectPlaylistFragment();
} else if("Download".equals(fragmentType)) {
return new DownloadFragment();
} else {
return new SelectArtistFragment();
}
private boolean isNowPlayingOpen() {
return slideUpPanel.getPanelState() == PanelState.EXPANDED;
}
public void checkUpdates() {
try {
String version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
int ver = Integer.parseInt(version.replace(".", ""));
Updater updater = new Updater(ver);
updater.checkUpdates(this);
}
catch(Exception e) {
private SubsonicFragment getNewFragment(String fragmentType) {
switch (fragmentType) {
case "Playlist":
return new SelectPlaylistFragment();
case "Download":
return new DownloadFragment();
default:
return new SelectArtistFragment();
}
}
private void loadSession() {
loadSettings();
// If we are on Subsonic 5.2+, save play queue
if(!Util.isOffline(this)) {
loadRemotePlayQueue();
}
sessionInitialized = true;
}
private void loadSettings() {
PreferenceManager.setDefaultValues(this, R.xml.settings_appearance, false);
PreferenceManager.setDefaultValues(this, R.xml.settings_cache, false);
PreferenceManager.setDefaultValues(this, R.xml.settings_playback, false);
@ -660,9 +569,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
} else {
String path = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
File cacheLocation = new File(path);
if(!FileUtil.verifyCanWrite(cacheLocation)) {
if (!FileUtil.verifyCanWrite(cacheLocation)) {
// Only warn user if there is a difference saved
if(resetCacheLocation(prefs)) {
if (resetCacheLocation(prefs)) {
Util.info(this, R.string.common_warning, R.string.settings_cache_location_reset);
}
}
@ -679,17 +588,19 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
editor.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
editor.apply();
}
if(!prefs.contains(Constants.PREFERENCES_KEY_SERVER_COUNT)) {
if (!prefs.contains(Constants.PREFERENCES_KEY_SERVER_COUNT)) {
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(Constants.PREFERENCES_KEY_SERVER_COUNT, 1);
editor.apply();
}
sessionInitialized = true;
}
private boolean resetCacheLocation(SharedPreferences prefs) {
String newDirectory = FileUtil.getDefaultMusicDirectory(this).getPath();
String oldDirectory = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
if(newDirectory == null || (oldDirectory != null && newDirectory.equals(oldDirectory))) {
if (oldDirectory != null && newDirectory.equals(oldDirectory)) {
return false;
} else {
SharedPreferences.Editor editor = prefs.edit();
@ -699,52 +610,12 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
}
}
private void loadRemotePlayQueue() {
if(Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_RESUME_PLAY_QUEUE_NEVER, false)) {
return;
}
final SubsonicActivity context = this;
new SilentBackgroundTask<Void>(this) {
private PlayerQueue playerQueue;
@Override
protected Void doInBackground() throws Throwable {
try {
MusicService musicService = MusicServiceFactory.getMusicService(context);
PlayerQueue remoteState = musicService.getPlayQueue(context, null);
// Make sure we wait until download service is ready
DownloadService downloadService = getDownloadService();
while(downloadService == null || !downloadService.isInitialized()) {
Util.sleepQuietly(100L);
downloadService = getDownloadService();
}
// If we had a remote state and it's changed is more recent than our existing state
if(remoteState != null && remoteState.changed != null) {
// Check if changed + 30 seconds since some servers have slight skew
Date remoteChange = new Date(remoteState.changed.getTime() - ALLOWED_SKEW);
Date localChange = downloadService.getLastStateChanged();
if(localChange == null || localChange.before(remoteChange)) {
playerQueue = remoteState;
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to get playing queue to server", e);
}
return null;
}
}.execute();
}
private void createAccount() {
final Context context = this;
new SilentBackgroundTask<Void>(this) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE);
Account account = new Account(Constants.SYNC_ACCOUNT_NAME, Constants.SYNC_ACCOUNT_TYPE);
accountManager.addAccountExplicitly(account, null, null);
@ -759,27 +630,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
return null;
}
@Override
protected void done(Void result) {
}
}.execute();
}
private void showInfoDialog() {
if (!infoDialogDisplayed) {
infoDialogDisplayed = true;
if (Util.getRestUrl(this, null).contains("demo.subsonic.org")) {
Util.info(this, R.string.main_welcome_title, R.string.main_welcome_text);
}
}
}
public Toolbar getActiveToolbar() {
return slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED ? nowPlayingToolbar : mainToolbar;
}
@Override
public void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex) {
this.currentPlaying = currentPlaying;
@ -789,7 +642,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
song = currentPlaying.getSong();
trackView.setText(song.getTitle());
if(song.getArtist() != null) {
if (song.getArtist() != null) {
artistView.setVisibility(View.VISIBLE);
artistView.setText(song.getArtist());
} else {
@ -820,7 +673,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
@Override
public void onSongsChanged(List<DownloadFile> songs, DownloadFile currentPlaying, int currentPlayingIndex) {
if(this.currentPlaying != currentPlaying || this.currentPlaying == null) {
if (this.currentPlaying != currentPlaying || this.currentPlaying == null) {
onSongChanged(currentPlaying, currentPlayingIndex);
}
}
@ -831,7 +684,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
}
@Override
public void onStateUpdate(DownloadFile downloadFile, PlayerState playerState) {
public void onStateUpdate(PlayerState playerState) {
int[] attrs = new int[]{(playerState == PlayerState.STARTED) ? R.attr.actionbar_pause : R.attr.actionbar_start};
TypedArray typedArray = this.obtainStyledAttributes(attrs);
startButton.setImageResource(typedArray.getResourceId(0, 0));
@ -840,7 +693,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
@Override
public void onMetadataUpdate(MusicDirectory.Entry song, int fieldChange) {
if(song != null && coverArtView != null && fieldChange == DownloadService.METADATA_UPDATED_COVER_ART) {
if (song != null && coverArtView != null && fieldChange == DownloadService.METADATA_UPDATED_COVER_ART) {
int height = coverArtView.getHeight();
if (height <= 0) {
int[] attrs = new int[]{R.attr.actionBarSize};
@ -851,7 +704,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
getImageLoader().loadImage(coverArtView, song, false, height, false);
// We need to update it immediately since it won't update if updater is not running for it
if(nowPlayingFragment != null && slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) {
if (nowPlayingFragment != null && slideUpPanel.getPanelState() == PanelState.COLLAPSED) {
nowPlayingFragment.onMetadataUpdate(song, fieldChange);
}
}

View File

@ -19,28 +19,24 @@
package net.nullsum.audinaut.activity;
import android.app.Activity;
import android.app.SearchManager;
import android.content.Intent;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.SearchRecentSuggestions;
import android.util.Log;
import net.nullsum.audinaut.fragments.SubsonicFragment;
import androidx.appcompat.app.AppCompatActivity;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.provider.AudinautSearchProvider;
/**
* Receives voice search queries and forwards to the SearchFragment.
*
* <p>
* http://android-developers.blogspot.com/2010/09/supporting-new-music-voice-action.html
*
* @author Sindre Mehus
*/
public class VoiceQueryReceiverActivity extends Activity {
private static final String TAG = VoiceQueryReceiverActivity.class.getSimpleName();
public class VoiceQueryReceiverActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
@ -57,6 +53,5 @@ public class VoiceQueryReceiverActivity extends Activity {
Util.startActivityWithoutTransition(VoiceQueryReceiverActivity.this, intent);
}
finish();
Util.disablePendingTransition(this);
}
}

View File

@ -17,12 +17,12 @@ package net.nullsum.audinaut.adapter;
import android.content.Context;
import java.util.List;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.util.ImageLoader;
import net.nullsum.audinaut.view.FastScroller;
import java.util.List;
public class AlphabeticalAlbumAdapter extends EntryInfiniteGridAdapter implements FastScroller.BubbleTextGetter {
public AlphabeticalAlbumAdapter(Context context, List<MusicDirectory.Entry> entries, ImageLoader imageLoader, boolean largeCell) {
super(context, entries, imageLoader, largeCell);
@ -31,8 +31,8 @@ public class AlphabeticalAlbumAdapter extends EntryInfiniteGridAdapter implement
@Override
public String getTextToShowInBubble(int position) {
// Make sure that we are not trying to get an item for the loading placeholder
if(position >= sections.get(0).size()) {
if(sections.get(0).size() > 0) {
if (position >= sections.get(0).size()) {
if (sections.get(0).size() > 0) {
return getTextToShowInBubble(position - 1);
} else {
return "*";

View File

@ -16,19 +16,15 @@
package net.nullsum.audinaut.adapter;
import android.content.Context;
import android.support.v7.widget.PopupMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.io.Serializable;
import java.util.List;
import androidx.appcompat.widget.PopupMenu;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.MusicDirectory.Entry;
import net.nullsum.audinaut.domain.MusicFolder;
import net.nullsum.audinaut.util.Util;
@ -37,16 +33,14 @@ import net.nullsum.audinaut.view.FastScroller;
import net.nullsum.audinaut.view.SongView;
import net.nullsum.audinaut.view.UpdateView;
import java.io.Serializable;
import java.util.List;
public class ArtistAdapter extends SectionAdapter<Serializable> implements FastScroller.BubbleTextGetter {
public static int VIEW_TYPE_SONG = 3;
public static int VIEW_TYPE_ARTIST = 4;
private List<MusicFolder> musicFolders;
private OnMusicFolderChanged onMusicFolderChanged;
public ArtistAdapter(Context context, List<Serializable> artists, OnItemClickedListener listener) {
this(context, artists, null, listener, null);
}
public static final int VIEW_TYPE_ARTIST = 4;
private static final int VIEW_TYPE_SONG = 3;
private final List<MusicFolder> musicFolders;
private final OnMusicFolderChanged onMusicFolderChanged;
public ArtistAdapter(Context context, List<Serializable> artists, List<MusicFolder> musicFolders, OnItemClickedListener onItemClickedListener, OnMusicFolderChanged onMusicFolderChanged) {
super(context, artists);
@ -54,7 +48,7 @@ public class ArtistAdapter extends SectionAdapter<Serializable> implements FastS
this.onItemClickedListener = onItemClickedListener;
this.onMusicFolderChanged = onMusicFolderChanged;
if(musicFolders != null) {
if (musicFolders != null) {
this.singleSectionHeader = true;
}
}
@ -62,46 +56,41 @@ public class ArtistAdapter extends SectionAdapter<Serializable> implements FastS
@Override
public UpdateView.UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
final View header = LayoutInflater.from(context).inflate(R.layout.select_artist_header, parent, false);
header.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PopupMenu popup = new PopupMenu(context, header.findViewById(R.id.select_artist_folder_2));
header.setOnClickListener(v -> {
PopupMenu popup = new PopupMenu(context, header.findViewById(R.id.select_artist_folder_2));
popup.getMenu().add(R.string.select_artist_all_folders);
popup.getMenu().add(R.string.select_artist_all_folders);
for (MusicFolder musicFolder : musicFolders) {
popup.getMenu().add(musicFolder.getName());
}
popup.setOnMenuItemClickListener(item -> {
for (MusicFolder musicFolder : musicFolders) {
popup.getMenu().add(musicFolder.getName());
}
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
for (MusicFolder musicFolder : musicFolders) {
if(item.getTitle().equals(musicFolder.getName())) {
if(onMusicFolderChanged != null) {
onMusicFolderChanged.onMusicFolderChanged(musicFolder);
}
return true;
}
}
if(onMusicFolderChanged != null) {
onMusicFolderChanged.onMusicFolderChanged(null);
if (item.getTitle().equals(musicFolder.getName())) {
if (onMusicFolderChanged != null) {
onMusicFolderChanged.onMusicFolderChanged(musicFolder);
}
return true;
}
});
popup.show();
}
}
if (onMusicFolderChanged != null) {
onMusicFolderChanged.onMusicFolderChanged(null);
}
return true;
});
popup.show();
});
return new UpdateView.UpdateViewHolder(header, false);
return new UpdateView.UpdateViewHolder(header);
}
@Override
public void onBindHeaderHolder(UpdateView.UpdateViewHolder holder, String header, int sectionIndex) {
TextView folderName = (TextView) holder.getView().findViewById(R.id.select_artist_folder_2);
TextView folderName = holder.getView().findViewById(R.id.select_artist_folder_2);
String musicFolderId = Util.getSelectedMusicFolderId(context);
if(musicFolderId != null) {
if (musicFolderId != null) {
for (MusicFolder musicFolder : musicFolders) {
if (musicFolder.getId().equals(musicFolderId)) {
folderName.setText(musicFolder.getName());
@ -114,11 +103,11 @@ public class ArtistAdapter extends SectionAdapter<Serializable> implements FastS
}
@Override
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(int viewType) {
UpdateView updateView = null;
if(viewType == VIEW_TYPE_ARTIST) {
if (viewType == VIEW_TYPE_ARTIST) {
updateView = new ArtistView(context);
} else if(viewType == VIEW_TYPE_SONG) {
} else if (viewType == VIEW_TYPE_SONG) {
updateView = new SongView(context);
}
@ -128,9 +117,9 @@ public class ArtistAdapter extends SectionAdapter<Serializable> implements FastS
@Override
public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Serializable item, int viewType) {
UpdateView view = holder.getUpdateView();
if(viewType == VIEW_TYPE_ARTIST) {
if (viewType == VIEW_TYPE_ARTIST) {
view.setObject(item);
} else if(viewType == VIEW_TYPE_SONG) {
} else if (viewType == VIEW_TYPE_SONG) {
SongView songView = (SongView) view;
Entry entry = (Entry) item;
songView.setObject(entry, checkable);
@ -139,7 +128,7 @@ public class ArtistAdapter extends SectionAdapter<Serializable> implements FastS
@Override
public int getItemViewType(Serializable item) {
if(item instanceof Artist) {
if (item instanceof Artist) {
return VIEW_TYPE_ARTIST;
} else {
return VIEW_TYPE_SONG;
@ -149,7 +138,7 @@ public class ArtistAdapter extends SectionAdapter<Serializable> implements FastS
@Override
public String getTextToShowInBubble(int position) {
Object item = getItemForPosition(position);
if(item instanceof Artist) {
if (item instanceof Artist) {
return getNameIndex(((Artist) item).getName(), true);
} else {
return null;

View File

@ -16,15 +16,14 @@
package net.nullsum.audinaut.adapter;
import android.content.Context;
import android.view.ViewGroup;
import java.util.List;
import net.nullsum.audinaut.view.BasicListView;
import net.nullsum.audinaut.view.UpdateView;
import java.util.List;
public class BasicListAdapter extends SectionAdapter<String> {
public static int VIEW_TYPE_LINE = 1;
private static final int VIEW_TYPE_LINE = 1;
public BasicListAdapter(Context context, List<String> strings, OnItemClickedListener listener) {
super(context, strings);
@ -32,7 +31,7 @@ public class BasicListAdapter extends SectionAdapter<String> {
}
@Override
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(int viewType) {
return new UpdateView.UpdateViewHolder(new BasicListView(context));
}

View File

@ -16,8 +16,6 @@
package net.nullsum.audinaut.adapter;
import android.content.Context;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
@ -25,32 +23,32 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.List;
import net.nullsum.audinaut.R;
public class DetailsAdapter extends ArrayAdapter<String> {
private List<String> headers;
private List<String> details;
import java.util.List;
public DetailsAdapter(Context context, int layout, List<String> headers, List<String> details) {
super(context, layout, headers);
public class DetailsAdapter extends ArrayAdapter<String> {
private final List<String> headers;
private final List<String> details;
public DetailsAdapter(Context context, List<String> headers, List<String> details) {
super(context, R.layout.details_item, headers);
this.headers = headers;
this.details = details;
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if(convertView == null) {
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(R.layout.details_item, null);
} else {
view = convertView;
}
TextView nameView = (TextView) view.findViewById(R.id.detail_name);
TextView detailsView = (TextView) view.findViewById(R.id.detail_value);
TextView nameView = view.findViewById(R.id.detail_name);
TextView detailsView = view.findViewById(R.id.detail_value);
nameView.setText(headers.get(position));

View File

@ -18,12 +18,6 @@ package net.nullsum.audinaut.adapter;
import android.content.Context;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import java.util.List;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.service.DownloadFile;
@ -32,8 +26,10 @@ import net.nullsum.audinaut.view.FastScroller;
import net.nullsum.audinaut.view.SongView;
import net.nullsum.audinaut.view.UpdateView;
import java.util.List;
public class DownloadFileAdapter extends SectionAdapter<DownloadFile> implements FastScroller.BubbleTextGetter {
public static int VIEW_TYPE_DOWNLOAD_FILE = 1;
private static final int VIEW_TYPE_DOWNLOAD_FILE = 1;
public DownloadFileAdapter(Context context, List<DownloadFile> entries, OnItemClickedListener onItemClickedListener) {
super(context, entries);
@ -42,7 +38,7 @@ public class DownloadFileAdapter extends SectionAdapter<DownloadFile> implements
}
@Override
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(int viewType) {
return new UpdateView.UpdateViewHolder(new SongView(context));
}
@ -65,7 +61,7 @@ public class DownloadFileAdapter extends SectionAdapter<DownloadFile> implements
@Override
public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {
if(Util.isOffline(context)) {
if (Util.isOffline(context)) {
menuInflater.inflate(R.menu.multiselect_nowplaying_offline, menu);
} else {
menuInflater.inflate(R.menu.multiselect_nowplaying, menu);

View File

@ -18,11 +18,6 @@ package net.nullsum.audinaut.adapter;
import android.content.Context;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicDirectory;
@ -34,19 +29,17 @@ import net.nullsum.audinaut.view.SongView;
import net.nullsum.audinaut.view.UpdateView;
import net.nullsum.audinaut.view.UpdateView.UpdateViewHolder;
import java.util.List;
public class EntryGridAdapter extends SectionAdapter<Entry> {
private static String TAG = EntryGridAdapter.class.getSimpleName();
public static int VIEW_TYPE_ALBUM_CELL = 1;
public static int VIEW_TYPE_ALBUM_LINE = 2;
public static int VIEW_TYPE_SONG = 3;
private ImageLoader imageLoader;
private boolean largeAlbums;
public static final int VIEW_TYPE_ALBUM_CELL = 1;
public static final int VIEW_TYPE_ALBUM_LINE = 2;
public static final int VIEW_TYPE_SONG = 3;
private final ImageLoader imageLoader;
private final boolean largeAlbums;
private boolean showArtist = false;
private boolean showAlbum = false;
private boolean removeFromPlaylist = false;
private View header;
public EntryGridAdapter(Context context, List<Entry> entries, ImageLoader imageLoader, boolean largeCell) {
super(context, entries);
@ -55,12 +48,12 @@ public class EntryGridAdapter extends SectionAdapter<Entry> {
// Always show artist if they aren't all the same
String artist = null;
for(MusicDirectory.Entry entry: entries) {
if(artist == null) {
for (MusicDirectory.Entry entry : entries) {
if (artist == null) {
artist = entry.getArtist();
}
if(artist != null && !artist.equals(entry.getArtist())) {
if (artist != null && !artist.equals(entry.getArtist())) {
showArtist = true;
}
}
@ -68,11 +61,11 @@ public class EntryGridAdapter extends SectionAdapter<Entry> {
}
@Override
public UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
public UpdateViewHolder onCreateSectionViewHolder(int viewType) {
UpdateView updateView = null;
if(viewType == VIEW_TYPE_ALBUM_LINE || viewType == VIEW_TYPE_ALBUM_CELL) {
if (viewType == VIEW_TYPE_ALBUM_LINE || viewType == VIEW_TYPE_ALBUM_CELL) {
updateView = new AlbumView(context, viewType == VIEW_TYPE_ALBUM_CELL);
} else if(viewType == VIEW_TYPE_SONG) {
} else if (viewType == VIEW_TYPE_SONG) {
updateView = new SongView(context);
}
@ -82,27 +75,24 @@ public class EntryGridAdapter extends SectionAdapter<Entry> {
@Override
public void onBindViewHolder(UpdateViewHolder holder, Entry entry, int viewType) {
UpdateView view = holder.getUpdateView();
if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
if (viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
AlbumView albumView = (AlbumView) view;
albumView.setShowArtist(showArtist);
albumView.setObject(entry, imageLoader);
} else if(viewType == VIEW_TYPE_SONG) {
} else if (viewType == VIEW_TYPE_SONG) {
SongView songView = (SongView) view;
songView.setShowAlbum(showAlbum);
songView.setObject(entry, checkable);
}
}
public UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
return new UpdateViewHolder(header, false);
}
public void onBindHeaderHolder(UpdateViewHolder holder, String header, int sectionIndex) {
}
@Override
public int getItemViewType(Entry entry) {
if(entry.isDirectory()) {
if (entry.isDirectory()) {
if (largeAlbums) {
return VIEW_TYPE_ALBUM_CELL;
} else {
@ -113,27 +103,16 @@ public class EntryGridAdapter extends SectionAdapter<Entry> {
}
}
public void setHeader(View header) {
this.header = header;
this.singleSectionHeader = true;
}
public View getHeader() {
return header;
public void setShowArtist() {
this.showArtist = true;
}
public void setShowArtist(boolean showArtist) {
this.showArtist = showArtist;
}
public void setShowAlbum(boolean showAlbum) {
this.showAlbum = showAlbum;
public void setShowAlbum() {
this.showAlbum = true;
}
public void removeAt(int index) {
sections.get(0).remove(index);
if(header != null) {
index++;
}
notifyItemRemoved(index);
}
@ -143,13 +122,13 @@ public class EntryGridAdapter extends SectionAdapter<Entry> {
@Override
public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {
if(Util.isOffline(context)) {
if (Util.isOffline(context)) {
menuInflater.inflate(R.menu.multiselect_media_offline, menu);
} else {
menuInflater.inflate(R.menu.multiselect_media, menu);
}
if(!removeFromPlaylist) {
if (!removeFromPlaylist) {
menu.removeItem(R.id.menu_remove_playlist);
}
}

View File

@ -20,8 +20,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.MusicDirectory.Entry;
@ -32,8 +30,10 @@ import net.nullsum.audinaut.util.ImageLoader;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.view.UpdateView;
import java.util.List;
public class EntryInfiniteGridAdapter extends EntryGridAdapter {
public static int VIEW_TYPE_LOADING = 4;
public static final int VIEW_TYPE_LOADING = 4;
private String type;
private String extra;
@ -48,10 +48,10 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter {
@Override
public UpdateView.UpdateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == VIEW_TYPE_LOADING) {
if (viewType == VIEW_TYPE_LOADING) {
View progress = LayoutInflater.from(context).inflate(R.layout.tab_progress, null);
progress.setVisibility(View.VISIBLE);
return new UpdateView.UpdateViewHolder(progress, false);
return new UpdateView.UpdateViewHolder(progress);
}
return super.onCreateViewHolder(parent, viewType);
@ -59,7 +59,7 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter {
@Override
public int getItemViewType(int position) {
if(isLoadingView(position)) {
if (isLoadingView(position)) {
return VIEW_TYPE_LOADING;
}
@ -68,7 +68,7 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter {
@Override
public void onBindViewHolder(UpdateView.UpdateViewHolder holder, int position) {
if(!isLoadingView(position)) {
if (!isLoadingView(position)) {
super.onBindViewHolder(holder, position);
}
}
@ -77,7 +77,7 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter {
public int getItemCount() {
int size = super.getItemCount();
if(!allLoaded) {
if (!allLoaded) {
size++;
}
@ -89,13 +89,13 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter {
this.extra = extra;
this.size = size;
if(super.getItemCount() < size) {
if (super.getItemCount() < size) {
allLoaded = true;
}
}
public void loadMore() {
if(loading || allLoaded) {
if (loading || allLoaded) {
return;
}
loading = true;
@ -114,7 +114,7 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter {
appendCachedData(newData);
loading = false;
if(newData.size() < size) {
if (newData.size() < size) {
allLoaded = true;
notifyDataSetChanged();
}
@ -122,15 +122,15 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter {
}.execute();
}
protected List<Entry> cacheInBackground() throws Exception {
private List<Entry> cacheInBackground() throws Exception {
MusicService service = MusicServiceFactory.getMusicService(context);
MusicDirectory result;
int offset = sections.get(0).size();
if("genres".equals(type) || "years".equals(type)) {
if ("genres".equals(type) || "years".equals(type)) {
result = service.getAlbumList(type, extra, size, offset, false, context, null);
} else if("genres".equals(type) || "genres-songs".equals(type)) {
} else if ("genres".equals(type) || "genres-songs".equals(type)) {
result = service.getSongsByGenre(extra, size, offset, context, null);
}else if(type.indexOf(MainFragment.SONGS_LIST_PREFIX) != -1) {
} else if (type.contains(MainFragment.SONGS_LIST_PREFIX)) {
result = service.getSongList(type, size, offset, context, null);
} else {
result = service.getAlbumList(type, size, offset, false, context, null);
@ -138,15 +138,15 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter {
return result.getChildren();
}
protected void appendCachedData(List<Entry> newData) {
if(newData.size() > 0) {
private void appendCachedData(List<Entry> newData) {
if (newData.size() > 0) {
int start = sections.get(0).size();
sections.get(0).addAll(newData);
this.notifyItemRangeInserted(start, newData.size());
}
}
protected boolean isLoadingView(int position) {
private boolean isLoadingView(int position) {
return !allLoaded && position >= sections.get(0).size();
}
}

View File

@ -16,51 +16,34 @@
package net.nullsum.audinaut.adapter;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.util.DrawableTint;
import net.nullsum.audinaut.view.BasicHeaderView;
import net.nullsum.audinaut.view.UpdateView;
public abstract class ExpandableSectionAdapter<T> extends SectionAdapter<T> {
private static final String TAG = ExpandableSectionAdapter.class.getSimpleName();
import java.util.ArrayList;
import java.util.List;
abstract class ExpandableSectionAdapter<T> extends SectionAdapter<T> {
private static final int DEFAULT_VISIBLE = 4;
private static final int EXPAND_TOGGLE = R.attr.select_server;
private static final int COLLAPSE_TOGGLE = R.attr.select_tabs;
protected List<Integer> sectionsDefaultVisible;
protected List<List<T>> sectionsExtras;
protected int expandToggleRes;
protected int collapseToggleRes;
private List<List<T>> sectionsExtras;
protected ExpandableSectionAdapter() {}
public ExpandableSectionAdapter(Context context, List<T> section) {
List<List<T>> sections = new ArrayList<>();
sections.add(section);
ExpandableSectionAdapter() {
}
init(context, Arrays.asList("Section"), sections, Arrays.asList((Integer) null));
}
public ExpandableSectionAdapter(Context context, List<String> headers, List<List<T>> sections) {
init(context, headers, sections, null);
}
public ExpandableSectionAdapter(Context context, List<String> headers, List<List<T>> sections, List<Integer> sectionsDefaultVisible) {
init(context, headers, sections, sectionsDefaultVisible);
}
protected void init(Context context, List<String> headers, List<List<T>> fullSections, List<Integer> sectionsDefaultVisible) {
void init(Context context, List<String> headers, List<List<T>> fullSections, List<Integer> sectionsDefaultVisible) {
this.context = context;
this.headers = headers;
this.sectionsDefaultVisible = sectionsDefaultVisible;
if(sectionsDefaultVisible == null) {
if (sectionsDefaultVisible == null) {
sectionsDefaultVisible = new ArrayList<>(fullSections.size());
for(int i = 0; i < fullSections.size(); i++) {
for (int i = 0; i < fullSections.size(); i++) {
sectionsDefaultVisible.add(DEFAULT_VISIBLE);
}
}
@ -68,11 +51,11 @@ public abstract class ExpandableSectionAdapter<T> extends SectionAdapter<T> {
this.sections = new ArrayList<>();
this.sectionsExtras = new ArrayList<>();
int i = 0;
for(List<T> fullSection: fullSections) {
for (List<T> fullSection : fullSections) {
List<T> visibleSection = new ArrayList<>();
Integer defaultVisible = sectionsDefaultVisible.get(i);
if(defaultVisible == null || defaultVisible >= fullSection.size()) {
if (defaultVisible == null || defaultVisible >= fullSection.size()) {
visibleSection.addAll(fullSection);
this.sectionsExtras.add(null);
} else {
@ -83,9 +66,6 @@ public abstract class ExpandableSectionAdapter<T> extends SectionAdapter<T> {
i++;
}
expandToggleRes = DrawableTint.getDrawableRes(context, EXPAND_TOGGLE);
collapseToggleRes = DrawableTint.getDrawableRes(context, COLLAPSE_TOGGLE);
}
@Override
@ -96,39 +76,36 @@ public abstract class ExpandableSectionAdapter<T> extends SectionAdapter<T> {
@Override
public void onBindHeaderHolder(UpdateView.UpdateViewHolder holder, String header, final int sectionIndex) {
UpdateView view = holder.getUpdateView();
ImageView toggleSelectionView = (ImageView) view.findViewById(R.id.item_select);
ImageView toggleSelectionView = view.findViewById(R.id.item_select);
List<T> visibleSelection = sections.get(sectionIndex);
List<T> sectionExtras = sectionsExtras.get(sectionIndex);
if(sectionExtras != null && !sectionExtras.isEmpty()) {
if (sectionExtras != null && !sectionExtras.isEmpty()) {
toggleSelectionView.setVisibility(View.VISIBLE);
toggleSelectionView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<T> visibleSelection = sections.get(sectionIndex);
List<T> sectionExtras = sectionsExtras.get(sectionIndex);
toggleSelectionView.setOnClickListener(v -> {
List<T> visibleSelection1 = sections.get(sectionIndex);
List<T> sectionExtras1 = sectionsExtras.get(sectionIndex);
// Update icon
int selectToggleAttr;
if (!visibleSelection.contains(sectionExtras.get(0))) {
selectToggleAttr = COLLAPSE_TOGGLE;
// Update icon
int selectToggleAttr;
if (!visibleSelection1.contains(sectionExtras1.get(0))) {
selectToggleAttr = COLLAPSE_TOGGLE;
// Update how many are displayed
int lastIndex = getItemPosition(visibleSelection.get(visibleSelection.size() - 1));
visibleSelection.addAll(sectionExtras);
notifyItemRangeInserted(lastIndex, sectionExtras.size());
} else {
selectToggleAttr = EXPAND_TOGGLE;
// Update how many are displayed
int lastIndex = getItemPosition(visibleSelection1.get(visibleSelection1.size() - 1));
visibleSelection1.addAll(sectionExtras1);
notifyItemRangeInserted(lastIndex, sectionExtras1.size());
} else {
selectToggleAttr = EXPAND_TOGGLE;
// Update how many are displayed
visibleSelection.removeAll(sectionExtras);
int lastIndex = getItemPosition(visibleSelection.get(visibleSelection.size() - 1));
notifyItemRangeRemoved(lastIndex, sectionExtras.size());
}
((ImageView) v).setImageResource(DrawableTint.getDrawableRes(context, selectToggleAttr));
// Update how many are displayed
visibleSelection1.removeAll(sectionExtras1);
int lastIndex = getItemPosition(visibleSelection1.get(visibleSelection1.size() - 1));
notifyItemRangeRemoved(lastIndex, sectionExtras1.size());
}
((ImageView) v).setImageResource(DrawableTint.getDrawableRes(context, selectToggleAttr));
});
int selectToggleAttr;
@ -143,8 +120,6 @@ public abstract class ExpandableSectionAdapter<T> extends SectionAdapter<T> {
toggleSelectionView.setVisibility(View.GONE);
}
if(view != null) {
view.setObject(header);
}
view.setObject(header);
}
}

View File

@ -16,7 +16,7 @@
package net.nullsum.audinaut.adapter;
import android.content.Context;
import android.view.ViewGroup;
import net.nullsum.audinaut.domain.Genre;
import net.nullsum.audinaut.view.FastScroller;
import net.nullsum.audinaut.view.GenreView;
@ -24,8 +24,8 @@ import net.nullsum.audinaut.view.UpdateView;
import java.util.List;
public class GenreAdapter extends SectionAdapter<Genre> implements FastScroller.BubbleTextGetter{
public static int VIEW_TYPE_GENRE = 1;
public class GenreAdapter extends SectionAdapter<Genre> implements FastScroller.BubbleTextGetter {
private static final int VIEW_TYPE_GENRE = 1;
public GenreAdapter(Context context, List<Genre> genres, OnItemClickedListener listener) {
super(context, genres);
@ -33,7 +33,7 @@ public class GenreAdapter extends SectionAdapter<Genre> implements FastScroller.
}
@Override
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(int viewType) {
return new UpdateView.UpdateViewHolder(new GenreView(context));
}

View File

@ -19,18 +19,16 @@ import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import java.util.List;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.view.BasicHeaderView;
import net.nullsum.audinaut.view.BasicListView;
import net.nullsum.audinaut.view.UpdateView;
import java.util.List;
public class MainAdapter extends SectionAdapter<Integer> {
public static final int VIEW_TYPE_ALBUM_LIST = 1;
private static final int VIEW_TYPE_ALBUM_LIST = 1;
public MainAdapter(Context context, List<String> headers, List<List<Integer>> sections, OnItemClickedListener onItemClickedListener) {
super(context, headers, sections);
@ -38,7 +36,7 @@ public class MainAdapter extends SectionAdapter<Integer> {
}
@Override
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(int viewType) {
UpdateView updateView = new BasicListView(context);
return new UpdateView.UpdateViewHolder(updateView);
}
@ -47,7 +45,7 @@ public class MainAdapter extends SectionAdapter<Integer> {
public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Integer item, int viewType) {
UpdateView updateView = holder.getUpdateView();
if(viewType == VIEW_TYPE_ALBUM_LIST) {
if (viewType == VIEW_TYPE_ALBUM_LIST) {
updateView.setObject(context.getResources().getString(item));
} else {
updateView.setObject(item);
@ -63,13 +61,14 @@ public class MainAdapter extends SectionAdapter<Integer> {
public UpdateView.UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
return new UpdateView.UpdateViewHolder(new BasicHeaderView(context, R.layout.album_list_header));
}
@Override
public void onBindHeaderHolder(UpdateView.UpdateViewHolder holder, String header, int sectionIndex) {
UpdateView view = holder.getUpdateView();
CheckBox checkBox = (CheckBox) view.findViewById(R.id.item_checkbox);
CheckBox checkBox = view.findViewById(R.id.item_checkbox);
String display;
if("songs".equals(header)) {
if ("songs".equals(header)) {
display = context.getResources().getString(R.string.search_songs);
checkBox.setVisibility(View.GONE);
} else {
@ -77,8 +76,6 @@ public class MainAdapter extends SectionAdapter<Integer> {
checkBox.setVisibility(View.GONE);
}
if(view != null) {
view.setObject(display);
}
view.setObject(display);
}
}

View File

@ -16,20 +16,19 @@ package net.nullsum.audinaut.adapter;
import android.content.Context;
import java.util.List;
import android.view.ViewGroup;
import net.nullsum.audinaut.domain.Playlist;
import net.nullsum.audinaut.util.ImageLoader;
import net.nullsum.audinaut.view.FastScroller;
import net.nullsum.audinaut.view.PlaylistView;
import net.nullsum.audinaut.view.UpdateView;
public class PlaylistAdapter extends SectionAdapter<Playlist> implements FastScroller.BubbleTextGetter {
public static int VIEW_TYPE_PLAYLIST = 1;
import java.util.List;
private ImageLoader imageLoader;
private boolean largeCell;
public class PlaylistAdapter extends SectionAdapter<Playlist> implements FastScroller.BubbleTextGetter {
private static final int VIEW_TYPE_PLAYLIST = 1;
private final ImageLoader imageLoader;
private final boolean largeCell;
public PlaylistAdapter(Context context, List<Playlist> playlists, ImageLoader imageLoader, boolean largeCell, OnItemClickedListener listener) {
super(context, playlists);
@ -37,15 +36,9 @@ public class PlaylistAdapter extends SectionAdapter<Playlist> implements FastScr
this.largeCell = largeCell;
this.onItemClickedListener = listener;
}
public PlaylistAdapter(Context context, List<String> headers, List<List<Playlist>> sections, ImageLoader imageLoader, boolean largeCell, OnItemClickedListener listener) {
super(context, headers, sections);
this.imageLoader = imageLoader;
this.largeCell = largeCell;
this.onItemClickedListener = listener;
}
@Override
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(int viewType) {
return new UpdateView.UpdateViewHolder(new PlaylistView(context, imageLoader, largeCell));
}
@ -63,7 +56,7 @@ public class PlaylistAdapter extends SectionAdapter<Playlist> implements FastScr
@Override
public String getTextToShowInBubble(int position) {
Object item = getItemForPosition(position);
if(item instanceof Playlist) {
if (item != null) {
return getNameIndex(((Playlist) item).getName());
} else {
return null;

View File

@ -19,38 +19,32 @@ import android.content.Context;
import android.content.res.Resources;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicDirectory.Entry;
import net.nullsum.audinaut.domain.SearchResult;
import net.nullsum.audinaut.util.DrawableTint;
import net.nullsum.audinaut.util.ImageLoader;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.view.AlbumView;
import net.nullsum.audinaut.view.ArtistView;
import net.nullsum.audinaut.view.BasicHeaderView;
import net.nullsum.audinaut.view.SongView;
import net.nullsum.audinaut.view.UpdateView;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import static net.nullsum.audinaut.adapter.ArtistAdapter.VIEW_TYPE_ARTIST;
import static net.nullsum.audinaut.adapter.EntryGridAdapter.VIEW_TYPE_ALBUM_CELL;
import static net.nullsum.audinaut.adapter.EntryGridAdapter.VIEW_TYPE_ALBUM_LINE;
import static net.nullsum.audinaut.adapter.EntryGridAdapter.VIEW_TYPE_SONG;
public class SearchAdapter extends ExpandableSectionAdapter<Serializable> {
private ImageLoader imageLoader;
private boolean largeAlbums;
private static final int MAX_ARTISTS = 10;
private static final int MAX_ALBUMS = 4;
private static final int MAX_SONGS = 10;
private final ImageLoader imageLoader;
private final boolean largeAlbums;
public SearchAdapter(Context context, SearchResult searchResult, ImageLoader imageLoader, boolean largeAlbums, OnItemClickedListener listener) {
this.imageLoader = imageLoader;
@ -60,17 +54,17 @@ public class SearchAdapter extends ExpandableSectionAdapter<Serializable> {
List<String> headers = new ArrayList<>();
List<Integer> defaultVisible = new ArrayList<>();
Resources res = context.getResources();
if(!searchResult.getArtists().isEmpty()) {
if (!searchResult.getArtists().isEmpty()) {
sections.add((List<Serializable>) (List<?>) searchResult.getArtists());
headers.add(res.getString(R.string.search_artists));
defaultVisible.add(MAX_ARTISTS);
}
if(!searchResult.getAlbums().isEmpty()) {
if (!searchResult.getAlbums().isEmpty()) {
sections.add((List<Serializable>) (List<?>) searchResult.getAlbums());
headers.add(res.getString(R.string.search_albums));
defaultVisible.add(MAX_ALBUMS);
}
if(!searchResult.getSongs().isEmpty()) {
if (!searchResult.getSongs().isEmpty()) {
sections.add((List<Serializable>) (List<?>) searchResult.getSongs());
headers.add(res.getString(R.string.search_songs));
defaultVisible.add(MAX_SONGS);
@ -82,14 +76,19 @@ public class SearchAdapter extends ExpandableSectionAdapter<Serializable> {
}
@Override
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(int viewType) {
UpdateView updateView = null;
if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
updateView = new AlbumView(context, viewType == VIEW_TYPE_ALBUM_CELL);
} else if(viewType == VIEW_TYPE_SONG) {
updateView = new SongView(context);
} else if(viewType == VIEW_TYPE_ARTIST) {
updateView = new ArtistView(context);
switch (viewType) {
case VIEW_TYPE_ALBUM_CELL:
case VIEW_TYPE_ALBUM_LINE:
updateView = new AlbumView(context, viewType == VIEW_TYPE_ALBUM_CELL);
break;
case VIEW_TYPE_SONG:
updateView = new SongView(context);
break;
case VIEW_TYPE_ARTIST:
updateView = new ArtistView(context);
break;
}
return new UpdateView.UpdateViewHolder(updateView);
@ -98,20 +97,25 @@ public class SearchAdapter extends ExpandableSectionAdapter<Serializable> {
@Override
public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Serializable item, int viewType) {
UpdateView view = holder.getUpdateView();
if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
AlbumView albumView = (AlbumView) view;
albumView.setObject((Entry) item, imageLoader);
} else if(viewType == VIEW_TYPE_SONG) {
SongView songView = (SongView) view;
songView.setObject((Entry) item, true);
} else if(viewType == VIEW_TYPE_ARTIST) {
view.setObject(item);
switch (viewType) {
case VIEW_TYPE_ALBUM_CELL:
case VIEW_TYPE_ALBUM_LINE:
AlbumView albumView = (AlbumView) view;
albumView.setObject((Entry) item, imageLoader);
break;
case VIEW_TYPE_SONG:
SongView songView = (SongView) view;
songView.setObject((Entry) item, true);
break;
case VIEW_TYPE_ARTIST:
view.setObject(item);
break;
}
}
@Override
public int getItemViewType(Serializable item) {
if(item instanceof Entry) {
if (item instanceof Entry) {
Entry entry = (Entry) item;
if (entry.isDirectory()) {
if (largeAlbums) {
@ -129,7 +133,7 @@ public class SearchAdapter extends ExpandableSectionAdapter<Serializable> {
@Override
public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {
if(Util.isOffline(context)) {
if (Util.isOffline(context)) {
menuInflater.inflate(R.menu.multiselect_media_offline, menu);
} else {
menuInflater.inflate(R.menu.multiselect_media, menu);

View File

@ -17,24 +17,16 @@ package net.nullsum.audinaut.adapter;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Build;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.PopupMenu;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import androidx.appcompat.view.ActionMode;
import androidx.recyclerview.widget.RecyclerView;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.activity.SubsonicFragmentActivity;
@ -45,129 +37,103 @@ import net.nullsum.audinaut.view.BasicHeaderView;
import net.nullsum.audinaut.view.UpdateView;
import net.nullsum.audinaut.view.UpdateView.UpdateViewHolder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewHolder<T>> {
private static String TAG = SectionAdapter.class.getSimpleName();
public static int VIEW_TYPE_HEADER = 0;
public static String[] ignoredArticles;
public static final int VIEW_TYPE_HEADER = 0;
private static final String TAG = SectionAdapter.class.getSimpleName();
private static String[] ignoredArticles;
private final List<T> selected = new ArrayList<>();
private final List<UpdateView> selectedViews = new ArrayList<>();
Context context;
List<String> headers;
List<List<T>> sections;
boolean singleSectionHeader;
OnItemClickedListener<T> onItemClickedListener;
boolean checkable = false;
private ActionMode currentActionMode;
protected Context context;
protected List<String> headers;
protected List<List<T>> sections;
protected boolean singleSectionHeader;
protected OnItemClickedListener<T> onItemClickedListener;
protected List<T> selected = new ArrayList<>();
protected List<UpdateView> selectedViews = new ArrayList<>();
protected ActionMode currentActionMode;
protected boolean checkable = false;
protected SectionAdapter() {}
public SectionAdapter(Context context, List<T> section) {
this(context, section, false);
SectionAdapter() {
}
public SectionAdapter(Context context, List<T> section, boolean singleSectionHeader) {
SectionAdapter(Context context, List<T> section) {
this.context = context;
this.headers = Arrays.asList("Section");
this.headers = Collections.singletonList("Section");
this.sections = new ArrayList<>();
this.sections.add(section);
this.singleSectionHeader = singleSectionHeader;
this.singleSectionHeader = false;
}
public SectionAdapter(Context context, List<String> headers, List<List<T>> sections) {
this(context, headers, sections, true);
}
public SectionAdapter(Context context, List<String> headers, List<List<T>> sections, boolean singleSectionHeader){
SectionAdapter(Context context, List<String> headers, List<List<T>> sections) {
this.context = context;
this.headers = headers;
this.sections = sections;
this.singleSectionHeader = singleSectionHeader;
}
public void replaceExistingData(List<T> section) {
this.sections = new ArrayList<>();
this.sections.add(section);
notifyDataSetChanged();
}
public void replaceExistingData(List<String> headers, List<List<T>> sections) {
this.headers = headers;
this.sections = sections;
notifyDataSetChanged();
this.singleSectionHeader = true;
}
@Override
public UpdateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == VIEW_TYPE_HEADER) {
if (viewType == VIEW_TYPE_HEADER) {
return onCreateHeaderHolder(parent);
} else {
final UpdateViewHolder<T> holder = onCreateSectionViewHolder(parent, viewType);
final UpdateViewHolder<T> holder = onCreateSectionViewHolder(viewType);
final UpdateView updateView = holder.getUpdateView();
if(updateView != null) {
updateView.getChildAt(0).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
T item = holder.getItem();
updateView.onClick();
if (currentActionMode != null) {
if(updateView.isCheckable()) {
if (selected.contains(item)) {
selected.remove(item);
selectedViews.remove(updateView);
setChecked(updateView, false);
} else {
selected.add(item);
selectedViews.add(updateView);
setChecked(updateView, true);
}
if (selected.isEmpty()) {
currentActionMode.finish();
} else {
currentActionMode.setTitle(context.getResources().getString(R.string.select_album_n_selected, selected.size()));
}
if (updateView != null) {
updateView.getChildAt(0).setOnClickListener(v -> {
T item = holder.getItem();
if (currentActionMode != null) {
if (updateView.isCheckable()) {
if (selected.contains(item)) {
selected.remove(item);
selectedViews.remove(updateView);
setChecked(updateView, false);
} else {
selected.add(item);
selectedViews.add(updateView);
setChecked(updateView, true);
}
if (selected.isEmpty()) {
currentActionMode.finish();
} else {
currentActionMode.setTitle(context.getResources().getString(R.string.select_album_n_selected, selected.size()));
}
} else if (onItemClickedListener != null) {
onItemClickedListener.onItemClicked(updateView, item);
}
} else if (onItemClickedListener != null) {
onItemClickedListener.onItemClicked(updateView, item);
}
});
View moreButton = updateView.findViewById(R.id.item_more);
if (moreButton != null) {
moreButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
final T item = holder.getItem();
if (onItemClickedListener != null) {
PopupMenu popup = new PopupMenu(context, v);
onItemClickedListener.onCreateContextMenu(popup.getMenu(), popup.getMenuInflater(), updateView, item);
moreButton.setOnClickListener(v -> {
try {
final T item = holder.getItem();
if (onItemClickedListener != null) {
PopupMenu popup = new PopupMenu(context, v);
onItemClickedListener.onCreateContextMenu(popup.getMenu(), popup.getMenuInflater(), updateView, item);
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
return onItemClickedListener.onContextItemSelected(menuItem, updateView, item);
}
});
popup.show();
}
} catch(Exception e) {
Log.w(TAG, "Failed to show popup", e);
popup.setOnMenuItemClickListener(menuItem -> onItemClickedListener.onContextItemSelected(menuItem, updateView, item));
popup.show();
}
} catch (Exception e) {
Log.w(TAG, "Failed to show popup", e);
}
});
if(checkable) {
updateView.getChildAt(0).setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(updateView.isCheckable()) {
if (currentActionMode == null) {
startActionMode(holder);
} else {
updateView.getChildAt(0).performClick();
}
if (checkable) {
updateView.getChildAt(0).setOnLongClickListener(v -> {
if (updateView.isCheckable()) {
if (currentActionMode == null) {
startActionMode(holder);
} else {
updateView.getChildAt(0).performClick();
}
return true;
}
return true;
});
}
}
@ -181,7 +147,7 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
public void onBindViewHolder(UpdateViewHolder holder, int position) {
UpdateView updateView = holder.getUpdateView();
if(sections.size() == 1 && !singleSectionHeader) {
if (sections.size() == 1 && !singleSectionHeader) {
T item = sections.get(0).get(position);
onBindViewHolder(holder, item, getItemViewType(position));
postBindView(updateView, item);
@ -191,15 +157,15 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
int subPosition = 0;
int subHeader = 0;
for(List<T> section: sections) {
for (List<T> section : sections) {
boolean validHeader = headers.get(subHeader) != null;
if(position == subPosition && validHeader) {
if (position == subPosition && validHeader) {
onBindHeaderHolder(holder, headers.get(subHeader), subHeader);
return;
}
int headerOffset = validHeader ? 1 : 0;
if(position < (subPosition + section.size() + headerOffset)) {
if (position < (subPosition + section.size() + headerOffset)) {
T item = section.get(position - subPosition - headerOffset);
onBindViewHolder(holder, item, getItemViewType(item));
@ -209,7 +175,7 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
}
subPosition += section.size();
if(validHeader) {
if (validHeader) {
subPosition += 1;
}
subHeader++;
@ -217,13 +183,13 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
}
private void postBindView(UpdateView updateView, T item) {
if(updateView.isCheckable()) {
if (updateView.isCheckable()) {
setChecked(updateView, selected.contains(item));
}
View moreButton = updateView.findViewById(R.id.item_more);
if(moreButton != null) {
if(onItemClickedListener != null) {
if (moreButton != null) {
if (onItemClickedListener != null) {
PopupMenu popup = new PopupMenu(context, moreButton);
Menu menu = popup.getMenu();
onItemClickedListener.onCreateContextMenu(popup.getMenu(), popup.getMenuInflater(), updateView, item);
@ -240,17 +206,17 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
@Override
public int getItemCount() {
if(sections.size() == 1 && !singleSectionHeader) {
if (sections.size() == 1 && !singleSectionHeader) {
return sections.get(0).size();
}
int count = 0;
for(String header: headers) {
if(header != null) {
for (String header : headers) {
if (header != null) {
count++;
}
}
for(List<T> section: sections) {
for (List<T> section : sections) {
count += section.size();
}
@ -259,25 +225,25 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
@Override
public int getItemViewType(int position) {
if(sections.size() == 1 && !singleSectionHeader) {
if (sections.size() == 1 && !singleSectionHeader) {
return getItemViewType(sections.get(0).get(position));
}
int subPosition = 0;
int subHeader = 0;
for(List<T> section: sections) {
for (List<T> section : sections) {
boolean validHeader = headers.get(subHeader) != null;
if(position == subPosition && validHeader) {
if (position == subPosition && validHeader) {
return VIEW_TYPE_HEADER;
}
int headerOffset = validHeader ? 1 : 0;
if(position < (subPosition + section.size() + headerOffset)) {
if (position < (subPosition + section.size() + headerOffset)) {
return getItemViewType(section.get(position - subPosition - headerOffset));
}
subPosition += section.size();
if(validHeader) {
if (validHeader) {
subPosition += 1;
}
subHeader++;
@ -286,28 +252,29 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
return -1;
}
public UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
return new UpdateViewHolder(new BasicHeaderView(context));
}
public void onBindHeaderHolder(UpdateViewHolder holder, String header, int sectionIndex) {
void onBindHeaderHolder(UpdateViewHolder holder, String header, int sectionIndex) {
UpdateView view = holder.getUpdateView();
if(view != null) {
if (view != null) {
view.setObject(header);
}
}
public T getItemForPosition(int position) {
if(sections.size() == 1 && !singleSectionHeader) {
T getItemForPosition(int position) {
if (sections.size() == 1 && !singleSectionHeader) {
return sections.get(0).get(position);
}
int subPosition = 0;
for(List<T> section: sections) {
if(position == subPosition) {
for (List<T> section : sections) {
if (position == subPosition) {
return null;
}
if(position <= (subPosition + section.size())) {
if (position <= (subPosition + section.size())) {
return section.get(position - subPosition - 1);
}
@ -316,17 +283,18 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
return null;
}
public int getItemPosition(T item) {
if(sections.size() == 1 && !singleSectionHeader) {
if (sections.size() == 1 && !singleSectionHeader) {
return sections.get(0).indexOf(item);
}
int subPosition = 0;
for(List<T> section: sections) {
for (List<T> section : sections) {
subPosition += section.size() + 1;
int position = section.indexOf(item);
if(position != -1) {
if (position != -1) {
return position + subPosition;
}
}
@ -341,24 +309,15 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
public void addSelected(T item) {
selected.add(item);
}
public List<T> getSelected() {
List<T> selected = new ArrayList<>();
selected.addAll(this.selected);
return selected;
return new ArrayList<>(this.selected);
}
public void clearSelected() {
// TODO: This needs to work with multiple sections
for(T item: selected) {
int index = sections.get(0).indexOf(item);
if(singleSectionHeader) {
index++;
}
}
selected.clear();
for(UpdateView updateView: selectedViews) {
for (UpdateView updateView : selectedViews) {
updateView.setChecked(false);
}
}
@ -366,9 +325,9 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
public void moveItem(int from, int to) {
List<T> section = sections.get(0);
int max = section.size();
if(to >= max) {
if (to >= max) {
to = max - 1;
} else if(to < 0) {
} else if (to < 0) {
to = 0;
}
@ -377,10 +336,11 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
notifyItemMoved(from, to);
}
public void removeItem(T item) {
int subPosition = 0;
for(List<T> section: sections) {
if(sections.size() > 1 || singleSectionHeader) {
for (List<T> section : sections) {
if (sections.size() > 1 || singleSectionHeader) {
subPosition++;
}
@ -395,16 +355,18 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
}
}
public abstract UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType);
public abstract void onBindViewHolder(UpdateViewHolder holder, T item, int viewType);
public abstract int getItemViewType(T item);
public void setCheckable(boolean checkable) {
this.checkable = checkable;
}
public void setChecked(UpdateView updateView, boolean checked) {
protected abstract UpdateView.UpdateViewHolder onCreateSectionViewHolder(int viewType);
protected abstract void onBindViewHolder(UpdateViewHolder holder, T item, int viewType);
protected abstract int getItemViewType(T item);
private void setChecked(UpdateView updateView, boolean checked) {
updateView.setChecked(checked);
}
public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {}
void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {
}
private void startActionMode(final UpdateView.UpdateViewHolder<T> holder) {
final UpdateView<T> updateView = holder.getUpdateView();
@ -424,17 +386,6 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
MenuUtil.hideMenuItems(context, menu, updateView);
mode.setTitle(context.getResources().getString(R.string.select_album_n_selected, selected.size()));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true);
int colorPrimaryDark = typedValue.data;
Window window = ((SubsonicFragmentActivity) context).getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.setStatusBarColor(colorPrimaryDark);
}
return true;
}
@ -461,30 +412,27 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
updateView.setChecked(false);
}
selectedViews.clear();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)) {
Window window = ((SubsonicFragmentActivity) context).getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
});
}
}
public void stopActionMode() {
if(currentActionMode != null) {
if (currentActionMode != null) {
currentActionMode.finish();
}
}
public String getNameIndex(String name) {
String getNameIndex(String name) {
return getNameIndex(name, false);
}
public String getNameIndex(String name, boolean removeIgnoredArticles) {
if(name == null) {
String getNameIndex(String name, boolean removeIgnoredArticles) {
if (name == null) {
return "*";
}
if(removeIgnoredArticles) {
if (removeIgnoredArticles) {
if (ignoredArticles == null) {
SharedPreferences prefs = Util.getPreferences(context);
String ignoredArticlesString = prefs.getString(Constants.CACHE_KEY_IGNORE, "The El La Los Las Le Les");
@ -510,7 +458,9 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
public interface OnItemClickedListener<T> {
void onItemClicked(UpdateView<T> updateView, T item);
void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<T> updateView, T item);
boolean onContextItemSelected(MenuItem menuItem, UpdateView<T> updateView, T item);
}
}

View File

@ -1,121 +0,0 @@
/*
This file is part of Subsonic.
Subsonic 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.
Subsonic 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.
You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 (C) Scott Jackson
*/
package net.nullsum.audinaut.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.User;
import net.nullsum.audinaut.util.ImageLoader;
import net.nullsum.audinaut.util.UserUtil;
import net.nullsum.audinaut.view.BasicHeaderView;
import net.nullsum.audinaut.view.RecyclingImageView;
import net.nullsum.audinaut.view.SettingView;
import net.nullsum.audinaut.view.UpdateView;
import static net.nullsum.audinaut.domain.User.Setting;
public class SettingsAdapter extends SectionAdapter<Setting> {
private static final String TAG = SettingsAdapter.class.getSimpleName();
public final int VIEW_TYPE_SETTING = 1;
public final int VIEW_TYPE_SETTING_HEADER = 2;
private final User user;
private final boolean editable;
private final ImageLoader imageLoader;
public SettingsAdapter(Context context, User user, List<String> headers, List<List<User.Setting>> settingSections, ImageLoader imageLoader, boolean editable, OnItemClickedListener<Setting> onItemClickedListener) {
super(context, headers, settingSections, imageLoader != null);
this.user = user;
this.imageLoader = imageLoader;
this.editable = editable;
this.onItemClickedListener = onItemClickedListener;
for(List<Setting> settings: sections) {
for (Setting setting : settings) {
if (setting.getValue()) {
addSelected(setting);
}
}
}
}
@Override
public int getItemViewType(int position) {
int viewType = super.getItemViewType(position);
if(viewType == SectionAdapter.VIEW_TYPE_HEADER) {
if(position == 0 && imageLoader != null) {
return VIEW_TYPE_HEADER;
} else {
return VIEW_TYPE_SETTING_HEADER;
}
} else {
return viewType;
}
}
public void onBindHeaderHolder(UpdateView.UpdateViewHolder holder, String description, int sectionIndex) {
View header = holder.getView();
}
@Override
public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
if(viewType == VIEW_TYPE_SETTING_HEADER) {
return new UpdateView.UpdateViewHolder(new BasicHeaderView(context));
} else {
return new UpdateView.UpdateViewHolder(new SettingView(context));
}
}
@Override
public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Setting item, int viewType) {
holder.getUpdateView().setObject(item, editable);
}
@Override
public int getItemViewType(Setting item) {
return VIEW_TYPE_SETTING;
}
@Override
public void setChecked(UpdateView updateView, boolean checked) {
if(updateView instanceof SettingView) {
updateView.setChecked(checked);
}
}
public static SettingsAdapter getSettingsAdapter(Context context, User user, ImageLoader imageLoader, OnItemClickedListener<Setting> onItemClickedListener) {
return getSettingsAdapter(context, user, imageLoader, true, onItemClickedListener);
}
public static SettingsAdapter getSettingsAdapter(Context context, User user, ImageLoader imageLoader, boolean isEditable, OnItemClickedListener<Setting> onItemClickedListener) {
List<String> headers = new ArrayList<>();
List<List<User.Setting>> settingsSections = new ArrayList<>();
settingsSections.add(user.getSettings());
if(user.getMusicFolderSettings() != null) {
settingsSections.add(user.getMusicFolderSettings());
}
return new SettingsAdapter(context, user, headers, settingsSections, imageLoader, isEditable, onItemClickedListener);
}
}

View File

@ -19,17 +19,11 @@
package net.nullsum.audinaut.audiofx;
import android.content.Context;
import android.media.MediaPlayer;
import android.media.audiofx.AudioEffect;
import android.media.audiofx.LoudnessEnhancer;
import android.os.Build;
import android.util.Log;
public class AudioEffectsController {
private static final String TAG = AudioEffectsController.class.getSimpleName();
private final Context context;
private int audioSessionId = 0;
private final int audioSessionId;
private EqualizerController equalizerController;
@ -39,7 +33,7 @@ public class AudioEffectsController {
}
public void release() {
if(equalizerController != null) {
if (equalizerController != null) {
equalizerController.release();
}
}

View File

@ -18,14 +18,15 @@
*/
package net.nullsum.audinaut.audiofx;
import java.io.Serializable;
import android.content.Context;
import android.media.audiofx.BassBoost;
import android.media.audiofx.Equalizer;
import android.util.Log;
import net.nullsum.audinaut.util.FileUtil;
import java.io.Serializable;
/**
* Backward-compatible wrapper for {@link Equalizer}, which is API Level 9.
*
@ -42,7 +43,7 @@ public class EqualizerController {
private boolean loudnessAvailable = false;
private LoudnessEnhancerController loudnessEnhancerController;
private boolean released = false;
private int audioSessionId = 0;
private final int audioSessionId;
public EqualizerController(Context context, int audioSessionId) {
this.context = context;
@ -54,7 +55,7 @@ public class EqualizerController {
equalizer = new Equalizer(0, audioSessionId);
bass = new BassBoost(0, audioSessionId);
loudnessAvailable = true;
loudnessEnhancerController = new LoudnessEnhancerController(context, audioSessionId);
loudnessEnhancerController = new LoudnessEnhancerController(audioSessionId);
}
public void saveSettings() {
@ -80,14 +81,14 @@ public class EqualizerController {
}
}
public boolean isAvailable() {
private boolean isAvailable() {
return equalizer != null && bass != null;
}
public boolean isEnabled() {
try {
return isAvailable() && equalizer.getEnabled();
} catch(Exception e) {
} catch (Exception e) {
return false;
}
}
@ -97,14 +98,14 @@ public class EqualizerController {
released = true;
equalizer.release();
bass.release();
if(loudnessEnhancerController != null && loudnessEnhancerController.isAvailable()) {
if (loudnessEnhancerController != null && loudnessEnhancerController.isAvailable()) {
loudnessEnhancerController.release();
}
}
}
public Equalizer getEqualizer() {
if(released) {
if (released) {
released = false;
try {
init();
@ -116,8 +117,9 @@ public class EqualizerController {
}
return equalizer;
}
public BassBoost getBassBoost() {
if(released) {
if (released) {
released = false;
try {
init();
@ -128,8 +130,9 @@ public class EqualizerController {
}
return bass;
}
public LoudnessEnhancerController getLoudnessEnhancerController() {
if(loudnessAvailable && released) {
if (loudnessAvailable && released) {
released = false;
try {
init();
@ -144,7 +147,6 @@ public class EqualizerController {
private static class EqualizerSettings implements Serializable {
private short[] bandLevels;
private short preset;
private boolean enabled;
private short bass;
private int loudness;
@ -152,26 +154,22 @@ public class EqualizerController {
public EqualizerSettings() {
}
public EqualizerSettings(Equalizer equalizer, BassBoost boost, LoudnessEnhancerController loudnessEnhancerController) {
enabled = equalizer.getEnabled();
bandLevels = new short[equalizer.getNumberOfBands()];
for (short i = 0; i < equalizer.getNumberOfBands(); i++) {
bandLevels[i] = equalizer.getBandLevel(i);
}
try {
preset = equalizer.getCurrentPreset();
} catch (Exception x) {
preset = -1;
}
try {
bass = boost.getRoundedStrength();
} catch(Exception e) {
} catch (Exception e) {
bass = 0;
}
try {
loudness = (int) loudnessEnhancerController.getGain();
} catch(Exception e) {
} catch (Exception e) {
loudness = 0;
}
}
@ -181,11 +179,11 @@ public class EqualizerController {
equalizer.setBandLevel(i, bandLevels[i]);
}
equalizer.setEnabled(enabled);
if(bass != 0) {
if (bass != 0) {
boost.setEnabled(true);
boost.setStrength(bass);
}
if(loudness != 0) {
if (loudness != 0) {
loudnessController.enable();
loudnessController.setGain(loudness);
}

View File

@ -18,22 +18,16 @@
*/
package net.nullsum.audinaut.audiofx;
import android.content.Context;
import android.media.audiofx.LoudnessEnhancer;
import android.util.Log;
public class LoudnessEnhancerController {
private static final String TAG = LoudnessEnhancerController.class.getSimpleName();
private final Context context;
private LoudnessEnhancer enhancer;
private boolean released = false;
private int audioSessionId = 0;
public LoudnessEnhancerController(Context context, int audioSessionId) {
this.context = context;
public LoudnessEnhancerController(int audioSessionId) {
try {
this.audioSessionId = audioSessionId;
enhancer = new LoudnessEnhancer(audioSessionId);
} catch (Throwable x) {
Log.w(TAG, "Failed to create enhancer", x);
@ -47,7 +41,7 @@ public class LoudnessEnhancerController {
public boolean isEnabled() {
try {
return isAvailable() && enhancer.getEnabled();
} catch(Exception e) {
} catch (Exception e) {
return false;
}
}
@ -55,6 +49,7 @@ public class LoudnessEnhancerController {
public void enable() {
enhancer.setEnabled(true);
}
public void disable() {
enhancer.setEnabled(false);
}
@ -62,6 +57,7 @@ public class LoudnessEnhancerController {
public float getGain() {
return enhancer.getTargetGain();
}
public void setGain(int gain) {
enhancer.setTargetGain(gain);
}
@ -69,7 +65,6 @@ public class LoudnessEnhancerController {
public void release() {
if (isAvailable()) {
enhancer.release();
released = true;
}
}

View File

@ -19,26 +19,25 @@
package net.nullsum.audinaut.domain;
import android.content.Context;
import android.content.SharedPreferences;
import android.media.MediaMetadataRetriever;
import android.os.Build;
import android.util.Log;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.io.File;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.Locale;
import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.UpdateHelper;
import net.nullsum.audinaut.util.Util;
import java.io.File;
import java.io.Serializable;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
/**
* @author Sindre Mehus
*/
@ -51,8 +50,9 @@ public class MusicDirectory implements Serializable {
private List<Entry> children;
public MusicDirectory() {
children = new ArrayList<Entry>();
children = new ArrayList<>();
}
public MusicDirectory(List<Entry> children) {
this.children = children;
}
@ -65,7 +65,7 @@ public class MusicDirectory implements Serializable {
this.name = name;
}
public String getId() {
public String getId() {
return id;
}
@ -82,10 +82,11 @@ public class MusicDirectory implements Serializable {
}
public void addChild(Entry child) {
if(child != null) {
if (child != null) {
children.add(child);
}
}
public void addChildren(List<Entry> children) {
this.children.addAll(children);
}
@ -103,7 +104,7 @@ public class MusicDirectory implements Serializable {
return children;
}
List<Entry> result = new ArrayList<Entry>(children.size());
List<Entry> result = new ArrayList<>(children.size());
for (Entry child : children) {
if (child != null && child.isDirectory() && includeDirs || !child.isDirectory() && includeFiles) {
result.add(child);
@ -111,8 +112,9 @@ public class MusicDirectory implements Serializable {
}
return result;
}
public synchronized List<Entry> getSongs() {
List<Entry> result = new ArrayList<Entry>();
List<Entry> result = new ArrayList<>();
for (Entry child : children) {
if (child != null && !child.isDirectory()) {
result.add(child);
@ -125,24 +127,19 @@ public class MusicDirectory implements Serializable {
return children.size();
}
public void shuffleChildren() {
Collections.shuffle(this.children);
}
public void sortChildren(Context context, int instance) {
public void sortChildren(Context context) {
sortChildren(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_CUSTOM_SORT_ENABLED, true));
}
public void sortChildren(boolean byYear) {
EntryComparator.sort(children, byYear);
}
public synchronized boolean updateMetadata(MusicDirectory refreshedDirectory) {
boolean metadataUpdated = false;
Iterator<Entry> it = children.iterator();
while(it.hasNext()) {
Entry entry = it.next();
for (Entry entry : children) {
int index = refreshedDirectory.children.indexOf(entry);
if(index != -1) {
if (index != -1) {
final Entry refreshed = refreshedDirectory.children.get(index);
entry.setTitle(refreshed.getTitle());
@ -155,7 +152,7 @@ public class MusicDirectory implements Serializable {
entry.setTranscodedSuffix(refreshed.getTranscodedSuffix());
entry.setDiscNumber(refreshed.getDiscNumber());
entry.setType(refreshed.getType());
if(!Util.equals(entry.getCoverArt(), refreshed.getCoverArt())) {
if (!Util.equals(entry.getCoverArt(), refreshed.getCoverArt())) {
metadataUpdated = true;
entry.setCoverArt(refreshed.getCoverArt());
}
@ -173,7 +170,7 @@ public class MusicDirectory implements Serializable {
found.setTranscodedSuffix(refreshed.getTranscodedSuffix());
found.setDiscNumber(refreshed.getDiscNumber());
found.setType(refreshed.getType());
if(!Util.equals(found.getCoverArt(), refreshed.getCoverArt())) {
if (!Util.equals(found.getCoverArt(), refreshed.getCoverArt())) {
found.setCoverArt(refreshed.getCoverArt());
metadataUpdate = DownloadService.METADATA_UPDATED_COVER_ART;
}
@ -184,13 +181,14 @@ public class MusicDirectory implements Serializable {
return metadataUpdated;
}
public synchronized boolean updateEntriesList(Context context, int instance, MusicDirectory refreshedDirectory) {
public synchronized boolean updateEntriesList(Context context, MusicDirectory refreshedDirectory) {
boolean changed = false;
Iterator<Entry> it = children.iterator();
while(it.hasNext()) {
while (it.hasNext()) {
Entry entry = it.next();
// No longer exists in here
if(refreshedDirectory.children.indexOf(entry) == -1) {
if (refreshedDirectory.children.indexOf(entry) == -1) {
it.remove();
changed = true;
}
@ -198,16 +196,16 @@ public class MusicDirectory implements Serializable {
// Make sure we contain all children from refreshed set
boolean resort = false;
for(Entry refreshed: refreshedDirectory.children) {
if(!this.children.contains(refreshed)) {
for (Entry refreshed : refreshedDirectory.children) {
if (!this.children.contains(refreshed)) {
this.children.add(refreshed);
resort = true;
changed = true;
}
}
if(resort) {
this.sortChildren(context, instance);
if (resort) {
this.sortChildren(context);
}
return changed;
@ -218,7 +216,6 @@ public class MusicDirectory implements Serializable {
private String id;
private String parent;
private String grandParent;
private String albumId;
private String artistId;
private boolean directory;
@ -233,26 +230,25 @@ public class MusicDirectory implements Serializable {
private String transcodedContentType;
private String transcodedSuffix;
private String coverArt;
private Long size;
private Integer duration;
private Integer bitRate;
private String path;
private Integer discNumber;
private int type = 0;
private int closeness;
private transient Artist linkedArtist;
public Entry() {
}
public Entry(String id) {
this.id = id;
}
public Entry(Artist artist) {
this.id = artist.getId();
this.title = artist.getName();
this.directory = true;
this.linkedArtist = artist;
}
public void loadMetadata(File file) {
@ -260,16 +256,16 @@ public class MusicDirectory implements Serializable {
MediaMetadataRetriever metadata = new MediaMetadataRetriever();
metadata.setDataSource(file.getAbsolutePath());
String discNumber = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER);
if(discNumber == null) {
if (discNumber == null) {
discNumber = "1/1";
}
int slashIndex = discNumber.indexOf("/");
if(slashIndex > 0) {
if (slashIndex > 0) {
discNumber = discNumber.substring(0, slashIndex);
}
try {
setDiscNumber(Integer.parseInt(discNumber));
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Non numbers in disc field!");
}
String bitrate = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE);
@ -277,22 +273,23 @@ public class MusicDirectory implements Serializable {
String length = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
setDuration(Integer.parseInt(length) / 1000);
String artist = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
if(artist != null) {
if (artist != null) {
setArtist(artist);
}
String album = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM);
if(album != null) {
if (album != null) {
setAlbum(album);
}
metadata.release();
} catch(Exception e) {
} catch (Exception e) {
Log.i(TAG, "Device doesn't properly support MediaMetadataRetreiver", e);
}
}
public void rebaseTitleOffPath() {
try {
String filename = getPath();
if(filename == null) {
if (filename == null) {
return;
}
@ -304,13 +301,13 @@ public class MusicDirectory implements Serializable {
}
index = filename.lastIndexOf('.');
if(index != -1) {
if (index != -1) {
filename = filename.substring(0, index);
}
setTitle(filename);
}
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to update title based off of path", e);
}
}
@ -331,14 +328,6 @@ public class MusicDirectory implements Serializable {
this.parent = parent;
}
public String getGrandParent() {
return grandParent;
}
public void setGrandParent(String grandParent) {
this.grandParent = grandParent;
}
public String getAlbumId() {
return albumId;
}
@ -379,18 +368,18 @@ public class MusicDirectory implements Serializable {
return getParent() != null || getArtist() != null;
}
public void setAlbum(String album) {
this.album = album;
}
public String getAlbumDisplay() {
if(album != null && title.startsWith("Disc ")) {
if (album != null && title.startsWith("Disc ")) {
return album;
} else {
return title;
}
}
public void setAlbum(String album) {
this.album = album;
}
public String getArtist() {
return artist;
}
@ -455,14 +444,6 @@ public class MusicDirectory implements Serializable {
this.transcodedSuffix = transcodedSuffix;
}
public Long getSize() {
return size;
}
public void setSize(Long size) {
this.size = size;
}
public Integer getDuration() {
return duration;
}
@ -506,9 +487,11 @@ public class MusicDirectory implements Serializable {
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public boolean isSong() {
return type == TYPE_SONG;
}
@ -521,18 +504,6 @@ public class MusicDirectory implements Serializable {
this.closeness = closeness;
}
public boolean isOnlineId(Context context) {
try {
String cacheLocation = Util.getPreferences(context).getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
return cacheLocation == null || id == null || id.indexOf(cacheLocation) == -1;
} catch(Exception e) {
Log.w(TAG, "Failed to check online id validity");
// Err on the side of default functionality
return true;
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -558,8 +529,8 @@ public class MusicDirectory implements Serializable {
}
public static class EntryComparator implements Comparator<Entry> {
private boolean byYear;
private Collator collator;
private final boolean byYear;
private final Collator collator;
public EntryComparator(boolean byYear) {
this.byYear = byYear;
@ -567,20 +538,28 @@ public class MusicDirectory implements Serializable {
this.collator.setStrength(Collator.PRIMARY);
}
public static void sort(List<Entry> entries, boolean byYear) {
try {
Collections.sort(entries, new EntryComparator(byYear));
} catch (Exception e) {
Log.w(TAG, "Failed to sort MusicDirectory");
}
}
public int compare(Entry lhs, Entry rhs) {
if(lhs.isDirectory() && !rhs.isDirectory()) {
if (lhs.isDirectory() && !rhs.isDirectory()) {
return -1;
} else if(!lhs.isDirectory() && rhs.isDirectory()) {
} else if (!lhs.isDirectory() && rhs.isDirectory()) {
return 1;
} else if(lhs.isDirectory() && rhs.isDirectory()) {
if(byYear) {
} else if (lhs.isDirectory() && rhs.isDirectory()) {
if (byYear) {
Integer lhsYear = lhs.getYear();
Integer rhsYear = rhs.getYear();
if(lhsYear != null && rhsYear != null) {
if (lhsYear != null && rhsYear != null) {
return lhsYear.compareTo(rhsYear);
} else if(lhsYear != null) {
} else if (lhsYear != null) {
return -1;
} else if(rhsYear != null) {
} else if (rhsYear != null) {
return 1;
}
}
@ -591,36 +570,25 @@ public class MusicDirectory implements Serializable {
Integer lhsDisc = lhs.getDiscNumber();
Integer rhsDisc = rhs.getDiscNumber();
if(lhsDisc != null && rhsDisc != null) {
if(lhsDisc < rhsDisc) {
if (lhsDisc != null && rhsDisc != null) {
if (lhsDisc < rhsDisc) {
return -1;
} else if(lhsDisc > rhsDisc) {
} else if (lhsDisc > rhsDisc) {
return 1;
}
}
Integer lhsTrack = lhs.getTrack();
Integer rhsTrack = rhs.getTrack();
if(lhsTrack != null && rhsTrack != null && lhsTrack != rhsTrack) {
if (lhsTrack != null && rhsTrack != null && !Objects.equals(lhsTrack, rhsTrack)) {
return lhsTrack.compareTo(rhsTrack);
} else if(lhsTrack != null) {
} else if (lhsTrack != null) {
return -1;
} else if(rhsTrack != null) {
} else if (rhsTrack != null) {
return 1;
}
return collator.compare(lhs.getTitle(), rhs.getTitle());
}
public static void sort(List<Entry> entries) {
sort(entries, true);
}
public static void sort(List<Entry> entries, boolean byYear) {
try {
Collections.sort(entries, new EntryComparator(byYear));
} catch (Exception e) {
Log.w(TAG, "Failed to sort MusicDirectory");
}
}
}
}

View File

@ -35,16 +35,24 @@ public class MusicFolder implements Serializable {
private static final String TAG = MusicFolder.class.getSimpleName();
private String id;
private String name;
private boolean enabled;
public MusicFolder() {
}
public MusicFolder(String id, String name) {
this.id = id;
this.name = name;
}
public static void sort(List<MusicFolder> musicFolders) {
try {
Collections.sort(musicFolders, new MusicFolderComparator());
} catch (Exception e) {
Log.w(TAG, "Failed to sort music folders", e);
}
}
public String getId() {
return id;
}
@ -53,28 +61,13 @@ public class MusicFolder implements Serializable {
return name;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean getEnabled() {
return enabled;
}
public static class MusicFolderComparator implements Comparator<MusicFolder> {
public int compare(MusicFolder lhsMusicFolder, MusicFolder rhsMusicFolder) {
if(lhsMusicFolder == rhsMusicFolder || lhsMusicFolder.getName().equals(rhsMusicFolder.getName())) {
if (lhsMusicFolder == rhsMusicFolder || lhsMusicFolder.getName().equals(rhsMusicFolder.getName())) {
return 0;
} else {
return lhsMusicFolder.getName().compareToIgnoreCase(rhsMusicFolder.getName());
}
}
}
public static void sort(List<MusicFolder> musicFolders) {
try {
Collections.sort(musicFolders, new MusicFolderComparator());
} catch (Exception e) {
Log.w(TAG, "Failed to sort music folders", e);
}
}
}

View File

@ -17,14 +17,12 @@ package net.nullsum.audinaut.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class PlayerQueue implements Serializable {
public List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>();
public List<MusicDirectory.Entry> toDelete = new ArrayList<MusicDirectory.Entry>();
public final List<MusicDirectory.Entry> songs = new ArrayList<>();
public final List<MusicDirectory.Entry> toDelete = new ArrayList<>();
public int currentPlayingIndex;
public int currentPlayingPosition;
public boolean renameCurrent = false;
public Date changed = null;
}

View File

@ -45,10 +45,12 @@ public class Playlist implements Serializable {
public Playlist() {
}
public Playlist(String id, String name) {
this.id = id;
this.name = name;
}
public Playlist(String id, String name, String owner, String comment, String songCount, String pub, String created, String changed, Integer duration) {
this.id = id;
this.name = name;
@ -65,10 +67,6 @@ public class Playlist implements Serializable {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
@ -81,10 +79,6 @@ public class Playlist implements Serializable {
return this.owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getComment() {
return this.comment;
}
@ -104,6 +98,7 @@ public class Playlist implements Serializable {
public Boolean getPublic() {
return this.pub;
}
public void setPublic(Boolean pub) {
this.pub = pub;
}
@ -112,7 +107,7 @@ public class Playlist implements Serializable {
return created;
}
public void setCreated(String created) {
private void setCreated(String created) {
if (created != null) {
try {
this.created = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(created);
@ -123,14 +118,12 @@ public class Playlist implements Serializable {
this.created = null;
}
}
public void setCreated(Date created) {
this.created = created;
}
public Date getChanged() {
return changed;
}
public void setChanged(String changed) {
private void setChanged(String changed) {
if (changed != null) {
try {
this.changed = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(changed);
@ -141,16 +134,10 @@ public class Playlist implements Serializable {
this.changed = null;
}
}
public void setChanged(Date changed) {
this.changed = changed;
}
public Integer getDuration() {
return duration;
}
public void setDuration(Integer duration) {
this.duration = duration;
}
@Override
public String toString() {
@ -159,13 +146,13 @@ public class Playlist implements Serializable {
@Override
public boolean equals(Object o) {
if(o == this) {
if (o == this) {
return true;
} else if(o == null) {
} else if (o == null) {
return false;
} else if(o instanceof String) {
} else if (o instanceof String) {
return o.equals(this.id);
} else if(o.getClass() != getClass()) {
} else if (o.getClass() != getClass()) {
return false;
}
@ -174,14 +161,14 @@ public class Playlist implements Serializable {
}
public static class PlaylistComparator implements Comparator<Playlist> {
@Override
public int compare(Playlist playlist1, Playlist playlist2) {
return playlist1.getName().compareToIgnoreCase(playlist2.getName());
}
public static List<Playlist> sort(List<Playlist> playlists) {
Collections.sort(playlists, new PlaylistComparator());
return playlists;
}
@Override
public int compare(Playlist playlist1, Playlist playlist2) {
return playlist1.getName().compareToIgnoreCase(playlist2.getName());
}
}
}

View File

@ -68,24 +68,24 @@ public class SearchCritera {
// * Replace spaces and wildcard '*' characters with ".*"
// * All other characters are properly quoted
if (this.pattern == null) {
String regex = ".*";
String currentPart = "";
StringBuilder regex = new StringBuilder(".*");
StringBuilder currentPart = new StringBuilder();
for (int i = 0; i < query.length(); i++) {
char c = query.charAt(i);
if (c == '*' || c == ' ') {
regex += Pattern.quote(currentPart);
regex += ".*";
currentPart = "";
regex.append(Pattern.quote(currentPart.toString()));
regex.append(".*");
currentPart = new StringBuilder();
} else {
currentPart += c;
currentPart.append(c);
}
}
if (currentPart.length() > 0) {
regex += Pattern.quote(currentPart);
regex.append(Pattern.quote(currentPart.toString()));
}
regex += ".*";
this.pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
regex.append(".*");
this.pattern = Pattern.compile(regex.toString(), Pattern.CASE_INSENSITIVE);
}
return this.pattern;

View File

@ -50,13 +50,4 @@ public class SearchResult implements Serializable {
return songs;
}
public boolean hasArtists() {
return !artists.isEmpty();
}
public boolean hasAlbums() {
return !albums.isEmpty();
}
public boolean hasSongs() {
return !songs.isEmpty();
}
}

View File

@ -15,21 +15,19 @@
package net.nullsum.audinaut.domain;
import android.util.Pair;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class User implements Serializable {
public static final String ADMIN = "adminRole";
public static final String SETTINGS = "settingsRole";
public static final String DOWNLOAD = "downloadRole";
public static final String UPLOAD = "uploadRole";
public static final String COVERART = "coverArtRole";
public static final String COMMENT = "commentRole";
public static final String STREAM = "streamRole";
public static final List<String> ROLES = new ArrayList<>();
private static final String ADMIN = "adminRole";
private static final String SETTINGS = "settingsRole";
private static final String DOWNLOAD = "downloadRole";
private static final String UPLOAD = "uploadRole";
private static final String COVERART = "coverArtRole";
private static final String COMMENT = "commentRole";
private static final String STREAM = "streamRole";
static {
ROLES.add(ADMIN);
@ -41,77 +39,28 @@ public class User implements Serializable {
ROLES.add(COMMENT);
}
private String username;
private String password;
private String email;
private List<Setting> settings = new ArrayList<Setting>();
private List<Setting> musicFolders;
public User() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<Setting> getSettings() {
return settings;
}
public void setSettings(List<Setting> settings) {
this.settings.clear();
this.settings.addAll(settings);
}
public void addSetting(String name, Boolean value) {
settings.add(new Setting(name, value));
}
public void addMusicFolder(MusicFolder musicFolder) {
if(musicFolders == null) {
if (musicFolders == null) {
musicFolders = new ArrayList<>();
}
musicFolders.add(new MusicFolderSetting(musicFolder.getId(), musicFolder.getName(), false));
musicFolders.add(new MusicFolderSetting(musicFolder.getId(), musicFolder.getName()));
}
public void addMusicFolder(MusicFolderSetting musicFolderSetting, boolean defaultValue) {
if(musicFolders == null) {
musicFolders = new ArrayList<>();
}
musicFolders.add(new MusicFolderSetting(musicFolderSetting.getName(), musicFolderSetting.getLabel(), defaultValue));
}
public List<Setting> getMusicFolderSettings() {
return musicFolders;
}
public static class Setting implements Serializable {
private String name;
private final String name;
private Boolean value;
public Setting() {
}
public Setting(String name, Boolean value) {
this.name = name;
this.value = value;
@ -120,22 +69,21 @@ public class User implements Serializable {
public String getName() {
return name;
}
public Boolean getValue() {
return value;
}
public void setValue(Boolean value) {
this.value = value;
}
}
public static class MusicFolderSetting extends Setting {
private String label;
private final String label;
public MusicFolderSetting() {
}
public MusicFolderSetting(String name, String label, Boolean value) {
super(name, value);
public MusicFolderSetting(String name, String label) {
super(name, false);
this.label = label;
}

View File

@ -15,10 +15,8 @@
package net.nullsum.audinaut.fragments;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -26,13 +24,10 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import androidx.recyclerview.widget.ItemTouchHelper;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.adapter.DownloadFileAdapter;
import net.nullsum.audinaut.adapter.SectionAdapter;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.service.DownloadFile;
@ -42,9 +37,14 @@ import net.nullsum.audinaut.util.DownloadFileItemHelperCallback;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.adapter.DownloadFileAdapter;
import net.nullsum.audinaut.view.UpdateView;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class DownloadFragment extends SelectRecyclerFragment<DownloadFile> implements SectionAdapter.OnItemClickedListener<DownloadFile> {
private long currentRevision;
private ScheduledExecutorService executorService;
@ -69,17 +69,7 @@ public class DownloadFragment extends SelectRecyclerFragment<DownloadFile> imple
super.onResume();
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
update();
}
});
}
};
Runnable runnable = () -> handler.post(this::update);
executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS);
@ -102,14 +92,13 @@ public class DownloadFragment extends SelectRecyclerFragment<DownloadFile> imple
}
@Override
public List<DownloadFile> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
public List<DownloadFile> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) {
DownloadService downloadService = getDownloadService();
if(downloadService == null) {
return new ArrayList<DownloadFile>();
if (downloadService == null) {
return new ArrayList<>();
}
List<DownloadFile> songList = new ArrayList<DownloadFile>();
songList.addAll(downloadService.getBackgroundDownloads());
List<DownloadFile> songList = new ArrayList<>(downloadService.getBackgroundDownloads());
currentRevision = downloadService.getDownloadListUpdateRevision();
return songList;
}
@ -128,7 +117,7 @@ public class DownloadFragment extends SelectRecyclerFragment<DownloadFile> imple
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<DownloadFile> updateView, DownloadFile downloadFile) {
MusicDirectory.Entry selectedItem = downloadFile.getSong();
onCreateContextMenuSupport(menu, menuInflater, updateView, selectedItem);
if(!Util.isOffline(context)) {
if (!Util.isOffline(context)) {
menu.removeItem(R.id.song_menu_remove_playlist);
}
@ -143,29 +132,24 @@ public class DownloadFragment extends SelectRecyclerFragment<DownloadFile> imple
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
if(super.onOptionsItemSelected(menuItem)) {
if (super.onOptionsItemSelected(menuItem)) {
return true;
}
switch (menuItem.getItemId()) {
case R.id.menu_remove_all:
Util.confirmDialog(context, R.string.download_menu_remove_all, "", new DialogInterface.OnClickListener() {
Util.confirmDialog(context, R.string.download_menu_remove_all, "", (dialog, which) -> new SilentBackgroundTask<Void>(context) {
@Override
public void onClick(DialogInterface dialog, int which) {
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
getDownloadService().clearBackground();
return null;
}
@Override
protected void done(Void result) {
update();
}
}.execute();
protected Void doInBackground() {
getDownloadService().clearBackground();
return null;
}
});
@Override
protected void done(Void result) {
update();
}
}.execute());
return true;
}

View File

@ -29,14 +29,10 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import java.util.HashMap;
import java.util.Map;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.audiofx.EqualizerController;
import net.nullsum.audinaut.audiofx.LoudnessEnhancerController;
@ -44,6 +40,9 @@ import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.Util;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Scott on 10/27/13.
*/
@ -52,7 +51,7 @@ public class EqualizerFragment extends SubsonicFragment {
private static final int MENU_GROUP_PRESET = 100;
private final Map<Short, SeekBar> bars = new HashMap<Short, SeekBar>();
private final Map<Short, SeekBar> bars = new HashMap<>();
private SeekBar bassBar;
private SeekBar loudnessBar;
private EqualizerController equalizerController;
@ -73,7 +72,7 @@ public class EqualizerFragment extends SubsonicFragment {
loudnessEnhancer = equalizerController.getLoudnessEnhancerController();
initEqualizer();
} catch(Exception e) {
} catch (Exception e) {
Log.e(TAG, "Failed to initialize EQ", e);
Util.toast(context, "Failed to initialize EQ");
context.onBackPressed();
@ -81,25 +80,17 @@ public class EqualizerFragment extends SubsonicFragment {
final View presetButton = rootView.findViewById(R.id.equalizer_preset);
registerForContextMenu(presetButton);
presetButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
presetButton.showContextMenu();
}
});
presetButton.setOnClickListener(view -> presetButton.showContextMenu());
CheckBox enabledCheckBox = (CheckBox) rootView.findViewById(R.id.equalizer_enabled);
CheckBox enabledCheckBox = rootView.findViewById(R.id.equalizer_enabled);
enabledCheckBox.setChecked(equalizer.getEnabled());
enabledCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
try {
setEqualizerEnabled(b);
} catch(Exception e) {
Log.e(TAG, "Failed to set EQ enabled", e);
Util.toast(context, "Failed to set EQ enabled");
context.onBackPressed();
}
enabledCheckBox.setOnCheckedChangeListener((compoundButton, b) -> {
try {
setEqualizerEnabled(b);
} catch (Exception e) {
Log.e(TAG, "Failed to set EQ enabled", e);
Util.toast(context, "Failed to set EQ enabled");
context.onBackPressed();
}
});
@ -119,7 +110,7 @@ public class EqualizerFragment extends SubsonicFragment {
if (!equalizer.getEnabled()) {
equalizerController.release();
}
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to release controller", e);
}
}
@ -135,7 +126,7 @@ public class EqualizerFragment extends SubsonicFragment {
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, view, menuInfo);
if(!primaryFragment) {
if (!primaryFragment) {
return;
}
@ -158,7 +149,7 @@ public class EqualizerFragment extends SubsonicFragment {
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
short preset = (short) menuItem.getItemId();
for(int i = 0; i < 10; i++) {
for (int i = 0; i < 10; i++) {
try {
equalizer.usePreset(preset);
i = 10;
@ -178,7 +169,7 @@ public class EqualizerFragment extends SubsonicFragment {
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(Constants.PREFERENCES_EQUALIZER_ON, enabled);
editor.apply();
for(int i = 0; i < 10; i++) {
for (int i = 0; i < 10; i++) {
try {
equalizer.setEnabled(enabled);
updateBars(true);
@ -245,36 +236,36 @@ public class EqualizerFragment extends SubsonicFragment {
editor.putInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, masterLevel);
editor.apply();
}
} catch(Exception e) {
} catch (Exception e) {
Log.e(TAG, "Failed to update bars");
}
}
private void initEqualizer() {
LinearLayout layout = (LinearLayout) rootView.findViewById(R.id.equalizer_layout);
LinearLayout layout = rootView.findViewById(R.id.equalizer_layout);
final short minEQLevel = equalizer.getBandLevelRange()[0];
final short maxEQLevel = equalizer.getBandLevelRange()[1];
// Setup Pregain
SharedPreferences prefs = Util.getPreferences(context);
masterLevel = (short)prefs.getInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, 0);
masterLevel = (short) prefs.getInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, 0);
initPregain(layout, minEQLevel, maxEQLevel);
for (short i = 0; i < equalizer.getNumberOfBands(); i++) {
final short band = i;
View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
TextView freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
final TextView levelTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
SeekBar bar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
TextView freqTextView = bandBar.findViewById(R.id.equalizer_frequency);
final TextView levelTextView = bandBar.findViewById(R.id.equalizer_level);
SeekBar bar = bandBar.findViewById(R.id.equalizer_bar);
freqTextView.setText((equalizer.getCenterFreq(band) / 1000) + " Hz");
bars.put(band, bar);
bar.setMax(maxEQLevel - minEQLevel);
short level = equalizer.getBandLevel(band);
if(equalizer.getEnabled()) {
if (equalizer.getEnabled()) {
level = (short) (level - masterLevel);
}
bar.setProgress(level - minEQLevel);
@ -290,7 +281,7 @@ public class EqualizerFragment extends SubsonicFragment {
equalizer.setBandLevel(band, (short) (level + masterLevel));
}
updateLevelText(levelTextView, level);
} catch(Exception e) {
} catch (Exception e) {
Log.e(TAG, "Failed to change equalizer", e);
}
}
@ -306,18 +297,18 @@ public class EqualizerFragment extends SubsonicFragment {
layout.addView(bandBar);
}
LinearLayout specialLayout = (LinearLayout) rootView.findViewById(R.id.special_effects_layout);
LinearLayout specialLayout = rootView.findViewById(R.id.special_effects_layout);
// Setup bass booster
View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
TextView freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
final TextView bassTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
bassBar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
TextView freqTextView = bandBar.findViewById(R.id.equalizer_frequency);
final TextView bassTextView = bandBar.findViewById(R.id.equalizer_level);
bassBar = bandBar.findViewById(R.id.equalizer_bar);
freqTextView.setText(R.string.equalizer_bass_booster);
bassBar.setEnabled(equalizer.getEnabled());
short bassLevel = 0;
if(bass.getEnabled()) {
if (bass.getEnabled()) {
bassLevel = bass.getRoundedStrength();
}
bassTextView.setText(context.getResources().getString(R.string.equalizer_bass_size, bassLevel));
@ -339,7 +330,7 @@ public class EqualizerFragment extends SubsonicFragment {
bass.setEnabled(false);
}
}
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Error on changing bass: ", e);
}
}
@ -356,17 +347,17 @@ public class EqualizerFragment extends SubsonicFragment {
});
specialLayout.addView(bandBar);
if(loudnessEnhancer != null && loudnessEnhancer.isAvailable()) {
if (loudnessEnhancer != null && loudnessEnhancer.isAvailable()) {
// Setup loudness enhancer
bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
final TextView loudnessTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
loudnessBar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
freqTextView = bandBar.findViewById(R.id.equalizer_frequency);
final TextView loudnessTextView = bandBar.findViewById(R.id.equalizer_level);
loudnessBar = bandBar.findViewById(R.id.equalizer_bar);
freqTextView.setText(R.string.equalizer_voice_booster);
loudnessBar.setEnabled(equalizer.getEnabled());
int loudnessLevel = 0;
if(loudnessEnhancer.isEnabled()) {
if (loudnessEnhancer.isEnabled()) {
loudnessLevel = (int) loudnessEnhancer.getGain();
}
loudnessBar.setProgress(loudnessLevel / 100);
@ -377,18 +368,18 @@ public class EqualizerFragment extends SubsonicFragment {
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
try {
loudnessTextView.setText(context.getResources().getString(R.string.equalizer_db_size, progress));
if(fromUser) {
if(progress > 0) {
if(!loudnessEnhancer.isEnabled()) {
if (fromUser) {
if (progress > 0) {
if (!loudnessEnhancer.isEnabled()) {
loudnessEnhancer.enable();
}
loudnessEnhancer.setGain(progress * 100);
} else if(progress == 0 && loudnessEnhancer.isEnabled()) {
} else if (progress == 0 && loudnessEnhancer.isEnabled()) {
loudnessEnhancer.setGain(progress * 100);
loudnessEnhancer.disable();
}
}
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Error on changing loudness: ", e);
}
}
@ -409,13 +400,13 @@ public class EqualizerFragment extends SubsonicFragment {
private void initPregain(LinearLayout layout, final short minEQLevel, final short maxEQLevel) {
View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
TextView freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
final TextView levelTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
SeekBar bar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
TextView freqTextView = bandBar.findViewById(R.id.equalizer_frequency);
final TextView levelTextView = bandBar.findViewById(R.id.equalizer_level);
SeekBar bar = bandBar.findViewById(R.id.equalizer_bar);
freqTextView.setText("Master");
bars.put((short)-1, bar);
bars.put((short) -1, bar);
bar.setMax(maxEQLevel - minEQLevel);
bar.setProgress(masterLevel - minEQLevel);
bar.setEnabled(equalizer.getEnabled());
@ -437,7 +428,7 @@ public class EqualizerFragment extends SubsonicFragment {
}
}
updateLevelText(levelTextView, masterLevel);
} catch(Exception e) {
} catch (Exception e) {
Log.e(TAG, "Failed to change equalizer", e);
}
}

View File

@ -1,15 +1,7 @@
package net.nullsum.audinaut.fragments;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.StatFs;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@ -17,32 +9,17 @@ import android.view.MenuItem;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.adapter.MainAdapter;
import net.nullsum.audinaut.adapter.SectionAdapter;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.EnvironmentVariables;
import net.nullsum.audinaut.util.FileUtil;
import net.nullsum.audinaut.util.LoadingTask;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.UserUtil;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.service.MusicService;
import net.nullsum.audinaut.service.MusicServiceFactory;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.view.UpdateView;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class MainFragment extends SelectRecyclerFragment<Integer> {
private static final String TAG = MainFragment.class.getSimpleName();
public static final String SONGS_LIST_PREFIX = "songs-";
public static final String SONGS_NEWEST = SONGS_LIST_PREFIX + "newest";
public static final String SONGS_TOP_PLAYED = SONGS_LIST_PREFIX + "topPlayed";
@ -63,15 +40,6 @@ public class MainFragment extends SelectRecyclerFragment<Integer> {
onFinishSetupOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(super.onOptionsItemSelected(item)) {
return true;
}
return false;
}
@Override
public int getOptionsMenu() {
return 0;
@ -97,8 +65,8 @@ public class MainFragment extends SelectRecyclerFragment<Integer> {
}
@Override
public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
return Arrays.asList(0);
public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) {
return Collections.singletonList(0);
}
@Override
@ -107,111 +75,77 @@ public class MainFragment extends SelectRecyclerFragment<Integer> {
}
private void showAlbumList(String type) {
if("genres".equals(type)) {
SubsonicFragment fragment = new SelectGenreFragment();
replaceFragment(fragment);
} else if("years".equals(type)) {
SubsonicFragment fragment = new SelectYearFragment();
replaceFragment(fragment);
} else {
// Clear out recently added count when viewing
if("newest".equals(type)) {
SharedPreferences.Editor editor = Util.getPreferences(context).edit();
editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
editor.apply();
switch (type) {
case "genres": {
SubsonicFragment fragment = new SelectGenreFragment();
replaceFragment(fragment);
break;
}
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
fragment.setArguments(args);
replaceFragment(fragment);
}
}
private void showAboutDialog() {
new LoadingTask<Void>(context) {
Long[] used;
long bytesTotalFs;
long bytesAvailableFs;
@Override
protected Void doInBackground() throws Throwable {
File rootFolder = FileUtil.getMusicDirectory(context);
StatFs stat = new StatFs(rootFolder.getPath());
bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize();
bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
used = FileUtil.getUsedSize(context, rootFolder);
return null;
case "years": {
SubsonicFragment fragment = new SelectYearFragment();
replaceFragment(fragment);
break;
}
@Override
protected void done(Void result) {
List<Integer> headers = new ArrayList<>();
List<String> details = new ArrayList<>();
headers.add(R.string.details_author);
details.add("Andrew Rabert");
headers.add(R.string.details_email);
details.add("ar@nullsum.net");
try {
headers.add(R.string.details_version);
details.add(context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName);
} catch(Exception e) {
details.add("");
default: {
// Clear out recently added count when viewing
if ("newest".equals(type)) {
SharedPreferences.Editor editor = Util.getPreferences(context).edit();
editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
editor.apply();
}
Resources res = context.getResources();
headers.add(R.string.details_files_cached);
details.add(Long.toString(used[0]));
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
fragment.setArguments(args);
headers.add(R.string.details_files_permanent);
details.add(Long.toString(used[1]));
headers.add(R.string.details_used_space);
details.add(res.getString(R.string.details_of, Util.formatLocalizedBytes(used[2], context), Util.formatLocalizedBytes(Util.getCacheSizeMB(context) * 1024L * 1024L, context)));
headers.add(R.string.details_available_space);
details.add(res.getString(R.string.details_of, Util.formatLocalizedBytes(bytesAvailableFs, context), Util.formatLocalizedBytes(bytesTotalFs, context)));
Util.showDetailsDialog(context, R.string.main_about_title, headers, details);
replaceFragment(fragment);
break;
}
}.execute();
}
}
@Override
public void onItemClicked(UpdateView<Integer> updateView, Integer item) {
if (item == R.string.main_albums_random) {
showAlbumList("random");
} else if (item == R.string.main_albums_recent) {
showAlbumList("recent");
} else if (item == R.string.main_albums_frequent) {
showAlbumList("frequent");
} else if(item == R.string.main_albums_genres) {
showAlbumList("genres");
} else if(item == R.string.main_albums_year) {
showAlbumList("years");
} else if(item == R.string.main_albums_alphabetical) {
showAlbumList("alphabeticalByName");
} else if (item == R.string.main_songs_newest) {
showAlbumList(SONGS_NEWEST);
} else if (item == R.string.main_songs_top_played) {
showAlbumList(SONGS_TOP_PLAYED);
} else if (item == R.string.main_songs_recent) {
showAlbumList(SONGS_RECENT);
} else if (item == R.string.main_songs_frequent) {
showAlbumList(SONGS_FREQUENT);
switch (item) {
case R.string.main_albums_random:
showAlbumList("random");
break;
case R.string.main_albums_recent:
showAlbumList("recent");
break;
case R.string.main_albums_frequent:
showAlbumList("frequent");
break;
case R.string.main_albums_genres:
showAlbumList("genres");
break;
case R.string.main_albums_year:
showAlbumList("years");
break;
case R.string.main_albums_alphabetical:
showAlbumList("alphabeticalByName");
break;
case R.string.main_songs_newest:
showAlbumList(SONGS_NEWEST);
break;
case R.string.main_songs_top_played:
showAlbumList(SONGS_TOP_PLAYED);
break;
case R.string.main_songs_recent:
showAlbumList(SONGS_RECENT);
break;
case R.string.main_songs_frequent:
showAlbumList(SONGS_FREQUENT);
break;
}
}
@Override
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Integer> updateView, Integer item) {}
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Integer> updateView, Integer item) {
}
@Override
public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Integer> updateView, Integer item) {

View File

@ -26,32 +26,33 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.util.Constants;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public abstract class PreferenceCompatFragment extends SubsonicFragment {
private static final String TAG = PreferenceCompatFragment.class.getSimpleName();
private static final int FIRST_REQUEST_CODE = 100;
private static final int MSG_BIND_PREFERENCES = 1;
private static final String PREFERENCES_TAG = "android:preferences";
private boolean mHavePrefs;
private boolean mInitDone;
private ListView mList;
final private Runnable mRequestFocus = new Runnable() {
public void run() {
mList.focusableViewAvailable(mList);
}
};
private PreferenceManager mPreferenceManager;
private Handler mHandler = new Handler() {
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@ -63,12 +64,6 @@ public abstract class PreferenceCompatFragment extends SubsonicFragment {
}
};
final private Runnable mRequestFocus = new Runnable() {
public void run() {
mList.focusableViewAvailable(mList);
}
};
private void bindPreferences() {
PreferenceScreen localPreferenceScreen = getPreferenceScreen();
if (localPreferenceScreen != null) {
@ -89,10 +84,7 @@ public abstract class PreferenceCompatFragment extends SubsonicFragment {
throw new RuntimeException("Content has view with id attribute 'android.R.id.list' that is not a ListView class");
}
mList = (ListView)listView;
if (mList == null) {
throw new RuntimeException("Your content must have a ListView whose id attribute is 'android.R.id.list'");
}
mList = (ListView) listView;
mHandler.post(mRequestFocus);
}
@ -110,26 +102,17 @@ public abstract class PreferenceCompatFragment extends SubsonicFragment {
}
}
public void addPreferencesFromIntent(Intent intent) {
requirePreferenceManager();
PreferenceScreen screen = inflateFromIntent(intent, getPreferenceScreen());
setPreferenceScreen(screen);
}
public PreferenceScreen addPreferencesFromResource(int resId) {
private PreferenceScreen addPreferencesFromResource(int resId) {
requirePreferenceManager();
PreferenceScreen screen = inflateFromResource(getActivity(), resId, getPreferenceScreen());
setPreferenceScreen(screen);
for(int i = 0; i < screen.getPreferenceCount(); i++) {
for (int i = 0; i < screen.getPreferenceCount(); i++) {
Preference preference = screen.getPreference(i);
if(preference instanceof PreferenceScreen && preference.getKey() != null) {
preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
onStartNewFragment(preference.getKey());
return false;
}
if (preference instanceof PreferenceScreen && preference.getKey() != null) {
preference.setOnPreferenceClickListener(preference1 -> {
onStartNewFragment(preference1.getKey());
return false;
});
}
}
@ -137,19 +120,19 @@ public abstract class PreferenceCompatFragment extends SubsonicFragment {
return screen;
}
public Preference findPreference(CharSequence key) {
Preference findPreference(CharSequence key) {
if (mPreferenceManager == null) {
return null;
}
return mPreferenceManager.findPreference(key);
}
public ListView getListView() {
private ListView getListView() {
ensureList();
return mList;
}
public PreferenceManager getPreferenceManager() {
PreferenceManager getPreferenceManager() {
return mPreferenceManager;
}
@ -184,7 +167,7 @@ public abstract class PreferenceCompatFragment extends SubsonicFragment {
mPreferenceManager = createPreferenceManager();
int res = this.getArguments().getInt(Constants.INTENT_EXTRA_FRAGMENT_TYPE, 0);
if(res != 0) {
if (res != 0) {
PreferenceScreen preferenceScreen = addPreferencesFromResource(res);
onInitPreferences(preferenceScreen);
}
@ -226,7 +209,9 @@ public abstract class PreferenceCompatFragment extends SubsonicFragment {
dispatchActivityStop();
}
/** Access methods with visibility private **/
/**
* Access methods with visibility private
**/
private PreferenceManager createPreferenceManager() {
try {
@ -248,7 +233,7 @@ public abstract class PreferenceCompatFragment extends SubsonicFragment {
}
}
protected void setPreferenceScreen(PreferenceScreen preferenceScreen) {
void setPreferenceScreen(PreferenceScreen preferenceScreen) {
try {
Method m = PreferenceManager.class.getDeclaredMethod("setPreferences", PreferenceScreen.class);
m.setAccessible(true);
@ -294,19 +279,8 @@ public abstract class PreferenceCompatFragment extends SubsonicFragment {
}
}
private void setFragment(PreferenceFragment preferenceFragment) {
try {
Method m = PreferenceManager.class.getDeclaredMethod("setFragment", PreferenceFragment.class);
m.setAccessible(true);
m.invoke(mPreferenceManager, preferenceFragment);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public PreferenceScreen inflateFromResource(Context context, int resId, PreferenceScreen rootPreferences) {
PreferenceScreen preferenceScreen ;
private PreferenceScreen inflateFromResource(Context context, int resId, PreferenceScreen rootPreferences) {
PreferenceScreen preferenceScreen;
try {
Method m = PreferenceManager.class.getDeclaredMethod("inflateFromResource", Context.class, int.class, PreferenceScreen.class);
m.setAccessible(true);
@ -317,18 +291,7 @@ public abstract class PreferenceCompatFragment extends SubsonicFragment {
return preferenceScreen;
}
public PreferenceScreen inflateFromIntent(Intent queryIntent, PreferenceScreen rootPreferences) {
PreferenceScreen preferenceScreen ;
try {
Method m = PreferenceManager.class.getDeclaredMethod("inflateFromIntent", Intent.class, PreferenceScreen.class);
m.setAccessible(true);
preferenceScreen = (PreferenceScreen) m.invoke(mPreferenceManager, queryIntent, rootPreferences);
} catch (Exception e) {
throw new RuntimeException(e);
}
return preferenceScreen;
}
protected abstract void onInitPreferences(PreferenceScreen preferenceScreen);
protected abstract void onStartNewFragment(String name);
}

View File

@ -1,24 +1,17 @@
package net.nullsum.audinaut.fragments;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.MenuItem;
import android.net.Uri;
import android.view.View;
import android.view.ViewGroup;
import androidx.core.view.MenuItemCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.adapter.ArtistAdapter;
import net.nullsum.audinaut.adapter.EntryGridAdapter;
@ -28,26 +21,30 @@ import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.SearchCritera;
import net.nullsum.audinaut.domain.SearchResult;
import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.service.MusicService;
import net.nullsum.audinaut.service.MusicServiceFactory;
import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.util.BackgroundTask;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.TabBackgroundTask;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.view.UpdateView;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SearchFragment extends SubsonicFragment implements SectionAdapter.OnItemClickedListener<Serializable> {
private static final String TAG = SearchFragment.class.getSimpleName();
private static final int MAX_ARTISTS = 20;
private static final int MAX_ALBUMS = 20;
private static final int MAX_SONGS = 50;
private static final int MIN_CLOSENESS = 1;
protected RecyclerView recyclerView;
protected SearchAdapter adapter;
protected boolean largeAlbums = false;
private RecyclerView recyclerView;
private SearchAdapter adapter;
private boolean largeAlbums = false;
private SearchResult searchResult;
private boolean skipSearch = false;
@ -62,7 +59,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null) {
if (savedInstanceState != null) {
searchResult = (SearchResult) savedInstanceState.getSerializable(Constants.FRAGMENT_LIST);
}
largeAlbums = Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true);
@ -79,16 +76,16 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
rootView = inflater.inflate(R.layout.abstract_recycler_fragment, container, false);
setTitle(R.string.search_title);
refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
refreshLayout = rootView.findViewById(R.id.refresh_layout);
refreshLayout.setEnabled(false);
recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler);
recyclerView = rootView.findViewById(R.id.fragment_recycler);
setupLayoutManager(recyclerView, largeAlbums);
registerForContextMenu(recyclerView);
context.onNewIntent(context.getIntent());
if(searchResult != null) {
if (searchResult != null) {
skipSearch = true;
recyclerView.setAdapter(adapter = new SearchAdapter(context, searchResult, getImageLoader(), largeAlbums, this));
}
@ -100,9 +97,9 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
public void setIsOnlyVisible(boolean isOnlyVisible) {
boolean update = this.isOnlyVisible != isOnlyVisible;
super.setIsOnlyVisible(isOnlyVisible);
if(update && adapter != null) {
if (update && adapter != null) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if(layoutManager instanceof GridLayoutManager) {
if (layoutManager instanceof GridLayoutManager) {
((GridLayoutManager) layoutManager).setSpanCount(getRecyclerColumnCount());
}
}
@ -114,7 +111,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
@Override
public int getSpanSize(int position) {
int viewType = adapter.getItemViewType(position);
if(viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER || viewType == ArtistAdapter.VIEW_TYPE_ARTIST) {
if (viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER || viewType == ArtistAdapter.VIEW_TYPE_ARTIST) {
return gridLayoutManager.getSpanCount();
} else {
return 1;
@ -132,7 +129,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
@Override
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Serializable> updateView, Serializable item) {
onCreateContextMenuSupport(menu, menuInflater, updateView, item);
if(item instanceof MusicDirectory.Entry && !Util.isOffline(context)) {
if (item instanceof MusicDirectory.Entry && !Util.isOffline(context)) {
menu.removeItem(R.id.song_menu_remove_playlist);
}
recreateContextMenu(menu);
@ -157,7 +154,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
if (entry.isDirectory()) {
onAlbumSelected(entry, false);
} else {
onSongSelected(entry, false, true, true, false);
onSongSelected(entry, true);
}
}
}
@ -166,8 +163,8 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
protected List<MusicDirectory.Entry> getSelectedEntries() {
List<Serializable> selected = adapter.getSelected();
List<MusicDirectory.Entry> selectedMedia = new ArrayList<>();
for(Serializable ser: selected) {
if(ser instanceof MusicDirectory.Entry) {
for (Serializable ser : selected) {
if (ser instanceof MusicDirectory.Entry) {
selectedMedia.add((MusicDirectory.Entry) ser);
}
}
@ -181,7 +178,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
}
public void search(final String query, final boolean autoplay) {
if(skipSearch) {
if (skipSearch) {
skipSearch = false;
return;
}
@ -207,7 +204,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
};
task.execute();
if(searchItem != null) {
if (searchItem != null) {
MenuItemCompat.collapseActionView(searchItem);
}
}
@ -221,7 +218,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
if(autoplay) {
if (autoplay) {
args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
}
args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
@ -235,7 +232,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ID, album.getId());
args.putString(Constants.INTENT_EXTRA_NAME_NAME, album.getTitle());
if(autoplay) {
if (autoplay) {
args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
}
fragment.setArguments(args);
@ -243,16 +240,14 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
replaceFragment(fragment);
}
private void onSongSelected(MusicDirectory.Entry song, boolean save, boolean append, boolean autoplay, boolean playNext) {
private void onSongSelected(MusicDirectory.Entry song, boolean append) {
DownloadService downloadService = getDownloadService();
if (downloadService != null) {
if (!append) {
downloadService.clear();
}
downloadService.download(Arrays.asList(song), save, false, playNext, false);
if (autoplay) {
downloadService.play(downloadService.size() - 1);
}
downloadService.download(Collections.singletonList(song), false, false, false, false);
downloadService.play(downloadService.size() - 1);
Util.toast(context, getResources().getQuantityString(R.plurals.select_album_n_songs_added, 1, 1));
}
@ -262,30 +257,30 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
query = query.toLowerCase();
Artist artist = null;
if(!searchResult.getArtists().isEmpty()) {
if (!searchResult.getArtists().isEmpty()) {
artist = searchResult.getArtists().get(0);
artist.setCloseness(Util.getStringDistance(artist.getName().toLowerCase(), query));
}
MusicDirectory.Entry album = null;
if(!searchResult.getAlbums().isEmpty()) {
if (!searchResult.getAlbums().isEmpty()) {
album = searchResult.getAlbums().get(0);
album.setCloseness(Util.getStringDistance(album.getTitle().toLowerCase(), query));
}
MusicDirectory.Entry song = null;
if(!searchResult.getSongs().isEmpty()) {
if (!searchResult.getSongs().isEmpty()) {
song = searchResult.getSongs().get(0);
song.setCloseness(Util.getStringDistance(song.getTitle().toLowerCase(), query));
}
if(artist != null && (artist.getCloseness() <= MIN_CLOSENESS ||
if (artist != null && (artist.getCloseness() <= MIN_CLOSENESS ||
(album == null || artist.getCloseness() <= album.getCloseness()) &&
(song == null || artist.getCloseness() <= song.getCloseness()))) {
(song == null || artist.getCloseness() <= song.getCloseness()))) {
onArtistSelected(artist, true);
} else if(album != null && (album.getCloseness() <= MIN_CLOSENESS ||
song == null || album.getCloseness() <= song.getCloseness())) {
} else if (album != null && (album.getCloseness() <= MIN_CLOSENESS ||
song == null || album.getCloseness() <= song.getCloseness())) {
onAlbumSelected(album, true);
} else if(song != null) {
onSongSelected(song, false, false, true, false);
} else if (song != null) {
onSongSelected(song, false);
}
}
}

View File

@ -1,6 +1,5 @@
package net.nullsum.audinaut.fragments;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
@ -29,7 +28,6 @@ import java.util.ArrayList;
import java.util.List;
public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> implements ArtistAdapter.OnMusicFolderChanged {
private static final String TAG = SelectArtistFragment.class.getSimpleName();
private List<MusicFolder> musicFolders = null;
private List<Entry> entries;
@ -44,7 +42,7 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
if(bundle != null) {
if (bundle != null) {
musicFolders = (List<MusicFolder>) bundle.getSerializable(Constants.FRAGMENT_LIST2);
}
artist = true;
@ -59,8 +57,8 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
Bundle args = getArguments();
if(args != null) {
if(args.getBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, false)) {
if (args != null) {
if (args.getBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, false)) {
groupId = args.getString(Constants.INTENT_EXTRA_NAME_ID);
groupName = args.getString(Constants.INTENT_EXTRA_NAME_NAME);
@ -90,10 +88,10 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
@Override
public void onItemClicked(UpdateView<Serializable> updateView, Serializable item) {
SubsonicFragment fragment;
if(item instanceof Artist) {
if (item instanceof Artist) {
Artist artist = (Artist) item;
if ((Util.isFirstLevelArtist(context) || Util.isOffline(context) || Util.isTagBrowsing(context)) || groupId != null) {
if ((Util.isFirstLevelArtist(context) || Util.isOffline(context)) || groupId != null) {
fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
@ -129,7 +127,7 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
super.onCreateOptionsMenu(menu, menuInflater);
if(Util.isOffline(context) || Util.isTagBrowsing(context) || groupId != null) {
if (Util.isOffline(context) || groupId != null) {
menu.removeItem(R.id.menu_first_level_artist);
} else {
if (Util.isFirstLevelArtist(context)) {
@ -145,7 +143,7 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(super.onOptionsItemSelected(item)) {
if (super.onOptionsItemSelected(item)) {
return true;
}
@ -166,47 +164,31 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
@Override
public List<Serializable> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
List<Serializable> items;
if(groupId == null) {
if (!Util.isOffline(context) && !Util.isTagBrowsing(context)) {
musicFolders = musicService.getMusicFolders(refresh, context, listener);
// Hide folders option if there is only one
if (musicFolders.size() == 1) {
musicFolders = null;
Util.setSelectedMusicFolderId(context, null);
}
} else {
musicFolders = null;
}
if (groupId == null) {
musicFolders = null;
String musicFolderId = Util.getSelectedMusicFolderId(context);
Indexes indexes = musicService.getIndexes(musicFolderId, refresh, context, listener);
indexes.sortChildren(context);
indexes.sortChildren();
items = new ArrayList<>(indexes.getShortcuts().size() + indexes.getArtists().size());
items.addAll(indexes.getShortcuts());
items.addAll(indexes.getArtists());
entries = indexes.getEntries();
items.addAll(entries);
} else {
List<Artist> artists = new ArrayList<>();
items = new ArrayList<>();
MusicDirectory dir = musicService.getMusicDirectory(groupId, groupName, refresh, context, listener);
for(Entry entry: dir.getChildren(true, false)) {
for (Entry entry : dir.getChildren(true, false)) {
Artist artist = new Artist();
artist.setId(entry.getId());
artist.setName(entry.getTitle());
artists.add(artist);
}
Indexes indexes = new Indexes();
//indexes.setArtists = artists;
indexes.sortChildren(context);
items.addAll(indexes.getArtists());
indexes.sortChildren();
items = new ArrayList<>(indexes.getArtists());
entries = dir.getChildren(false, true);
for(Entry entry: entries) {
items.add(entry);
}
items.addAll(entries);
}
return items;
@ -221,7 +203,7 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
public void setEmpty(boolean empty) {
super.setEmpty(empty);
if(empty && !Util.isOffline(context)) {
if (empty && !Util.isOffline(context)) {
objects.clear();
recyclerView.setAdapter(new ArtistAdapter(context, objects, musicFolders, this, this));
recyclerView.setVisibility(View.VISIBLE);
@ -244,7 +226,7 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
String startMusicFolderId = Util.getSelectedMusicFolderId(context);
String musicFolderId = selectedFolder == null ? null : selectedFolder.getId();
if(!Util.equals(startMusicFolderId, musicFolderId)) {
if (!Util.equals(startMusicFolderId, musicFolderId)) {
Util.setSelectedMusicFolderId(context, musicFolderId);
context.invalidate();
}

View File

@ -1,74 +1,58 @@
package net.nullsum.audinaut.fragments;
import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.adapter.AlphabeticalAlbumAdapter;
import net.nullsum.audinaut.adapter.EntryInfiniteGridAdapter;
import net.nullsum.audinaut.adapter.EntryGridAdapter;
import net.nullsum.audinaut.adapter.EntryInfiniteGridAdapter;
import net.nullsum.audinaut.adapter.SectionAdapter;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.service.CachedMusicService;
import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.util.DrawableTint;
import net.nullsum.audinaut.util.ImageLoader;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import net.nullsum.audinaut.service.MusicService;
import net.nullsum.audinaut.service.MusicServiceFactory;
import net.nullsum.audinaut.service.OfflineException;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.LoadingTask;
import net.nullsum.audinaut.util.Pair;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.TabBackgroundTask;
import net.nullsum.audinaut.util.UpdateHelper;
import net.nullsum.audinaut.util.UserUtil;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.view.FastScroller;
import net.nullsum.audinaut.view.GridSpacingDecoration;
import net.nullsum.audinaut.view.MyLeadingMarginSpan2;
import net.nullsum.audinaut.view.RecyclingImageView;
import net.nullsum.audinaut.view.UpdateView;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.Collections;
import java.util.List;
import static net.nullsum.audinaut.domain.MusicDirectory.Entry;
public class SelectDirectoryFragment extends SubsonicFragment implements SectionAdapter.OnItemClickedListener<Entry> {
private static final String TAG = SelectDirectoryFragment.class.getSimpleName();
private String id;
private String name;
private Entry directory;
private String playlistId;
private String playlistName;
private boolean playlistOwner;
private String albumListType;
private String albumListExtra;
private int albumListSize;
private boolean refreshListing = false;
private boolean restoredInstance = false;
private boolean lookupParent = false;
private boolean largeAlbums = false;
private boolean topTracks = false;
private String lookupEntry;
private RecyclerView recyclerView;
private FastScroller fastScroller;
private EntryGridAdapter entryGridAdapter;
@ -76,27 +60,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
private List<Entry> entries;
private LoadTask currentTask;
private SilentBackgroundTask updateCoverArtTask;
private ImageView coverArtView;
private Entry coverArtRep;
private String coverArtId;
String id;
String name;
Entry directory;
String playlistId;
String playlistName;
boolean playlistOwner;
String albumListType;
String albumListExtra;
int albumListSize;
boolean refreshListing = false;
boolean restoredInstance = false;
boolean lookupParent = false;
boolean largeAlbums = false;
boolean topTracks = false;
String lookupEntry;
public SelectDirectoryFragment() {
super();
}
@ -104,10 +67,10 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
if(bundle != null) {
if (bundle != null) {
entries = (List<Entry>) bundle.getSerializable(Constants.FRAGMENT_LIST);
albums = (List<Entry>) bundle.getSerializable(Constants.FRAGMENT_LIST2);
if(albums == null) {
if (albums == null) {
albums = new ArrayList<>();
}
restoredInstance = true;
@ -124,14 +87,13 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
Bundle args = getArguments();
if(args != null) {
if (args != null) {
id = args.getString(Constants.INTENT_EXTRA_NAME_ID);
name = args.getString(Constants.INTENT_EXTRA_NAME_NAME);
directory = (Entry) args.getSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY);
playlistId = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID);
playlistName = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME);
playlistOwner = args.getBoolean(Constants.INTENT_EXTRA_NAME_PLAYLIST_OWNER, false);
Object shareObj = args.getSerializable(Constants.INTENT_EXTRA_NAME_SHARE);
albumListType = args.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
albumListExtra = args.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA);
albumListSize = args.getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
@ -141,37 +103,37 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
topTracks = args.getBoolean(Constants.INTENT_EXTRA_TOP_TRACKS);
String childId = args.getString(Constants.INTENT_EXTRA_NAME_CHILD_ID);
if(childId != null) {
if (childId != null) {
id = childId;
lookupParent = true;
}
if(entries == null) {
if (entries == null) {
entries = (List<Entry>) args.getSerializable(Constants.FRAGMENT_LIST);
albums = (List<Entry>) args.getSerializable(Constants.FRAGMENT_LIST2);
if(albums == null) {
albums = new ArrayList<Entry>();
if (albums == null) {
albums = new ArrayList<>();
}
}
}
rootView = inflater.inflate(R.layout.abstract_recycler_fragment, container, false);
refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
refreshLayout = rootView.findViewById(R.id.refresh_layout);
refreshLayout.setOnRefreshListener(this);
if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true)) {
if (Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true)) {
largeAlbums = true;
}
recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler);
recyclerView = rootView.findViewById(R.id.fragment_recycler);
recyclerView.setHasFixedSize(true);
fastScroller = (FastScroller) rootView.findViewById(R.id.fragment_fast_scroller);
fastScroller = rootView.findViewById(R.id.fragment_fast_scroller);
setupScrollList(recyclerView);
setupLayoutManager(recyclerView, largeAlbums);
if(entries == null) {
if(primaryFragment || secondaryFragment) {
if (entries == null) {
if (primaryFragment || secondaryFragment) {
load(false);
} else {
invalidated = true;
@ -180,7 +142,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
finishLoading();
}
if(name != null) {
if (name != null) {
setTitle(name);
}
@ -191,9 +153,9 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
public void setIsOnlyVisible(boolean isOnlyVisible) {
boolean update = this.isOnlyVisible != isOnlyVisible;
super.setIsOnlyVisible(isOnlyVisible);
if(update && entryGridAdapter != null) {
if (update && entryGridAdapter != null) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if(layoutManager instanceof GridLayoutManager) {
if (layoutManager instanceof GridLayoutManager) {
((GridLayoutManager) layoutManager).setSpanCount(getRecyclerColumnCount());
}
}
@ -201,18 +163,17 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
if(albumListType != null) {
if (albumListType != null) {
menuInflater.inflate(R.menu.select_album_list, menu);
} else if(artist) {
} else if (artist) {
menuInflater.inflate(R.menu.select_album, menu);
} else {
if(Util.isOffline(context)) {
if (Util.isOffline(context)) {
menuInflater.inflate(R.menu.select_song_offline, menu);
}
else {
} else {
menuInflater.inflate(R.menu.select_song, menu);
if(playlistId == null || !playlistOwner) {
if (playlistId == null || !playlistOwner) {
menu.removeItem(R.id.menu_remove_playlist);
}
}
@ -234,21 +195,22 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
@Override
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView updateView, Entry entry) {
onCreateContextMenuSupport(menu, menuInflater, updateView, entry);
if(!Util.isOffline(context) && (playlistId == null || !playlistOwner)) {
if (!Util.isOffline(context) && (playlistId == null || !playlistOwner)) {
menu.removeItem(R.id.song_menu_remove_playlist);
}
recreateContextMenu(menu);
}
@Override
public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Entry> updateView, Entry entry) {
if(onContextItemSelected(menuItem, entry)) {
if (onContextItemSelected(menuItem, entry)) {
return true;
}
switch (menuItem.getItemId()) {
case R.id.song_menu_remove_playlist:
removeFromPlaylist(playlistId, playlistName, Arrays.<Integer>asList(entries.indexOf(entry)));
removeFromPlaylist(playlistId, playlistName, Collections.singletonList(entries.indexOf(entry)));
break;
}
@ -266,12 +228,12 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
if ("newest".equals(albumListType)) {
args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true);
}
if(!entry.isAlbum()) {
if (!entry.isAlbum()) {
args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
}
fragment.setArguments(args);
replaceFragment(fragment, true);
replaceFragment(fragment);
} else {
onSongPress(entries, entry, albumListType == null);
}
@ -288,11 +250,11 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
}
private void load(boolean refresh) {
if(refreshListing) {
if (refreshListing) {
refresh = true;
}
if(currentTask != null) {
if (currentTask != null) {
currentTask.cancel();
}
@ -309,21 +271,21 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
private void getMusicDirectory(final String id, final String name, final boolean refresh) {
setTitle(name);
new LoadTask(refresh) {
new LoadTask() {
@Override
protected MusicDirectory load(MusicService service) throws Exception {
MusicDirectory dir = getMusicDirectory(id, name, refresh, service, this);
if(lookupParent && dir.getParent() != null) {
if (lookupParent && dir.getParent() != null) {
dir = getMusicDirectory(dir.getParent(), name, refresh, service, this);
// Update the fragment pointers so other stuff works correctly
SelectDirectoryFragment.this.id = dir.getId();
SelectDirectoryFragment.this.name = dir.getName();
} else if(id != null && directory == null && dir.getParent() != null && !artist) {
} else if (id != null && directory == null && dir.getParent() != null && !artist) {
MusicDirectory parentDir = getMusicDirectory(dir.getParent(), name, refresh, true, service, this);
for(Entry child: parentDir.getChildren()) {
if(id.equals(child.getId())) {
for (Entry child : parentDir.getChildren()) {
if (id.equals(child.getId())) {
directory = child;
break;
}
@ -342,47 +304,10 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
}.execute();
}
private void getRecursiveMusicDirectory(final String id, final String name, final boolean refresh) {
setTitle(name);
new LoadTask(refresh) {
@Override
protected MusicDirectory load(MusicService service) throws Exception {
MusicDirectory root = getMusicDirectory(id, name, refresh, service, this);
List<Entry> songs = new ArrayList<Entry>();
getSongsRecursively(root, songs);
root.replaceChildren(songs);
return root;
}
private void getSongsRecursively(MusicDirectory parent, List<Entry> songs) throws Exception {
songs.addAll(parent.getChildren(false, true));
for (Entry dir : parent.getChildren(true, false)) {
MusicService musicService = MusicServiceFactory.getMusicService(context);
MusicDirectory musicDirectory;
if(Util.isTagBrowsing(context) && !Util.isOffline(context)) {
musicDirectory = musicService.getAlbum(dir.getId(), dir.getTitle(), false, context, this);
} else {
musicDirectory = musicService.getMusicDirectory(dir.getId(), dir.getTitle(), false, context, this);
}
getSongsRecursively(musicDirectory, songs);
}
}
@Override
protected void done(Pair<MusicDirectory, Boolean> result) {
SelectDirectoryFragment.this.name = result.getFirst().getName();
setTitle(SelectDirectoryFragment.this.name);
super.done(result);
}
}.execute();
}
private void getPlaylist(final String playlistId, final String playlistName, final boolean refresh) {
setTitle(playlistName);
new LoadTask(refresh) {
new LoadTask() {
@Override
protected MusicDirectory load(MusicService service) throws Exception {
return service.getPlaylist(refresh, playlistId, playlistName, context, this);
@ -391,39 +316,52 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
}
private void getAlbumList(final String albumListType, final int size, final boolean refresh) {
if ("random".equals(albumListType)) {
setTitle(R.string.main_albums_random);
} else if ("recent".equals(albumListType)) {
setTitle(R.string.main_albums_recent);
} else if ("frequent".equals(albumListType)) {
setTitle(R.string.main_albums_frequent);
} else if("genres".equals(albumListType) || "years".equals(albumListType)) {
setTitle(albumListExtra);
} else if("alphabeticalByName".equals(albumListType)) {
setTitle(R.string.main_albums_alphabetical);
} if (MainFragment.SONGS_NEWEST.equals(albumListType)) {
setTitle(R.string.main_songs_newest);
} else if (MainFragment.SONGS_TOP_PLAYED.equals(albumListType)) {
setTitle(R.string.main_songs_top_played);
} else if (MainFragment.SONGS_RECENT.equals(albumListType)) {
setTitle(R.string.main_songs_recent);
} else if (MainFragment.SONGS_FREQUENT.equals(albumListType)) {
setTitle(R.string.main_songs_frequent);
switch (albumListType) {
case "random":
setTitle(R.string.main_albums_random);
break;
case "recent":
setTitle(R.string.main_albums_recent);
break;
case "frequent":
setTitle(R.string.main_albums_frequent);
break;
case "genres":
case "years":
setTitle(albumListExtra);
break;
case "alphabeticalByName":
setTitle(R.string.main_albums_alphabetical);
break;
}
switch (albumListType) {
case MainFragment.SONGS_NEWEST:
setTitle(R.string.main_songs_newest);
break;
case MainFragment.SONGS_TOP_PLAYED:
setTitle(R.string.main_songs_top_played);
break;
case MainFragment.SONGS_RECENT:
setTitle(R.string.main_songs_recent);
break;
case MainFragment.SONGS_FREQUENT:
setTitle(R.string.main_songs_frequent);
break;
}
new LoadTask(true) {
new LoadTask() {
@Override
protected MusicDirectory load(MusicService service) throws Exception {
MusicDirectory result;
if("genres".equals(albumListType) || "years".equals(albumListType)) {
if ("genres".equals(albumListType) || "years".equals(albumListType)) {
result = service.getAlbumList(albumListType, albumListExtra, size, 0, refresh, context, this);
if(result.getChildrenSize() == 0 && "genres".equals(albumListType)) {
if (result.getChildrenSize() == 0 && "genres".equals(albumListType)) {
SelectDirectoryFragment.this.albumListType = "genres-songs";
result = service.getSongsByGenre(albumListExtra, size, 0, context, this);
}
} else if("genres".equals(albumListType) || "genres-songs".equals(albumListType)) {
} else if ("genres".equals(albumListType) || "genres-songs".equals(albumListType)) {
result = service.getSongsByGenre(albumListExtra, size, 0, context, this);
} else if(albumListType.indexOf(MainFragment.SONGS_LIST_PREFIX) != -1) {
} else if (albumListType.contains(MainFragment.SONGS_LIST_PREFIX)) {
result = service.getSongList(albumListType, size, 0, context, this);
} else {
result = service.getAlbumList(albumListType, size, 0, refresh, context, this);
@ -433,58 +371,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
}.execute();
}
private abstract class LoadTask extends TabBackgroundTask<Pair<MusicDirectory, Boolean>> {
private boolean refresh;
public LoadTask(boolean refresh) {
super(SelectDirectoryFragment.this);
this.refresh = refresh;
currentTask = this;
}
protected abstract MusicDirectory load(MusicService service) throws Exception;
@Override
protected Pair<MusicDirectory, Boolean> doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getMusicService(context);
MusicDirectory dir = load(musicService);
albums = dir.getChildren(true, false);
entries = dir.getChildren();
// This isn't really an artist if no albums on it!
if(albums.size() == 0) {
artist = false;
}
return new Pair<>(dir, true);
}
@Override
protected void done(Pair<MusicDirectory, Boolean> result) {
finishLoading();
currentTask = null;
}
@Override
public void updateCache(int changeCode) {
if(entryGridAdapter != null && changeCode == CachedMusicService.CACHE_UPDATE_LIST) {
entryGridAdapter.notifyDataSetChanged();
} else if(changeCode == CachedMusicService.CACHE_UPDATE_METADATA) {
if(coverArtView != null && coverArtRep != null && !Util.equals(coverArtRep.getCoverArt(), coverArtId)) {
synchronized (coverArtRep) {
if (updateCoverArtTask != null && updateCoverArtTask.isRunning()) {
updateCoverArtTask.cancel();
}
updateCoverArtTask = getImageLoader().loadImage(coverArtView, coverArtRep, false, true);
coverArtId = coverArtRep.getCoverArt();
}
}
}
}
}
@Override
public SectionAdapter<Entry> getCurrentAdapter() {
return entryGridAdapter;
@ -496,7 +382,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
@Override
public int getSpanSize(int position) {
int viewType = entryGridAdapter.getItemViewType(position);
if(viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER || viewType == EntryInfiniteGridAdapter.VIEW_TYPE_LOADING) {
if (viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER || viewType == EntryInfiniteGridAdapter.VIEW_TYPE_LOADING) {
return gridLayoutManager.getSpanCount();
} else {
return 1;
@ -507,19 +393,19 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
private void finishLoading() {
boolean validData = !entries.isEmpty() || !albums.isEmpty();
if(!validData) {
if (!validData) {
setEmpty(true);
}
if(validData) {
if (validData) {
recyclerView.setVisibility(View.VISIBLE);
}
if(albumListType == null) {
if (albumListType == null) {
entryGridAdapter = new EntryGridAdapter(context, entries, getImageLoader(), largeAlbums);
entryGridAdapter.setRemoveFromPlaylist(playlistId != null);
} else {
if("alphabeticalByName".equals(albumListType)) {
if ("alphabeticalByName".equals(albumListType)) {
entryGridAdapter = new AlphabeticalAlbumAdapter(context, entries, getImageLoader(), largeAlbums);
} else {
entryGridAdapter = new EntryInfiniteGridAdapter(context, entries, getImageLoader(), largeAlbums);
@ -530,10 +416,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
infiniteGridAdapter.setData(albumListType, albumListExtra, albumListSize);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
@ -542,15 +424,15 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
int totalItemCount = layoutManager.getItemCount();
int lastVisibleItem;
if(layoutManager instanceof GridLayoutManager) {
if (layoutManager instanceof GridLayoutManager) {
lastVisibleItem = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if(layoutManager instanceof LinearLayoutManager) {
} else if (layoutManager instanceof LinearLayoutManager) {
lastVisibleItem = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
} else {
return;
}
if(totalItemCount > 0 && lastVisibleItem >= totalItemCount - 2) {
if (totalItemCount > 0 && lastVisibleItem >= totalItemCount - 2) {
infiniteGridAdapter.loadMore();
}
}
@ -558,17 +440,17 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
}
entryGridAdapter.setOnItemClickedListener(this);
// Always show artist if this is not a artist we are viewing
if(!artist) {
entryGridAdapter.setShowArtist(true);
if (!artist) {
entryGridAdapter.setShowArtist();
}
if(topTracks) {
entryGridAdapter.setShowAlbum(true);
if (topTracks) {
entryGridAdapter.setShowAlbum();
}
int scrollToPosition = -1;
if(lookupEntry != null) {
for(int i = 0; i < entries.size(); i++) {
if(lookupEntry.equals(entries.get(i).getTitle())) {
if (lookupEntry != null) {
for (int i = 0; i < entries.size(); i++) {
if (lookupEntry.equals(entries.get(i).getTitle())) {
scrollToPosition = i;
entryGridAdapter.addSelected(entries.get(i));
lookupEntry = null;
@ -581,7 +463,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
fastScroller.attachRecyclerView(recyclerView);
context.supportInvalidateOptionsMenu();
if(scrollToPosition != -1) {
if (scrollToPosition != -1) {
recyclerView.scrollToPosition(scrollToPosition);
}
@ -595,30 +477,31 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
@Override
protected void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
List<Entry> songs = getSelectedEntries();
if(!songs.isEmpty()) {
download(songs, append, false, !append, playNext, shuffle);
if (!songs.isEmpty()) {
download(songs, append, !append, playNext, shuffle);
entryGridAdapter.clearSelected();
} else {
playAll(shuffle, append, playNext);
}
}
private void playAll(final boolean shuffle, final boolean append, final boolean playNext) {
boolean hasSubFolders = albums != null && !albums.isEmpty();
if (hasSubFolders && id != null) {
downloadRecursively(id, false, append, !append, shuffle, false, playNext);
} else if(hasSubFolders && albumListType != null) {
} else if (hasSubFolders && albumListType != null) {
downloadRecursively(albums, shuffle, append, playNext);
} else {
download(entries, append, false, !append, playNext, shuffle);
download(entries, append, !append, playNext, shuffle);
}
}
private List<Integer> getSelectedIndexes() {
List<Entry> selected = entryGridAdapter.getSelected();
List<Integer> indexes = new ArrayList<Integer>();
List<Integer> indexes = new ArrayList<>();
for(Entry entry: selected) {
for (Entry entry : selected) {
indexes.add(entries.indexOf(entry));
}
@ -628,28 +511,29 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
@Override
protected void downloadBackground(final boolean save) {
List<Entry> songs = getSelectedEntries();
if(playlistId != null) {
if (playlistId != null) {
songs = entries;
}
if(songs.isEmpty()) {
if (songs.isEmpty()) {
// Get both songs and albums
downloadRecursively(id, save, false, false, false, true);
downloadRecursively(id, save, false, false, false, true, false);
} else {
downloadBackground(save, songs);
}
}
@Override
protected void downloadBackground(final boolean save, final List<Entry> entries) {
void downloadBackground(final boolean save, final List<Entry> entries) {
if (getDownloadService() == null) {
return;
}
warnIfStorageUnavailable();
RecursiveLoader onValid = new RecursiveLoader(context) {
new RecursiveLoader(context) {
@Override
protected Boolean doInBackground() throws Throwable {
getSongsRecursively(entries, true);
getSongsRecursively(entries);
getDownloadService().downloadBackground(songs, save);
return null;
}
@ -662,16 +546,16 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
}
@Override
protected void download(List<Entry> entries, boolean append, boolean save, boolean autoplay, boolean playNext, boolean shuffle) {
download(entries, append, save, autoplay, playNext, shuffle, playlistName, playlistId);
void download(List<Entry> entries, boolean append, boolean autoplay, boolean playNext, boolean shuffle) {
download(entries, append, autoplay, playNext, shuffle, playlistName, playlistId);
}
@Override
protected void delete() {
List<Entry> songs = getSelectedEntries();
if(songs.isEmpty()) {
for(Entry entry: entries) {
if(entry.isDirectory()) {
if (songs.isEmpty()) {
for (Entry entry : entries) {
if (entry.isDirectory()) {
deleteRecursively(entry);
} else {
songs.add(entry);
@ -683,7 +567,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
}
}
public void removeFromPlaylist(final String id, final String name, final List<Integer> indexes) {
private void removeFromPlaylist(final String id, final String name, final List<Integer> indexes) {
new LoadingTask<Void>(context, true) {
@Override
protected Void doInBackground() throws Throwable {
@ -694,7 +578,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
@Override
protected void done(Void result) {
for(Integer index: indexes) {
for (Integer index : indexes) {
entryGridAdapter.removeAt(index);
}
Util.toast(context, context.getResources().getString(R.string.removed_playlist, indexes.size(), name));
@ -714,113 +598,43 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
}.execute();
}
private void showTopTracks() {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle(getArguments());
args.putBoolean(Constants.INTENT_EXTRA_TOP_TRACKS, true);
fragment.setArguments(args);
private abstract class LoadTask extends TabBackgroundTask<Pair<MusicDirectory, Boolean>> {
replaceFragment(fragment, true);
}
public LoadTask() {
super(SelectDirectoryFragment.this);
private View createHeader() {
View header = LayoutInflater.from(context).inflate(R.layout.select_album_header, null, false);
setupCoverArt(header);
setupTextDisplay(header);
return header;
}
private void setupCoverArt(View header) {
setupCoverArtImpl((RecyclingImageView) header.findViewById(R.id.select_album_art));
}
private void setupCoverArtImpl(RecyclingImageView coverArtView) {
final ImageLoader imageLoader = getImageLoader();
if(entries.size() > 0) {
coverArtRep = null;
this.coverArtView = coverArtView;
for (int i = 0; (i < 3) && (coverArtRep == null || coverArtRep.getCoverArt() == null); i++) {
coverArtRep = entries.get(random.nextInt(entries.size()));
}
coverArtView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (coverArtRep == null || coverArtRep.getCoverArt() == null) {
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(context);
ImageView fullScreenView = new ImageView(context);
imageLoader.loadImage(fullScreenView, coverArtRep, true, true);
builder.setCancelable(true);
AlertDialog imageDialog = builder.create();
// Set view here with unecessary 0's to remove top/bottom border
imageDialog.setView(fullScreenView, 0, 0, 0, 0);
imageDialog.show();
}
});
synchronized (coverArtRep) {
coverArtId = coverArtRep.getCoverArt();
updateCoverArtTask = imageLoader.loadImage(coverArtView, coverArtRep, false, true);
}
currentTask = this;
}
coverArtView.setOnInvalidated(new RecyclingImageView.OnInvalidated() {
@Override
public void onInvalidated(RecyclingImageView imageView) {
setupCoverArtImpl(imageView);
protected abstract MusicDirectory load(MusicService service) throws Exception;
@Override
protected Pair<MusicDirectory, Boolean> doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getMusicService(context);
MusicDirectory dir = load(musicService);
albums = dir.getChildren(true, false);
entries = dir.getChildren();
// This isn't really an artist if no albums on it!
if (albums.size() == 0) {
artist = false;
}
});
}
private void setupTextDisplay(final View header) {
final TextView titleView = (TextView) header.findViewById(R.id.select_album_title);
if(playlistName != null) {
titleView.setText(playlistName);
} else if(name != null) {
titleView.setText(name);
return new Pair<>(dir, true);
}
int songCount = 0;
Set<String> artists = new HashSet<String>();
Set<Integer> years = new HashSet<Integer>();
Integer totalDuration = 0;
for (Entry entry : entries) {
if (!entry.isDirectory()) {
songCount++;
if (entry.getArtist() != null) {
artists.add(entry.getArtist());
}
if(entry.getYear() != null) {
years.add(entry.getYear());
}
Integer duration = entry.getDuration();
if(duration != null) {
totalDuration += duration;
}
}
@Override
protected void done(Pair<MusicDirectory, Boolean> result) {
finishLoading();
currentTask = null;
}
final TextView artistView = (TextView) header.findViewById(R.id.select_album_artist);
if (artists.size() == 1) {
String artistText = artists.iterator().next();
if(years.size() == 1) {
artistText += " - " + years.iterator().next();
@Override
public void updateCache(int changeCode) {
if (entryGridAdapter != null && changeCode == CachedMusicService.CACHE_UPDATE_LIST) {
entryGridAdapter.notifyDataSetChanged();
}
artistView.setText(artistText);
artistView.setVisibility(View.VISIBLE);
} else {
artistView.setVisibility(View.GONE);
}
TextView songCountView = (TextView) header.findViewById(R.id.select_album_song_count);
TextView songLengthView = (TextView) header.findViewById(R.id.select_album_song_length);
String s = context.getResources().getQuantityString(R.plurals.select_album_n_songs, songCount, songCount);
songCountView.setText(s.toUpperCase());
songLengthView.setText(Util.formatDuration(totalDuration));
}
}

View File

@ -21,18 +21,17 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.adapter.GenreAdapter;
import net.nullsum.audinaut.adapter.SectionAdapter;
import net.nullsum.audinaut.domain.Genre;
import net.nullsum.audinaut.service.MusicService;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.adapter.GenreAdapter;
import net.nullsum.audinaut.view.UpdateView;
import java.util.List;
public class SelectGenreFragment extends SelectRecyclerFragment<Genre> {
private static final String TAG = SelectGenreFragment.class.getSimpleName();
@Override
public int getOptionsMenu() {
@ -68,7 +67,8 @@ public class SelectGenreFragment extends SelectRecyclerFragment<Genre> {
}
@Override
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Genre> updateView, Genre item) {}
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Genre> updateView, Genre item) {
}
@Override
public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Genre> updateView, Genre item) {

View File

@ -1,10 +1,6 @@
package net.nullsum.audinaut.fragments;
import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@ -12,31 +8,29 @@ import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import androidx.appcompat.app.AlertDialog;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.adapter.PlaylistAdapter;
import net.nullsum.audinaut.adapter.SectionAdapter;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.Playlist;
import net.nullsum.audinaut.service.DownloadFile;
import net.nullsum.audinaut.service.MusicService;
import net.nullsum.audinaut.service.MusicServiceFactory;
import net.nullsum.audinaut.service.OfflineException;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.SyncUtil;
import net.nullsum.audinaut.util.CacheCleaner;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.LoadingTask;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.SyncUtil;
import net.nullsum.audinaut.util.UserUtil;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.adapter.PlaylistAdapter;
import net.nullsum.audinaut.view.UpdateView;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
private static final String TAG = SelectPlaylistFragment.class.getSimpleName();
@Override
public void onCreate(Bundle bundle) {
@ -50,11 +44,10 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Playlist> updateView, Playlist playlist) {
if (Util.isOffline(context)) {
menuInflater.inflate(R.menu.select_playlist_context_offline, menu);
}
else {
} else {
menuInflater.inflate(R.menu.select_playlist_context, menu);
if(playlist.getPublic() != null && playlist.getPublic() == true && playlist.getId().indexOf(".m3u") == -1 && !UserUtil.getCurrentUsername(context).equals(playlist.getOwner())) {
if (playlist.getPublic() != null && playlist.getPublic() && !playlist.getId().contains(".m3u") && !UserUtil.getCurrentUsername(context).equals(playlist.getOwner())) {
menu.removeItem(R.id.playlist_update_info);
menu.removeItem(R.id.playlist_menu_delete);
}
@ -67,11 +60,10 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Playlist> updateView, Playlist playlist) {
SubsonicFragment fragment;
Bundle args;
FragmentTransaction trans;
switch (menuItem.getItemId()) {
case R.id.playlist_menu_download:
downloadPlaylist(playlist.getId(), playlist.getName(), false, true, false, false, true);
downloadPlaylist(playlist.getId(), playlist.getName());
break;
case R.id.playlist_menu_play_now:
fragment = new SelectDirectoryFragment();
@ -115,22 +107,13 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
@Override
public SectionAdapter<Playlist> getAdapter(List<Playlist> playlists) {
List<Playlist> mine = new ArrayList<>();
String currentUsername = UserUtil.getCurrentUsername(context);
for(Playlist playlist: playlists) {
if(playlist.getOwner() == null || playlist.getOwner().equals(currentUsername)) {
mine.add(playlist);
}
}
return new PlaylistAdapter(context, playlists, getImageLoader(), largeAlbums, this);
}
@Override
public List<Playlist> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
List<Playlist> playlists = musicService.getPlaylists(refresh, context, listener);
if(!Util.isOffline(context) && refresh) {
if (!Util.isOffline(context) && refresh) {
new CacheCleaner(context, getDownloadService()).cleanPlaylists(playlists);
}
return playlists;
@ -147,7 +130,7 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
if((playlist.getOwner() != null && playlist.getOwner().equals(UserUtil.getCurrentUsername(context)) || playlist.getId().indexOf(".m3u") != -1)) {
if ((playlist.getOwner() != null && playlist.getOwner().equals(UserUtil.getCurrentUsername(context)) || playlist.getId().contains(".m3u"))) {
args.putBoolean(Constants.INTENT_EXTRA_NAME_PLAYLIST_OWNER, true);
}
fragment.setArguments(args);
@ -158,7 +141,7 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
@Override
public void onFinishRefresh() {
Bundle args = getArguments();
if(args != null) {
if (args != null) {
String playlistId = args.getString(Constants.INTENT_EXTRA_NAME_ID, null);
if (playlistId != null && objects != null) {
for (Playlist playlist : objects) {
@ -172,38 +155,33 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
}
private void deletePlaylist(final Playlist playlist) {
Util.confirmDialog(context, R.string.common_delete, playlist.getName(), new DialogInterface.OnClickListener() {
Util.confirmDialog(context, R.string.common_delete, playlist.getName(), (dialog, which) -> new LoadingTask<Void>(context, false) {
@Override
public void onClick(DialogInterface dialog, int which) {
new LoadingTask<Void>(context, false) {
@Override
protected Void doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getMusicService(context);
musicService.deletePlaylist(playlist.getId(), context, null);
SyncUtil.removeSyncedPlaylist(context, playlist.getId());
return null;
}
@Override
protected void done(Void result) {
adapter.removeItem(playlist);
Util.toast(context, context.getResources().getString(R.string.menu_deleted_playlist, playlist.getName()));
}
@Override
protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException) {
msg = getErrorMessage(error);
} else {
msg = context.getResources().getString(R.string.menu_deleted_playlist_error, playlist.getName()) + " " + getErrorMessage(error);
}
Util.toast(context, msg, false);
}
}.execute();
protected Void doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getMusicService(context);
musicService.deletePlaylist(playlist.getId(), context, null);
SyncUtil.removeSyncedPlaylist(context, playlist.getId());
return null;
}
});
@Override
protected void done(Void result) {
adapter.removeItem(playlist);
Util.toast(context, context.getResources().getString(R.string.menu_deleted_playlist, playlist.getName()));
}
@Override
protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException) {
msg = getErrorMessage(error);
} else {
msg = context.getResources().getString(R.string.menu_deleted_playlist_error, playlist.getName()) + " " + getErrorMessage(error);
}
Util.toast(context, msg, false);
}
}.execute());
}
private void displayPlaylistInfo(final Playlist playlist) {
@ -213,12 +191,12 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
headers.add(R.string.details_title);
details.add(playlist.getName());
if(playlist.getOwner() != null) {
if (playlist.getOwner() != null) {
headers.add(R.string.details_owner);
details.add(playlist.getOwner());
}
if(playlist.getComment() != null) {
if (playlist.getComment() != null) {
headers.add(R.string.details_comments);
details.add(playlist.getComment());
}
@ -226,23 +204,23 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
headers.add(R.string.details_song_count);
details.add(playlist.getSongCount());
if(playlist.getDuration() != null) {
if (playlist.getDuration() != null) {
headers.add(R.string.details_length);
details.add(Util.formatDuration(playlist.getDuration()));
}
if(playlist.getPublic() != null) {
if (playlist.getPublic() != null) {
headers.add(R.string.details_public);
details.add(Util.formatBoolean(context, playlist.getPublic()));
}
if(playlist.getCreated() != null) {
if (playlist.getCreated() != null) {
headers.add(R.string.details_created);
DateFormat dateFormat = DateFormat.getDateInstance();
details.add(dateFormat.format(playlist.getCreated()));
}
if(playlist.getChanged() != null) {
if (playlist.getChanged() != null) {
headers.add(R.string.details_updated);
DateFormat dateFormat = DateFormat.getDateInstance();
@ -254,93 +232,59 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
private void updatePlaylistInfo(final Playlist playlist) {
View dialogView = context.getLayoutInflater().inflate(R.layout.update_playlist, null);
final EditText nameBox = (EditText)dialogView.findViewById(R.id.get_playlist_name);
final EditText commentBox = (EditText)dialogView.findViewById(R.id.get_playlist_comment);
final CheckBox publicBox = (CheckBox)dialogView.findViewById(R.id.get_playlist_public);
final EditText nameBox = dialogView.findViewById(R.id.get_playlist_name);
final EditText commentBox = dialogView.findViewById(R.id.get_playlist_comment);
final CheckBox publicBox = dialogView.findViewById(R.id.get_playlist_public);
nameBox.setText(playlist.getName());
commentBox.setText(playlist.getComment());
Boolean pub = playlist.getPublic();
if(pub == null) {
if (pub == null) {
publicBox.setEnabled(false);
} else {
publicBox.setChecked(pub);
}
new AlertDialog.Builder(context)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.playlist_update_info)
.setView(dialogView)
.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
new LoadingTask<Void>(context, false) {
@Override
protected Void doInBackground() throws Throwable {
String name = nameBox.getText().toString();
String comment = commentBox.getText().toString();
boolean isPublic = publicBox.isChecked();
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.playlist_update_info)
.setView(dialogView)
.setPositiveButton(R.string.common_ok, (dialog, which) -> new LoadingTask<Void>(context, false) {
@Override
protected Void doInBackground() throws Throwable {
String name = nameBox.getText().toString();
String comment = commentBox.getText().toString();
boolean isPublic = publicBox.isChecked();
MusicService musicService = MusicServiceFactory.getMusicService(context);
musicService.updatePlaylist(playlist.getId(), name, comment, isPublic, context, null);
MusicService musicService = MusicServiceFactory.getMusicService(context);
musicService.updatePlaylist(playlist.getId(), name, comment, isPublic, context, null);
playlist.setName(name);
playlist.setComment(comment);
playlist.setPublic(isPublic);
playlist.setName(name);
playlist.setComment(comment);
playlist.setPublic(isPublic);
return null;
return null;
}
@Override
protected void done(Void result) {
Util.toast(context, context.getResources().getString(R.string.playlist_updated_info, playlist.getName()));
}
@Override
protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException) {
msg = getErrorMessage(error);
} else {
msg = context.getResources().getString(R.string.playlist_updated_info_error, playlist.getName()) + " " + getErrorMessage(error);
}
@Override
protected void done(Void result) {
Util.toast(context, context.getResources().getString(R.string.playlist_updated_info, playlist.getName()));
}
@Override
protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException) {
msg = getErrorMessage(error);
} else {
msg = context.getResources().getString(R.string.playlist_updated_info_error, playlist.getName()) + " " + getErrorMessage(error);
}
Util.toast(context, msg, false);
}
}.execute();
}
})
.setNegativeButton(R.string.common_cancel, null)
.show();
Util.toast(context, msg, false);
}
}.execute())
.setNegativeButton(R.string.common_cancel, null)
.show();
}
private void syncPlaylist(Playlist playlist) {
SyncUtil.addSyncedPlaylist(context, playlist.getId());
downloadPlaylist(playlist.getId(), playlist.getName(), true, true, false, false, true);
}
private void stopSyncPlaylist(final Playlist playlist) {
SyncUtil.removeSyncedPlaylist(context, playlist.getId());
new LoadingTask<Void>(context, false) {
@Override
protected Void doInBackground() throws Throwable {
// Unpin all of the songs in playlist
MusicService musicService = MusicServiceFactory.getMusicService(context);
MusicDirectory root = musicService.getPlaylist(true, playlist.getId(), playlist.getName(), context, this);
for(MusicDirectory.Entry entry: root.getChildren()) {
DownloadFile file = new DownloadFile(context, entry, false);
file.unpin();
}
return null;
}
@Override
protected void done(Void result) {
}
}.execute();
}
}

View File

@ -15,26 +15,16 @@
package net.nullsum.audinaut.fragments;
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.adapter.SectionAdapter;
@ -45,23 +35,27 @@ import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.TabBackgroundTask;
import net.nullsum.audinaut.view.FastScroller;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public abstract class SelectRecyclerFragment<T> extends SubsonicFragment implements SectionAdapter.OnItemClickedListener<T> {
private static final String TAG = SelectRecyclerFragment.class.getSimpleName();
protected RecyclerView recyclerView;
protected FastScroller fastScroller;
protected SectionAdapter<T> adapter;
protected UpdateTask currentTask;
protected List<T> objects;
protected boolean serialize = true;
protected boolean largeAlbums = false;
protected boolean pullToRefresh = true;
protected boolean backgroundUpdate = true;
RecyclerView recyclerView;
SectionAdapter<T> adapter;
List<T> objects;
boolean serialize = true;
boolean largeAlbums = false;
boolean pullToRefresh = true;
boolean backgroundUpdate = true;
private FastScroller fastScroller;
private UpdateTask currentTask;
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
if(bundle != null && serialize) {
if (bundle != null && serialize) {
objects = (List<T>) bundle.getSerializable(Constants.FRAGMENT_LIST);
}
}
@ -69,7 +63,7 @@ public abstract class SelectRecyclerFragment<T> extends SubsonicFragment impleme
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(serialize) {
if (serialize) {
outState.putSerializable(Constants.FRAGMENT_LIST, (Serializable) objects);
}
}
@ -78,20 +72,20 @@ public abstract class SelectRecyclerFragment<T> extends SubsonicFragment impleme
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
rootView = inflater.inflate(R.layout.abstract_recycler_fragment, container, false);
refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
refreshLayout = rootView.findViewById(R.id.refresh_layout);
refreshLayout.setOnRefreshListener(this);
recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler);
fastScroller = (FastScroller) rootView.findViewById(R.id.fragment_fast_scroller);
recyclerView = rootView.findViewById(R.id.fragment_recycler);
fastScroller = rootView.findViewById(R.id.fragment_fast_scroller);
setupLayoutManager();
if(pullToRefresh) {
if (pullToRefresh) {
setupScrollList(recyclerView);
} else {
refreshLayout.setEnabled(false);
}
if(objects == null) {
if (objects == null) {
refresh(false);
} else {
recyclerView.setAdapter(adapter = getAdapter(objects));
@ -102,7 +96,7 @@ public abstract class SelectRecyclerFragment<T> extends SubsonicFragment impleme
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
if(!primaryFragment) {
if (!primaryFragment) {
return;
}
@ -110,44 +104,39 @@ public abstract class SelectRecyclerFragment<T> extends SubsonicFragment impleme
onFinishSetupOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
@Override
public void setIsOnlyVisible(boolean isOnlyVisible) {
boolean update = this.isOnlyVisible != isOnlyVisible;
super.setIsOnlyVisible(isOnlyVisible);
if(update && adapter != null) {
if (update && adapter != null) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if(layoutManager instanceof GridLayoutManager) {
if (layoutManager instanceof GridLayoutManager) {
((GridLayoutManager) layoutManager).setSpanCount(getRecyclerColumnCount());
}
}
}
@Override
protected void refresh(final boolean refresh) {
void refresh(final boolean refresh) {
int titleRes = getTitleResource();
if(titleRes != 0) {
if (titleRes != 0) {
setTitle(getTitleResource());
}
if(backgroundUpdate) {
if (backgroundUpdate) {
recyclerView.setVisibility(View.GONE);
}
// Cancel current running task before starting another one
if(currentTask != null) {
if (currentTask != null) {
currentTask.cancel();
}
currentTask = new UpdateTask(this, refresh);
if(backgroundUpdate) {
if (backgroundUpdate) {
currentTask.execute();
} else {
objects = new ArrayList<T>();
objects = new ArrayList<>();
try {
objects = getObjects(null, refresh, null);
@ -167,17 +156,20 @@ public abstract class SelectRecyclerFragment<T> extends SubsonicFragment impleme
setupLayoutManager(recyclerView, largeAlbums);
}
public abstract int getOptionsMenu();
public abstract SectionAdapter<T> getAdapter(List<T> objs);
public abstract List<T> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception;
public abstract int getTitleResource();
protected abstract int getOptionsMenu();
public void onFinishRefresh() {
protected abstract SectionAdapter<T> getAdapter(List<T> objs);
protected abstract List<T> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception;
protected abstract int getTitleResource();
void onFinishRefresh() {
}
private class UpdateTask extends TabBackgroundTask<List<T>> {
private boolean refresh;
private final boolean refresh;
public UpdateTask(SubsonicFragment fragment, boolean refresh) {
super(fragment);
@ -185,10 +177,10 @@ public abstract class SelectRecyclerFragment<T> extends SubsonicFragment impleme
}
@Override
public List<T> doInBackground() throws Exception {
public List<T> doInBackground() {
MusicService musicService = MusicServiceFactory.getMusicService(context);
objects = new ArrayList<T>();
objects = new ArrayList<>();
try {
objects = getObjects(musicService, refresh, this);
@ -203,7 +195,7 @@ public abstract class SelectRecyclerFragment<T> extends SubsonicFragment impleme
public void done(List<T> result) {
if (result != null && !result.isEmpty()) {
recyclerView.setAdapter(adapter = getAdapter(result));
if(!fastScroller.isAttached()) {
if (!fastScroller.isAttached()) {
fastScroller.attachRecyclerView(recyclerView);
}

View File

@ -20,9 +20,6 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import java.util.ArrayList;
import java.util.List;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.adapter.BasicListAdapter;
import net.nullsum.audinaut.adapter.SectionAdapter;
@ -31,6 +28,9 @@ import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.view.UpdateView;
import java.util.ArrayList;
import java.util.List;
public class SelectYearFragment extends SelectRecyclerFragment<String> {
public SelectYearFragment() {
@ -51,9 +51,9 @@ public class SelectYearFragment extends SelectRecyclerFragment<String> {
}
@Override
public List<String> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
public List<String> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) {
List<String> decades = new ArrayList<>();
for(int i = 2010; i >= 1800; i -= 10) {
for (int i = 2010; i >= 1800; i -= 10) {
decades.add(String.valueOf(i));
}
@ -79,7 +79,8 @@ public class SelectYearFragment extends SelectRecyclerFragment<String> {
}
@Override
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<String> updateView, String item) {}
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<String> updateView, String item) {
}
@Override
public boolean onContextItemSelected(MenuItem menuItem, UpdateView<String> updateView, String item) {

View File

@ -16,9 +16,8 @@
package net.nullsum.audinaut.fragments;
import android.accounts.Account;
import android.app.backup.BackupManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
@ -29,23 +28,14 @@ import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
import android.text.InputType;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.LinkedHashMap;
import java.util.Map;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.service.HeadphoneListenerService;
@ -59,10 +49,16 @@ import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.view.CacheLocationPreference;
import net.nullsum.audinaut.view.ErrorDialog;
import java.io.File;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.LinkedHashMap;
import java.util.Map;
public class SettingsFragment extends PreferenceCompatFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
private final static String TAG = SettingsFragment.class.getSimpleName();
private final Map<String, ServerSettings> serverSettings = new LinkedHashMap<String, ServerSettings>();
private final Map<String, ServerSettings> serverSettings = new LinkedHashMap<>();
private boolean testingConnection;
private ListPreference theme;
private ListPreference maxBitrateWifi;
@ -74,7 +70,6 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
private ListPreference keepPlayedCount;
private ListPreference tempLoss;
private ListPreference pauseDisconnect;
private Preference addServerPreference;
private PreferenceCategory serversCategory;
private ListPreference songPressAction;
private ListPreference syncInterval;
@ -122,17 +117,22 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
Bundle args = new Bundle();
int xml = 0;
if("appearance".equals(name)) {
xml = R.xml.settings_appearance;
} else if("cache".equals(name)) {
xml = R.xml.settings_cache;
} else if("playback".equals(name)) {
xml = R.xml.settings_playback;
} else if("servers".equals(name)) {
xml = R.xml.settings_servers;
switch (name) {
case "appearance":
xml = R.xml.settings_appearance;
break;
case "cache":
xml = R.xml.settings_cache;
break;
case "playback":
xml = R.xml.settings_playback;
break;
case "servers":
xml = R.xml.settings_servers;
break;
}
if(xml != 0) {
if (xml != 0) {
args.putInt(Constants.INTENT_EXTRA_FRAGMENT_TYPE, xml);
newFragment.setArguments(args);
replaceFragment(newFragment);
@ -142,37 +142,43 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// Random error I have no idea how to reproduce
if(sharedPreferences == null) {
if (sharedPreferences == null) {
return;
}
update();
if (Constants.PREFERENCES_KEY_HIDE_MEDIA.equals(key)) {
setHideMedia(sharedPreferences.getBoolean(key, true));
}
else if (Constants.PREFERENCES_KEY_MEDIA_BUTTONS.equals(key)) {
setMediaButtonsEnabled(sharedPreferences.getBoolean(key, true));
}
else if (Constants.PREFERENCES_KEY_CACHE_LOCATION.equals(key)) {
setCacheLocation(sharedPreferences.getString(key, ""));
}
else if(Constants.PREFERENCES_KEY_SYNC_MOST_RECENT.equals(key)) {
SyncUtil.removeMostRecentSyncFiles(context);
} else if(Constants.PREFERENCES_KEY_REPLAY_GAIN.equals(key) || Constants.PREFERENCES_KEY_REPLAY_GAIN_BUMP.equals(key) || Constants.PREFERENCES_KEY_REPLAY_GAIN_UNTAGGED.equals(key)) {
DownloadService downloadService = DownloadService.getInstance();
if(downloadService != null) {
downloadService.reapplyVolume();
}
} else if(Constants.PREFERENCES_KEY_START_ON_HEADPHONES.equals(key)) {
Intent serviceIntent = new Intent();
serviceIntent.setClassName(context.getPackageName(), HeadphoneListenerService.class.getName());
switch (key) {
case Constants.PREFERENCES_KEY_HIDE_MEDIA:
setHideMedia(sharedPreferences.getBoolean(key, true));
break;
case Constants.PREFERENCES_KEY_MEDIA_BUTTONS:
setMediaButtonsEnabled(sharedPreferences.getBoolean(key, true));
break;
case Constants.PREFERENCES_KEY_CACHE_LOCATION:
setCacheLocation(sharedPreferences.getString(key, ""));
break;
case Constants.PREFERENCES_KEY_SYNC_MOST_RECENT:
SyncUtil.removeMostRecentSyncFiles(context);
break;
case Constants.PREFERENCES_KEY_REPLAY_GAIN:
case Constants.PREFERENCES_KEY_REPLAY_GAIN_BUMP:
case Constants.PREFERENCES_KEY_REPLAY_GAIN_UNTAGGED:
DownloadService downloadService = DownloadService.getInstance();
if (downloadService != null) {
downloadService.reapplyVolume();
}
break;
case Constants.PREFERENCES_KEY_START_ON_HEADPHONES:
Intent serviceIntent = new Intent();
serviceIntent.setClassName(context.getPackageName(), HeadphoneListenerService.class.getName());
if(sharedPreferences.getBoolean(key, false)) {
context.startService(serviceIntent);
} else {
context.stopService(serviceIntent);
}
if (sharedPreferences.getBoolean(key, false)) {
context.startService(serviceIntent);
} else {
context.stopService(serviceIntent);
}
break;
}
scheduleBackup();
@ -199,7 +205,7 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
tempLoss = (ListPreference) this.findPreference(Constants.PREFERENCES_KEY_TEMP_LOSS);
pauseDisconnect = (ListPreference) this.findPreference(Constants.PREFERENCES_KEY_PAUSE_DISCONNECT);
serversCategory = (PreferenceCategory) this.findPreference(Constants.PREFERENCES_KEY_SERVER_KEY);
addServerPreference = this.findPreference(Constants.PREFERENCES_KEY_SERVER_ADD);
Preference addServerPreference = this.findPreference(Constants.PREFERENCES_KEY_SERVER_ADD);
songPressAction = (ListPreference) this.findPreference(Constants.PREFERENCES_KEY_SONG_PRESS_ACTION);
syncInterval = (ListPreference) this.findPreference(Constants.PREFERENCES_KEY_SYNC_INTERVAL);
syncEnabled = (CheckBoxPreference) this.findPreference(Constants.PREFERENCES_KEY_SYNC_ENABLED);
@ -215,86 +221,69 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
settings = Util.getPreferences(context);
serverCount = settings.getInt(Constants.PREFERENCES_KEY_SERVER_COUNT, 1);
if(cacheSize != null) {
this.findPreference("clearCache").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Util.confirmDialog(context, R.string.common_delete, R.string.common_confirm_message_cache, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
new LoadingTask<Void>(context, false) {
@Override
protected Void doInBackground() throws Throwable {
FileUtil.deleteMusicDirectory(context);
FileUtil.deleteSerializedCache(context);
FileUtil.deleteArtworkCache(context);
return null;
}
if (cacheSize != null) {
this.findPreference("clearCache").setOnPreferenceClickListener(preference -> {
Util.confirmDialog(context, (dialog, which) -> new LoadingTask<Void>(context, false) {
@Override
protected Void doInBackground() {
FileUtil.deleteMusicDirectory(context);
FileUtil.deleteSerializedCache(context);
FileUtil.deleteArtworkCache(context);
return null;
}
@Override
protected void done(Void result) {
Util.toast(context, R.string.settings_cache_clear_complete);
}
@Override
protected void done(Void result) {
Util.toast(context, R.string.settings_cache_clear_complete);
}
@Override
protected void error(Throwable error) {
Util.toast(context, getErrorMessage(error), false);
}
}.execute();
}
});
return false;
}
@Override
protected void error(Throwable error) {
Util.toast(context, getErrorMessage(error), false);
}
}.execute());
return false;
});
}
if(syncEnabled != null) {
this.findPreference(Constants.PREFERENCES_KEY_SYNC_ENABLED).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Boolean syncEnabled = (Boolean) newValue;
if (syncEnabled != null) {
this.findPreference(Constants.PREFERENCES_KEY_SYNC_ENABLED).setOnPreferenceChangeListener((preference, newValue) -> {
Boolean syncEnabled = (Boolean) newValue;
Account account = new Account(Constants.SYNC_ACCOUNT_NAME, Constants.SYNC_ACCOUNT_TYPE);
ContentResolver.setSyncAutomatically(account, Constants.SYNC_ACCOUNT_PLAYLIST_AUTHORITY, syncEnabled);
Account account = new Account(Constants.SYNC_ACCOUNT_NAME, Constants.SYNC_ACCOUNT_TYPE);
ContentResolver.setSyncAutomatically(account, Constants.SYNC_ACCOUNT_PLAYLIST_AUTHORITY, syncEnabled);
return true;
}
return true;
});
syncInterval.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Integer syncInterval = Integer.parseInt(((String) newValue));
syncInterval.setOnPreferenceChangeListener((preference, newValue) -> {
Integer syncInterval = Integer.parseInt(((String) newValue));
Account account = new Account(Constants.SYNC_ACCOUNT_NAME, Constants.SYNC_ACCOUNT_TYPE);
ContentResolver.addPeriodicSync(account, Constants.SYNC_ACCOUNT_PLAYLIST_AUTHORITY, new Bundle(), 60L * syncInterval);
Account account = new Account(Constants.SYNC_ACCOUNT_NAME, Constants.SYNC_ACCOUNT_TYPE);
ContentResolver.addPeriodicSync(account, Constants.SYNC_ACCOUNT_PLAYLIST_AUTHORITY, new Bundle(), 60L * syncInterval);
return true;
}
return true;
});
}
if(serversCategory != null) {
addServerPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
serverCount++;
int instance = serverCount;
serversCategory.addPreference(addServer(serverCount));
if (serversCategory != null) {
addServerPreference.setOnPreferenceClickListener(preference -> {
serverCount++;
int instance = serverCount;
serversCategory.addPreference(addServer(serverCount));
SharedPreferences.Editor editor = settings.edit();
editor.putInt(Constants.PREFERENCES_KEY_SERVER_COUNT, serverCount);
// Reset set folder ID
editor.putString(Constants.PREFERENCES_KEY_MUSIC_FOLDER_ID + instance, null);
editor.putString(Constants.PREFERENCES_KEY_SERVER_URL + instance, "http://yourhost");
editor.putString(Constants.PREFERENCES_KEY_SERVER_NAME + instance, getResources().getString(R.string.settings_server_unused));
editor.apply();
SharedPreferences.Editor editor = settings.edit();
editor.putInt(Constants.PREFERENCES_KEY_SERVER_COUNT, serverCount);
// Reset set folder ID
editor.putString(Constants.PREFERENCES_KEY_MUSIC_FOLDER_ID + instance, null);
editor.putString(Constants.PREFERENCES_KEY_SERVER_URL + instance, "http://yourhost");
editor.putString(Constants.PREFERENCES_KEY_SERVER_NAME + instance, getResources().getString(R.string.settings_server_unused));
editor.apply();
ServerSettings ss = new ServerSettings(instance);
serverSettings.put(String.valueOf(instance), ss);
ss.update();
ServerSettings ss = new ServerSettings(instance);
serverSettings.put(String.valueOf(instance), ss);
ss.update();
return true;
}
return true;
});
serversCategory.setOrderingAsAdded(false);
@ -311,18 +300,8 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
}
private void scheduleBackup() {
try {
Class managerClass = Class.forName("android.app.backup.BackupManager");
Constructor managerConstructor = managerClass.getConstructor(Context.class);
Object manager = managerConstructor.newInstance(context);
Method m = managerClass.getMethod("dataChanged");
m.invoke(manager);
} catch(ClassNotFoundException e) {
Log.e(TAG, "No backup manager found");
} catch(Throwable t) {
Log.e(TAG, "Scheduling backup failed " + t);
t.printStackTrace();
}
BackupManager backupManager = new BackupManager(context);
backupManager.dataChanged();
}
private void update() {
@ -330,11 +309,11 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
return;
}
if(theme != null) {
if (theme != null) {
theme.setSummary(theme.getEntry());
}
if(cacheSize != null) {
if (cacheSize != null) {
maxBitrateWifi.setSummary(maxBitrateWifi.getEntry());
maxBitrateMobile.setSummary(maxBitrateMobile.getEntry());
networkTimeout.setSummary(networkTimeout.getEntry());
@ -343,24 +322,24 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
preloadCountMobile.setSummary(preloadCountMobile.getEntry());
try {
if(megabyteFromat == null) {
if (megabyteFromat == null) {
megabyteFromat = new DecimalFormat(getResources().getString(R.string.util_bytes_format_megabyte));
}
cacheSize.setSummary(megabyteFromat.format((double) Integer.parseInt(cacheSize.getText())).replace(".00", ""));
} catch(Exception e) {
} catch (Exception e) {
Log.e(TAG, "Failed to format cache size", e);
cacheSize.setSummary(cacheSize.getText());
}
}
if(keepPlayedCount != null) {
if (keepPlayedCount != null) {
keepPlayedCount.setSummary(keepPlayedCount.getEntry());
tempLoss.setSummary(tempLoss.getEntry());
pauseDisconnect.setSummary(pauseDisconnect.getEntry());
songPressAction.setSummary(songPressAction.getEntry());
if(replayGain.isChecked()) {
if (replayGain.isChecked()) {
replayGainType.setEnabled(true);
replayGainBump.setEnabled(true);
replayGainUntagged.setEnabled(true);
@ -372,18 +351,18 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
replayGainType.setSummary(replayGainType.getEntry());
}
if(syncEnabled != null) {
if (syncEnabled != null) {
syncInterval.setSummary(syncInterval.getEntry());
if(syncEnabled.isChecked()) {
if(!syncInterval.isEnabled()) {
if (syncEnabled.isChecked()) {
if (!syncInterval.isEnabled()) {
syncInterval.setEnabled(true);
syncWifi.setEnabled(true);
syncNotification.setEnabled(true);
syncMostRecent.setEnabled(true);
}
} else {
if(syncInterval.isEnabled()) {
if (syncInterval.isEnabled()) {
syncInterval.setEnabled(false);
syncWifi.setEnabled(false);
syncNotification.setEnabled(false);
@ -396,9 +375,10 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
ss.update();
}
}
public void checkForRemoved() {
private void checkForRemoved() {
for (ServerSettings ss : serverSettings.values()) {
if(!ss.update()) {
if (!ss.update()) {
serversCategory.removePreference(ss.getScreen());
serverCount--;
}
@ -410,18 +390,15 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
screen.setKey(Constants.PREFERENCES_KEY_SERVER_KEY + instance);
screen.setOrder(instance);
screen.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
SettingsFragment newFragment = new SettingsFragment();
screen.setOnPreferenceClickListener(preference -> {
SettingsFragment newFragment = new SettingsFragment();
Bundle args = new Bundle();
args.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, instance);
newFragment.setArguments(args);
Bundle args = new Bundle();
args.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, instance);
newFragment.setArguments(args);
replaceFragment(newFragment);
return false;
}
replaceFragment(newFragment);
return false;
});
return screen;
@ -466,12 +443,7 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
Button defaultButton = new Button(getContext());
defaultButton.setText(internalSSIDDisplay);
defaultButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
editText.setText(internalSSID);
}
});
defaultButton.setOnClickListener(v -> editText.setText(internalSSID));
root.addView(defaultButton);
}
};
@ -498,16 +470,19 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
serverPasswordPreference.setSummary("***");
serverPasswordPreference.setTitle(R.string.settings_server_password);
final SwitchPreference authMethodPreference = new SwitchPreference(context);
authMethodPreference.setKey(Constants.PREFERENCES_KEY_AUTH_METHOD + instance);
authMethodPreference.setSummary(R.string.settings_auth_summary);
authMethodPreference.setDefaultValue(true); // use Token/Salt by default
authMethodPreference.setTitle(R.string.settings_auth_method);
final Preference serverOpenBrowser = new Preference(context);
serverOpenBrowser.setKey(Constants.PREFERENCES_KEY_OPEN_BROWSER);
serverOpenBrowser.setPersistent(false);
serverOpenBrowser.setTitle(R.string.settings_server_open_browser);
serverOpenBrowser.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
openInBrowser(instance);
return true;
}
serverOpenBrowser.setOnPreferenceClickListener(preference -> {
openInBrowser(instance);
return true;
});
Preference serverRemoveServerPreference = new Preference(context);
@ -515,53 +490,53 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
serverRemoveServerPreference.setPersistent(false);
serverRemoveServerPreference.setTitle(R.string.settings_servers_remove);
serverRemoveServerPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Util.confirmDialog(context, R.string.common_delete, screen.getTitle().toString(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Reset values to null so when we ask for them again they are new
serverNamePreference.setText(null);
serverUrlPreference.setText(null);
serverUsernamePreference.setText(null);
serverPasswordPreference.setText(null);
serverRemoveServerPreference.setOnPreferenceClickListener(preference -> {
Util.confirmDialog(context, R.string.common_delete, screen.getTitle().toString(), (dialog, which) -> {
// Reset values to null so when we ask for them again they are new
serverNamePreference.setText(null);
serverUrlPreference.setText(null);
serverUsernamePreference.setText(null);
serverPasswordPreference.setText(null);
// Don't use Util.getActiveServer since it is 0 if offline
int activeServer = Util.getPreferences(context).getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
for (int i = instance; i <= serverCount; i++) {
Util.removeInstanceName(context, i, activeServer);
}
// Don't use Util.getActiveServer since it is 0 if offline
int activeServer = Util.getPreferences(context).getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
for (int i = instance; i <= serverCount; i++) {
Util.removeInstanceName(context, i, activeServer);
}
serverCount--;
SharedPreferences.Editor editor = settings.edit();
editor.putInt(Constants.PREFERENCES_KEY_SERVER_COUNT, serverCount);
editor.apply();
serverCount--;
SharedPreferences.Editor editor = settings.edit();
editor.putInt(Constants.PREFERENCES_KEY_SERVER_COUNT, serverCount);
editor.apply();
removeCurrent();
removeCurrent();
SubsonicFragment parentFragment = context.getCurrentFragment();
if(parentFragment instanceof SettingsFragment) {
SettingsFragment serverSelectionFragment = (SettingsFragment) parentFragment;
serverSelectionFragment.checkForRemoved();
}
}
});
SubsonicFragment parentFragment = context.getCurrentFragment();
if (parentFragment instanceof SettingsFragment) {
SettingsFragment serverSelectionFragment = (SettingsFragment) parentFragment;
serverSelectionFragment.checkForRemoved();
}
});
return true;
}
return true;
});
Preference serverTestConnectionPreference = new Preference(context);
serverTestConnectionPreference.setKey(Constants.PREFERENCES_KEY_TEST_CONNECTION + instance);
serverTestConnectionPreference.setPersistent(false);
serverTestConnectionPreference.setTitle(R.string.settings_test_connection_title);
serverTestConnectionPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
testConnection(instance);
return false;
}
serverTestConnectionPreference.setOnPreferenceClickListener(preference -> {
testConnection(instance);
return false;
});
Preference serverStartScanPreference = new Preference(context);
serverStartScanPreference.setKey(Constants.PREFERENCES_KEY_START_SCAN + instance);
serverStartScanPreference.setPersistent(false);
serverStartScanPreference.setTitle(R.string.settings_start_scan_title);
serverStartScanPreference.setOnPreferenceClickListener(preference -> {
startScan(instance);
return false;
});
screen.addPreference(serverNamePreference);
@ -570,7 +545,9 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
screen.addPreference(serverLocalNetworkSSIDPreference);
screen.addPreference(serverUsernamePreference);
screen.addPreference(serverPasswordPreference);
screen.addPreference(authMethodPreference);
screen.addPreference(serverTestConnectionPreference);
screen.addPreference(serverStartScanPreference);
screen.addPreference(serverOpenBrowser);
screen.addPreference(serverRemoveServerPreference);
@ -585,22 +562,22 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
if (!nomediaDir.createNewFile()) {
Log.w(TAG, "Failed to create " + nomediaDir);
}
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to create " + nomediaDir, e);
}
try {
if(!musicNoMedia.createNewFile()) {
if (!musicNoMedia.createNewFile()) {
Log.w(TAG, "Failed to create " + musicNoMedia);
}
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to create " + musicNoMedia, e);
}
} else if (!hide && nomediaDir.exists()) {
if (!nomediaDir.delete()) {
Log.w(TAG, "Failed to delete " + nomediaDir);
}
if(!musicNoMedia.delete()) {
if (!musicNoMedia.delete()) {
Log.w(TAG, "Failed to delete " + musicNoMedia);
}
}
@ -628,7 +605,7 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
editor.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, defaultPath);
editor.apply();
if(cacheLocation != null) {
if (cacheLocation != null) {
cacheLocation.setSummary(defaultPath);
cacheLocation.setText(defaultPath);
}
@ -640,13 +617,49 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
}
}
private void startScan(final int instance) {
LoadingTask<Boolean> task = new LoadingTask<Boolean>(context) {
@Override
protected Boolean doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getOnlineService();
try {
musicService.setInstance(instance);
musicService.startScan(context);
return true;
} finally {
musicService.setInstance(null);
}
}
@Override
protected void done(Boolean licenseValid) {
Log.d(TAG, "Finished media scan start");
Util.toast(context, R.string.settings_media_scan_started);
}
@Override
public void cancel() {
super.cancel();
}
@Override
protected void error(Throwable error) {
Log.w(TAG, error.toString(), error);
new ErrorDialog(context, getResources().getString(R.string.settings_media_scan_start_failed) +
" " + getErrorMessage(error), false);
}
};
task.execute();
}
private void testConnection(final int instance) {
LoadingTask<Boolean> task = new LoadingTask<Boolean>(context) {
private int previousInstance;
@Override
protected Boolean doInBackground() throws Throwable {
updateProgress(R.string.settings_testing_connection);
updateProgress();
previousInstance = Util.getActiveServer(context);
testingConnection = true;
@ -685,8 +698,8 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
private void openInBrowser(final int instance) {
SharedPreferences prefs = Util.getPreferences(context);
String url = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null);
if(url == null) {
new ErrorDialog(context, R.string.settings_invalid_url, false);
if (url == null) {
new ErrorDialog(context, R.string.settings_invalid_url);
return;
}
Uri uriServer = Uri.parse(url);
@ -696,13 +709,13 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
}
private class ServerSettings {
private int instance;
private EditTextPreference serverName;
private EditTextPreference serverUrl;
private EditTextPreference serverLocalNetworkSSID;
private EditTextPreference serverInternalUrl;
private EditTextPreference username;
private PreferenceScreen screen;
private final int instance;
private final EditTextPreference serverName;
private final EditTextPreference serverUrl;
private final EditTextPreference serverLocalNetworkSSID;
private final EditTextPreference serverInternalUrl;
private final EditTextPreference username;
private final PreferenceScreen screen;
private ServerSettings(int instance) {
this.instance = instance;
@ -713,55 +726,46 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
serverInternalUrl = (EditTextPreference) SettingsFragment.this.findPreference(Constants.PREFERENCES_KEY_SERVER_INTERNAL_URL + instance);
username = (EditTextPreference) SettingsFragment.this.findPreference(Constants.PREFERENCES_KEY_USERNAME + instance);
if(serverName != null) {
serverUrl.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object value) {
try {
String url = (String) value;
new URL(url);
if (url.contains(" ") || url.contains("@") || url.contains("_")) {
throw new Exception();
}
} catch (Exception x) {
new ErrorDialog(context, R.string.settings_invalid_url, false);
return false;
if (serverName != null) {
serverUrl.setOnPreferenceChangeListener((preference, value) -> {
try {
String url = (String) value;
new URL(url);
if (url.contains(" ") || url.contains("@") || url.contains("_")) {
throw new Exception();
}
return true;
} catch (Exception x) {
new ErrorDialog(context, R.string.settings_invalid_url);
return false;
}
return true;
});
serverInternalUrl.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object value) {
try {
String url = (String) value;
// Allow blank internal IP address
if ("".equals(url) || url == null) {
return true;
}
new URL(url);
if (url.contains(" ") || url.contains("@") || url.contains("_")) {
throw new Exception();
}
} catch (Exception x) {
new ErrorDialog(context, R.string.settings_invalid_url, false);
return false;
serverInternalUrl.setOnPreferenceChangeListener((preference, value) -> {
try {
String url = (String) value;
// Allow blank internal IP address
if ("".equals(url) || url == null) {
return true;
}
return true;
new URL(url);
if (url.contains(" ") || url.contains("@") || url.contains("_")) {
throw new Exception();
}
} catch (Exception x) {
new ErrorDialog(context, R.string.settings_invalid_url);
return false;
}
return true;
});
username.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object value) {
String username = (String) value;
if (username == null || !username.equals(username.trim())) {
new ErrorDialog(context, R.string.settings_invalid_username, false);
return false;
}
return true;
username.setOnPreferenceChangeListener((preference, value) -> {
String username = (String) value;
if (username == null || !username.equals(username.trim())) {
new ErrorDialog(context, R.string.settings_invalid_username);
return false;
}
return true;
});
}
}
@ -773,7 +777,7 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
public boolean update() {
SharedPreferences prefs = Util.getPreferences(context);
if(prefs.contains(Constants.PREFERENCES_KEY_SERVER_NAME + instance)) {
if (prefs.contains(Constants.PREFERENCES_KEY_SERVER_NAME + instance)) {
if (serverName != null) {
serverName.setSummary(serverName.getText());
serverUrl.setSummary(serverUrl.getText());

View File

@ -24,12 +24,6 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.Artist;
@ -40,13 +34,16 @@ import net.nullsum.audinaut.service.MusicService;
import net.nullsum.audinaut.service.MusicServiceFactory;
import net.nullsum.audinaut.util.Util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Provides search suggestions based on recent searches.
*
* @author Sindre Mehus
*/
public class AudinautSearchProvider extends ContentProvider {
private static final String TAG = AudinautSearchProvider.class.getSimpleName();
private static final String RESOURCE_PREFIX = "android.resource://net.nullsum.audinaut/";
private static final String[] COLUMNS = {"_id",
@ -58,7 +55,7 @@ public class AudinautSearchProvider extends ContentProvider {
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
if(selectionArgs[0].isEmpty()) {
if (selectionArgs[0].isEmpty()) {
return null;
}
@ -69,9 +66,6 @@ public class AudinautSearchProvider extends ContentProvider {
private SearchResult search(String query) {
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
if (musicService == null) {
return null;
}
try {
return musicService.search(new SearchCritera(query, 5, 10, 10), getContext(), null);
@ -87,14 +81,14 @@ public class AudinautSearchProvider extends ContentProvider {
}
// Add all results into one pot
List<Object> results = new ArrayList<Object>();
List<Object> results = new ArrayList<>();
results.addAll(searchResult.getArtists());
results.addAll(searchResult.getAlbums());
results.addAll(searchResult.getSongs());
// For each, calculate its string distance to the query
for(Object obj: results) {
if(obj instanceof Artist) {
for (Object obj : results) {
if (obj instanceof Artist) {
Artist artist = (Artist) obj;
artist.setCloseness(Util.getStringDistance(query, artist.getName()));
} else {
@ -104,78 +98,47 @@ public class AudinautSearchProvider extends ContentProvider {
}
// Sort based on the closeness paramater
Collections.sort(results, new Comparator<Object>() {
@Override
public int compare(Object lhs, Object rhs) {
// Get the closeness of the two objects
int left, right;
boolean leftArtist = lhs instanceof Artist;
boolean rightArtist = rhs instanceof Artist;
if (leftArtist) {
left = ((Artist) lhs).getCloseness();
} else {
left = ((MusicDirectory.Entry) lhs).getCloseness();
}
if (rightArtist) {
right = ((Artist) rhs).getCloseness();
} else {
right = ((MusicDirectory.Entry) rhs).getCloseness();
}
Collections.sort(results, (lhs, rhs) -> {
// Get the closeness of the two objects
int left, right;
boolean leftArtist = lhs instanceof Artist;
boolean rightArtist = rhs instanceof Artist;
if (leftArtist) {
left = ((Artist) lhs).getCloseness();
} else {
left = ((MusicDirectory.Entry) lhs).getCloseness();
}
if (rightArtist) {
right = ((Artist) rhs).getCloseness();
} else {
right = ((MusicDirectory.Entry) rhs).getCloseness();
}
if (left == right) {
if(leftArtist && rightArtist) {
return 0;
} else if(leftArtist) {
return -1;
} else if(rightArtist) {
return 1;
} else {
return 0;
}
} else if (left > right) {
if (left == right) {
if (leftArtist && rightArtist) {
return 0;
} else if (leftArtist) {
return -1;
} else if (rightArtist) {
return 1;
} else {
return -1;
return 0;
}
} else if (left > right) {
return 1;
} else {
return -1;
}
});
// Done sorting, add results to cursor
for(Object obj: results) {
if(obj instanceof Artist) {
for (Object obj : results) {
if (obj instanceof Artist) {
Artist artist = (Artist) obj;
String icon = RESOURCE_PREFIX + R.drawable.ic_action_artist;
cursor.addRow(new Object[]{artist.getId().hashCode(), artist.getName(), null, "ar-" + artist.getId(), artist.getName(), icon});
} else {
MusicDirectory.Entry entry = (MusicDirectory.Entry) obj;
if(entry.isDirectory()) {
String icon = RESOURCE_PREFIX + R.drawable.ic_action_album;
cursor.addRow(new Object[]{entry.getId().hashCode(), entry.getTitle(), entry.getArtist(), entry.getId(), entry.getTitle(), icon});
} else {
String icon = RESOURCE_PREFIX + R.drawable.ic_action_song;
String id;
if(Util.isTagBrowsing(getContext())) {
id = entry.getAlbumId();
} else {
id = entry.getParent();
}
String artistDisplay;
if(entry.getArtist() == null) {
if(entry.getAlbum() != null) {
artistDisplay = entry.getAlbumDisplay();
} else {
artistDisplay = "";
}
} else if(entry.getAlbum() != null) {
artistDisplay = entry.getArtist() + " - " + entry.getAlbumDisplay();
} else {
artistDisplay = entry.getArtist();
}
cursor.addRow(new Object[]{entry.getId().hashCode(), entry.getTitle(), artistDisplay, "so-" + id, entry.getTitle(), icon});
}
}
}
return cursor;

View File

@ -24,8 +24,8 @@ import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
@ -34,10 +34,12 @@ import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.activity.SubsonicActivity;
import net.nullsum.audinaut.activity.SubsonicFragmentActivity;
@ -66,16 +68,16 @@ public class AudinautWidgetProvider extends AppWidgetProvider {
private static AudinautWidget4x4 instance4x4;
public static synchronized void notifyInstances(Context context, DownloadService service, boolean playing) {
if(instance4x1 == null) {
if (instance4x1 == null) {
instance4x1 = new AudinautWidget4x1();
}
if(instance4x2 == null) {
if (instance4x2 == null) {
instance4x2 = new AudinautWidget4x2();
}
if(instance4x3 == null) {
if (instance4x3 == null) {
instance4x3 = new AudinautWidget4x3();
}
if(instance4x4 == null) {
if (instance4x4 == null) {
instance4x4 = new AudinautWidget4x4();
}
@ -85,165 +87,6 @@ public class AudinautWidgetProvider extends AppWidgetProvider {
instance4x4.notifyChange(context, service, playing);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
defaultAppWidget(context, appWidgetIds);
}
@Override
public void onEnabled(Context context) {
notifyInstances(context, DownloadService.getInstance(), false);
}
protected int getLayout() {
return 0;
}
/**
* Initialize given widgets to default state, where we launch Subsonic on default click
* and hide actions if service not running.
*/
private void defaultAppWidget(Context context, int[] appWidgetIds) {
final Resources res = context.getResources();
final RemoteViews views = new RemoteViews(context.getPackageName(), getLayout());
views.setTextViewText(R.id.artist, res.getText(R.string.widget_initial_text));
if(getLayout() == R.layout.appwidget4x2) {
views.setTextViewText(R.id.album, "");
}
linkButtons(context, views, false);
performUpdate(context, null, appWidgetIds, false);
}
private void pushUpdate(Context context, int[] appWidgetIds, RemoteViews views) {
// Update specific list of appWidgetIds if given, otherwise default to all
final AppWidgetManager manager = AppWidgetManager.getInstance(context);
if (appWidgetIds != null) {
manager.updateAppWidget(appWidgetIds, views);
} else {
manager.updateAppWidget(new ComponentName(context, this.getClass()), views);
}
}
/**
* Handle a change notification coming over from {@link DownloadService}
*/
public void notifyChange(Context context, DownloadService service, boolean playing) {
if (hasInstances(context)) {
performUpdate(context, service, null, playing);
}
}
/**
* Check against {@link AppWidgetManager} if there are any instances of this widget.
*/
private boolean hasInstances(Context context) {
AppWidgetManager manager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = manager.getAppWidgetIds(new ComponentName(context, getClass()));
return (appWidgetIds.length > 0);
}
/**
* Update all active widget instances by pushing changes
*/
private void performUpdate(Context context, DownloadService service, int[] appWidgetIds, boolean playing) {
final Resources res = context.getResources();
final RemoteViews views = new RemoteViews(context.getPackageName(), getLayout());
if(playing) {
views.setViewVisibility(R.id.widget_root, View.VISIBLE);
} else {
// Hide widget
SharedPreferences prefs = Util.getPreferences(context);
if(prefs.getBoolean(Constants.PREFERENCES_KEY_HIDE_WIDGET, false)) {
views.setViewVisibility(R.id.widget_root, View.GONE);
}
}
// Get Entry from current playing DownloadFile
MusicDirectory.Entry currentPlaying = null;
if(service == null) {
// Deserialize from playling list to setup
try {
PlayerQueue state = FileUtil.deserialize(context, DownloadServiceLifecycleSupport.FILENAME_DOWNLOADS_SER, PlayerQueue.class);
if (state != null && state.currentPlayingIndex != -1) {
currentPlaying = state.songs.get(state.currentPlayingIndex);
}
} catch(Exception e) {
Log.e(TAG, "Failed to grab current playing", e);
}
} else {
currentPlaying = service.getCurrentPlaying() == null ? null : service.getCurrentPlaying().getSong();
}
String title = currentPlaying == null ? null : currentPlaying.getTitle();
CharSequence artist = currentPlaying == null ? null : currentPlaying.getArtist();
CharSequence album = currentPlaying == null ? null : currentPlaying.getAlbum();
CharSequence errorState = null;
// Show error message?
String status = Environment.getExternalStorageState();
if (status.equals(Environment.MEDIA_SHARED) ||
status.equals(Environment.MEDIA_UNMOUNTED)) {
errorState = res.getText(R.string.widget_sdcard_busy);
} else if (status.equals(Environment.MEDIA_REMOVED)) {
errorState = res.getText(R.string.widget_sdcard_missing);
} else if (currentPlaying == null) {
errorState = res.getText(R.string.widget_initial_text);
}
if (errorState != null) {
// Show error state to user
views.setTextViewText(R.id.title,null);
views.setTextViewText(R.id.artist, errorState);
views.setTextViewText(R.id.album, "");
if(getLayout() != R.layout.appwidget4x1) {
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.appwidget_art_default);
}
} else {
// No error, so show normal titles
views.setTextViewText(R.id.title, title);
views.setTextViewText(R.id.artist, artist);
if(getLayout() != R.layout.appwidget4x1) {
views.setTextViewText(R.id.album, album);
}
}
// Set correct drawable for pause state
if (playing) {
views.setImageViewResource(R.id.control_play, R.drawable.media_pause_dark);
} else {
views.setImageViewResource(R.id.control_play, R.drawable.media_start_dark);
}
// Set the cover art
try {
boolean large = false;
if(getLayout() != R.layout.appwidget4x1 && getLayout() != R.layout.appwidget4x2) {
large = true;
}
ImageLoader imageLoader = SubsonicActivity.getStaticImageLoader(context);
Bitmap bitmap = imageLoader == null ? null : imageLoader.getCachedImage(context, currentPlaying, large);
if (bitmap == null) {
// Set default cover art
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.appwidget_art_unknown);
} else {
bitmap = getRoundedCornerBitmap(bitmap);
views.setImageViewBitmap(R.id.appwidget_coverart, bitmap);
}
} catch (Exception x) {
Log.e(TAG, "Failed to load cover art", x);
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.appwidget_art_unknown);
}
// Link actions buttons to intents
linkButtons(context, views, currentPlaying != null);
pushUpdate(context, appWidgetIds, views);
}
/**
* Round the corners of a bitmap for the cover art image
*/
@ -270,12 +113,173 @@ public class AudinautWidgetProvider extends AppWidgetProvider {
return output;
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
defaultAppWidget(context, appWidgetIds);
}
@Override
public void onEnabled(Context context) {
notifyInstances(context, DownloadService.getInstance(), false);
}
int getLayout() {
return 0;
}
/**
* Initialize given widgets to default state, where we launch Subsonic on default click
* and hide actions if service not running.
*/
private void defaultAppWidget(Context context, int[] appWidgetIds) {
final Resources res = context.getResources();
final RemoteViews views = new RemoteViews(context.getPackageName(), getLayout());
views.setTextViewText(R.id.artist, res.getText(R.string.widget_initial_text));
if (getLayout() == R.layout.appwidget4x2) {
views.setTextViewText(R.id.album, "");
}
linkButtons(context, views);
performUpdate(context, null, appWidgetIds, false);
}
private void pushUpdate(Context context, int[] appWidgetIds, RemoteViews views) {
// Update specific list of appWidgetIds if given, otherwise default to all
final AppWidgetManager manager = AppWidgetManager.getInstance(context);
if (appWidgetIds != null) {
manager.updateAppWidget(appWidgetIds, views);
} else {
manager.updateAppWidget(new ComponentName(context, this.getClass()), views);
}
}
/**
* Handle a change notification coming over from {@link DownloadService}
*/
void notifyChange(Context context, DownloadService service, boolean playing) {
if (hasInstances(context)) {
performUpdate(context, service, null, playing);
}
}
/**
* Check against {@link AppWidgetManager} if there are any instances of this widget.
*/
private boolean hasInstances(Context context) {
AppWidgetManager manager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = manager.getAppWidgetIds(new ComponentName(context, getClass()));
return (appWidgetIds.length > 0);
}
/**
* Update all active widget instances by pushing changes
*/
private void performUpdate(Context context, DownloadService service, int[] appWidgetIds, boolean playing) {
final Resources res = context.getResources();
final RemoteViews views = new RemoteViews(context.getPackageName(), getLayout());
if (playing) {
views.setViewVisibility(R.id.widget_root, View.VISIBLE);
} else {
// Hide widget
SharedPreferences prefs = Util.getPreferences(context);
if (prefs.getBoolean(Constants.PREFERENCES_KEY_HIDE_WIDGET, false)) {
views.setViewVisibility(R.id.widget_root, View.GONE);
}
}
// Get Entry from current playing DownloadFile
MusicDirectory.Entry currentPlaying = null;
if (service == null) {
// Deserialize from playling list to setup
try {
PlayerQueue state = FileUtil.deserialize(context, DownloadServiceLifecycleSupport.FILENAME_DOWNLOADS_SER, PlayerQueue.class);
if (state != null && state.currentPlayingIndex != -1) {
currentPlaying = state.songs.get(state.currentPlayingIndex);
}
} catch (Exception e) {
Log.e(TAG, "Failed to grab current playing", e);
}
} else {
currentPlaying = service.getCurrentPlaying() == null ? null : service.getCurrentPlaying().getSong();
}
String title = currentPlaying == null ? null : currentPlaying.getTitle();
CharSequence artist = currentPlaying == null ? null : currentPlaying.getArtist();
CharSequence album = currentPlaying == null ? null : currentPlaying.getAlbum();
CharSequence errorState = null;
// Show error message?
String status = Environment.getExternalStorageState();
if (status.equals(Environment.MEDIA_SHARED) ||
status.equals(Environment.MEDIA_UNMOUNTED)) {
errorState = res.getText(R.string.widget_sdcard_busy);
} else if (status.equals(Environment.MEDIA_REMOVED)) {
errorState = res.getText(R.string.widget_sdcard_missing);
} else if (currentPlaying == null) {
errorState = res.getText(R.string.widget_initial_text);
}
if (errorState != null) {
// Show error state to user
views.setTextViewText(R.id.title, null);
views.setTextViewText(R.id.artist, errorState);
views.setTextViewText(R.id.album, "");
if (getLayout() != R.layout.appwidget4x1) {
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.appwidget_art_default);
}
} else {
// No error, so show normal titles
views.setTextViewText(R.id.title, title);
views.setTextViewText(R.id.artist, artist);
if (getLayout() != R.layout.appwidget4x1) {
views.setTextViewText(R.id.album, album);
}
}
// Set correct drawable for pause state
if (playing) {
views.setImageViewResource(R.id.control_play, R.drawable.widget_media_pause);
} else {
views.setImageViewResource(R.id.control_play, R.drawable.widget_media_start);
}
// Set next/back button images
views.setImageViewResource(R.id.control_next, R.drawable.widget_media_forward);
views.setImageViewResource(R.id.control_previous, R.drawable.widget_media_backward);
// Set the cover art
try {
boolean large = false;
if (getLayout() != R.layout.appwidget4x1 && getLayout() != R.layout.appwidget4x2) {
large = true;
}
ImageLoader imageLoader = SubsonicActivity.getStaticImageLoader(context);
Bitmap bitmap = imageLoader == null ? null : imageLoader.getCachedImage(context, currentPlaying, large);
if (bitmap == null) {
// Set default cover art
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.appwidget_art_unknown);
} else {
bitmap = getRoundedCornerBitmap(bitmap);
views.setImageViewBitmap(R.id.appwidget_coverart, bitmap);
}
} catch (Exception x) {
Log.e(TAG, "Failed to load cover art", x);
views.setImageViewResource(R.id.appwidget_coverart, R.drawable.appwidget_art_unknown);
}
// Link actions buttons to intents
linkButtons(context, views);
pushUpdate(context, appWidgetIds, views);
}
/**
* Link up various button actions using {@link PendingIntent}.
*
* @param playerActive @param playerActive True if player is active in background. Launch {@link net.nullsum.audinaut.activity.SubsonicFragmentActivity}.
*/
private void linkButtons(Context context, RemoteViews views, boolean playerActive) {
private void linkButtons(Context context, RemoteViews views) {
Intent intent = new Intent(context, SubsonicFragmentActivity.class);
intent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, true);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
@ -287,19 +291,28 @@ public class AudinautWidgetProvider extends AppWidgetProvider {
intent = new Intent("Audinaut.PLAY_PAUSE");
intent.setComponent(new ComponentName(context, DownloadService.class));
intent.setAction(DownloadService.CMD_TOGGLEPAUSE);
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
if (Build.VERSION.SDK_INT >= 26)
pendingIntent = PendingIntent.getForegroundService(context, 0, intent, 0);
else
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.control_play, pendingIntent);
intent = new Intent("Audinaut.NEXT"); // Use a unique action name to ensure a different PendingIntent to be created.
intent.setComponent(new ComponentName(context, DownloadService.class));
intent.setAction(DownloadService.CMD_NEXT);
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
if (Build.VERSION.SDK_INT >= 26)
pendingIntent = PendingIntent.getForegroundService(context, 0, intent, 0);
else
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.control_next, pendingIntent);
intent = new Intent("Audinaut.PREVIOUS"); // Use a unique action name to ensure a different PendingIntent to be created.
intent.setComponent(new ComponentName(context, DownloadService.class));
intent.setAction(DownloadService.CMD_PREVIOUS);
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
if (Build.VERSION.SDK_INT >= 26)
pendingIntent = PendingIntent.getForegroundService(context, 0, intent, 0);
else
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.control_previous, pendingIntent);
}
}

View File

@ -4,11 +4,12 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import net.nullsum.audinaut.service.DownloadService;
public class A2dpIntentReceiver extends BroadcastReceiver {
private static final String PLAYSTATUS_RESPONSE = "com.android.music.playstatusresponse";
private String TAG = A2dpIntentReceiver.class.getSimpleName();
private final String TAG = A2dpIntentReceiver.class.getSimpleName();
@Override
public void onReceive(Context context, Intent intent) {
@ -16,7 +17,7 @@ public class A2dpIntentReceiver extends BroadcastReceiver {
DownloadService downloadService = DownloadService.getInstance();
if (downloadService != null){
if (downloadService != null) {
Intent avrcpIntent = new Intent(PLAYSTATUS_RESPONSE);
@ -24,7 +25,7 @@ public class A2dpIntentReceiver extends BroadcastReceiver {
avrcpIntent.putExtra("position", (long) downloadService.getPlayerPosition());
avrcpIntent.putExtra("ListSize", (long) downloadService.getSongs().size());
switch (downloadService.getPlayerState()){
switch (downloadService.getPlayerState()) {
case STARTED:
avrcpIntent.putExtra("playing", true);
break;

View File

@ -20,7 +20,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.util.Log;
import net.nullsum.audinaut.domain.PlayerState;
import net.nullsum.audinaut.service.DownloadService;
@ -28,24 +27,23 @@ import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.Util;
public class AudioNoisyReceiver extends BroadcastReceiver {
private static final String TAG = AudioNoisyReceiver.class.getSimpleName();
@Override
public void onReceive(Context context, Intent intent) {
DownloadService downloadService = DownloadService.getInstance();
// Don't do anything if downloadService is not started
if(downloadService == null) {
if (downloadService == null) {
return;
}
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals (intent.getAction ())) {
if((downloadService.getPlayerState() == PlayerState.STARTED || downloadService.getPlayerState() == PlayerState.PAUSED_TEMP)) {
SharedPreferences prefs = Util.getPreferences(downloadService);
int pausePref = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_PAUSE_DISCONNECT, "0"));
if(pausePref == 0) {
downloadService.pause();
}
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
if ((downloadService.getPlayerState() == PlayerState.STARTED || downloadService.getPlayerState() == PlayerState.PAUSED_TEMP)) {
SharedPreferences prefs = Util.getPreferences(downloadService);
int pausePref = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_PAUSE_DISCONNECT, "0"));
if (pausePref == 0) {
downloadService.pause();
}
}
}
}
}

View File

@ -22,10 +22,10 @@ import android.content.Intent;
import net.nullsum.audinaut.service.HeadphoneListenerService;
import net.nullsum.audinaut.util.Util;
public class BootReceiver extends BroadcastReceiver {
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(Util.shouldStartOnHeadphones(context)) {
if (Util.shouldStartOnHeadphones(context)) {
Intent serviceIntent = new Intent();
serviceIntent.setClassName(context.getPackageName(), HeadphoneListenerService.class.getName());
context.startService(serviceIntent);

View File

@ -18,19 +18,17 @@ package net.nullsum.audinaut.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.util.Util;
public class HeadphonePlugReceiver extends BroadcastReceiver {
private static final String TAG = HeadphonePlugReceiver.class.getSimpleName();
@Override
public void onReceive(Context context, Intent intent) {
if(Intent.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
if (Intent.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
int headphoneState = intent.getIntExtra("state", -1);
if(headphoneState == 1 && Util.shouldStartOnHeadphones(context)) {
if (headphoneState == 1 && Util.shouldStartOnHeadphones(context)) {
Intent start = new Intent(context, DownloadService.class);
start.setAction(DownloadService.START_PLAY);
context.startService(start);

View File

@ -36,8 +36,8 @@ public class MediaButtonIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
KeyEvent event = (KeyEvent) intent.getExtras().get(Intent.EXTRA_KEY_EVENT);
if(DownloadService.getInstance() == null && (event.getKeyCode() == KeyEvent.KEYCODE_MEDIA_STOP ||
event.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) {
if (DownloadService.getInstance() == null && (event.getKeyCode() == KeyEvent.KEYCODE_MEDIA_STOP ||
event.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) {
Log.w(TAG, "Ignore keycode event because downloadService is off");
return;
}

View File

@ -19,28 +19,35 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.util.Constants;
public class PlayActionReceiver extends BroadcastReceiver {
private static final String TAG = PlayActionReceiver.class.getSimpleName();
private Bundle lastdata = null;
@Override
public void onReceive(Context context, Intent intent) {
if(intent.hasExtra(Constants.TASKER_EXTRA_BUNDLE)) {
Bundle data = intent.getBundleExtra(Constants.TASKER_EXTRA_BUNDLE);
Boolean startShuffled = data.getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE);
if (lastdata.equals(intent.getBundleExtra(Constants.TASKER_EXTRA_BUNDLE))) {
// nothing has changed; we can safely return
return;
}
updateValues(intent);
if (intent.hasExtra(Constants.TASKER_EXTRA_BUNDLE)) {
Boolean startShuffled = lastdata.getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE);
Intent start = new Intent(context, DownloadService.class);
start.setAction(DownloadService.START_PLAY);
start.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, startShuffled);
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, data.getString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR));
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, data.getString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR));
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, data.getString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE));
start.putExtra(Constants.PREFERENCES_KEY_OFFLINE, data.getInt(Constants.PREFERENCES_KEY_OFFLINE));
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, lastdata.getString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR));
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, lastdata.getString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR));
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, lastdata.getString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE));
start.putExtra(Constants.PREFERENCES_KEY_OFFLINE, lastdata.getInt(Constants.PREFERENCES_KEY_OFFLINE));
context.startService(start);
}
}
private void updateValues(Intent intent) {
lastdata = intent.getBundleExtra(Constants.TASKER_EXTRA_BUNDLE);
}
}

View File

@ -18,17 +18,6 @@
*/
package net.nullsum.audinaut.service;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.TimeUnit;
import okhttp3.Response;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
@ -36,41 +25,42 @@ import android.util.Log;
import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.domain.Genre;
import net.nullsum.audinaut.domain.Indexes;
import net.nullsum.audinaut.domain.PlayerQueue;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.MusicFolder;
import net.nullsum.audinaut.domain.Playlist;
import net.nullsum.audinaut.domain.SearchCritera;
import net.nullsum.audinaut.domain.SearchResult;
import net.nullsum.audinaut.domain.User;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.FileUtil;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.SongDBHandler;
import net.nullsum.audinaut.util.TimeLimitedCache;
import net.nullsum.audinaut.util.FileUtil;
import net.nullsum.audinaut.util.Util;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import okhttp3.Response;
import static net.nullsum.audinaut.domain.MusicDirectory.Entry;
/**
* @author Sindre Mehus
*/
public class CachedMusicService implements MusicService {
public static final int CACHE_UPDATE_LIST = 1;
private static final int CACHE_UPDATE_METADATA = 2;
private static final String TAG = CachedMusicService.class.getSimpleName();
private static final int MUSIC_DIR_CACHE_SIZE = 20;
private static final int TTL_MUSIC_DIR = 5 * 60; // Five minutes
public static final int CACHE_UPDATE_LIST = 1;
public static final int CACHE_UPDATE_METADATA = 2;
private static final int CACHED_LAST_FM = 24 * 60;
private final RESTMusicService musicService;
private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<Indexes>(60 * 60, TimeUnit.SECONDS);
private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<List<Playlist>>(3600, TimeUnit.SECONDS);
private final TimeLimitedCache<List<MusicFolder>> cachedMusicFolders = new TimeLimitedCache<List<MusicFolder>>(10 * 3600, TimeUnit.SECONDS);
private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<>(60 * 60);
private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<>(3600);
private final TimeLimitedCache<List<MusicFolder>> cachedMusicFolders = new TimeLimitedCache<>(10 * 3600);
private String restUrl;
private String musicFolderId;
private boolean isTagBrowsing = false;
public CachedMusicService(RESTMusicService musicService) {
this.musicService = musicService;
@ -90,13 +80,13 @@ public class CachedMusicService implements MusicService {
}
List<MusicFolder> result = cachedMusicFolders.get();
if (result == null) {
if(!refresh) {
if (!refresh) {
result = FileUtil.deserialize(context, getCacheName(context, "musicFolders"), ArrayList.class);
}
if(result == null) {
if (result == null) {
result = musicService.getMusicFolders(refresh, context, progressListener);
FileUtil.serialize(context, new ArrayList<MusicFolder>(result), getCacheName(context, "musicFolders"));
FileUtil.serialize(context, new ArrayList<>(result), getCacheName(context, "musicFolders"));
}
MusicFolder.sort(result);
@ -114,13 +104,13 @@ public class CachedMusicService implements MusicService {
}
Indexes result = cachedIndexes.get();
if (result == null) {
String name = Util.isTagBrowsing(context, musicService.getInstance(context)) ? "artists" : "indexes";
String name = "artists";
name = getCacheName(context, name, musicFolderId);
if(!refresh) {
if (!refresh) {
result = FileUtil.deserialize(context, name, Indexes.class);
}
if(result == null) {
if (result == null) {
result = musicService.getIndexes(musicFolderId, refresh, context, progressListener);
FileUtil.serialize(context, result, name);
}
@ -133,7 +123,7 @@ public class CachedMusicService implements MusicService {
public MusicDirectory getMusicDirectory(final String id, final String name, final boolean refresh, final Context context, final ProgressListener progressListener) throws Exception {
MusicDirectory dir = null;
final MusicDirectory cached = FileUtil.deserialize(context, getCacheName(context, "directory", id), MusicDirectory.class);
if(!refresh && cached != null) {
if (!refresh && cached != null) {
dir = cached;
new SilentBackgroundTask<Void>(context) {
@ -153,11 +143,11 @@ public class CachedMusicService implements MusicService {
// Update which entries exist
@Override
public void done(Void result) {
if(progressListener != null) {
if(cached.updateEntriesList(context, musicService.getInstance(context), refreshed)) {
if (progressListener != null) {
if (cached.updateEntriesList(context, refreshed)) {
progressListener.updateCache(CACHE_UPDATE_LIST);
}
if(metadataUpdated) {
if (metadataUpdated) {
progressListener.updateCache(CACHE_UPDATE_METADATA);
}
}
@ -170,7 +160,7 @@ public class CachedMusicService implements MusicService {
}.execute();
}
if(dir == null) {
if (dir == null) {
dir = musicService.getMusicDirectory(id, name, refresh, context, progressListener);
updateAllSongs(context, dir);
FileUtil.serialize(context, dir, getCacheName(context, "directory", id));
@ -178,7 +168,7 @@ public class CachedMusicService implements MusicService {
// If a cached copy exists to check against, look for removes
deleteRemovedEntries(context, dir, cached);
}
dir.sortChildren(context, musicService.getInstance(context));
dir.sortChildren(context);
return dir;
}
@ -187,7 +177,7 @@ public class CachedMusicService implements MusicService {
public MusicDirectory getArtist(final String id, final String name, final boolean refresh, final Context context, final ProgressListener progressListener) throws Exception {
MusicDirectory dir = null;
final MusicDirectory cached = FileUtil.deserialize(context, getCacheName(context, "artist", id), MusicDirectory.class);
if(!refresh && cached != null) {
if (!refresh && cached != null) {
dir = cached;
new SilentBackgroundTask<Void>(context) {
@ -195,7 +185,7 @@ public class CachedMusicService implements MusicService {
@Override
protected Void doInBackground() throws Throwable {
refreshed = musicService.getArtist(id, name, refresh, context, null);
refreshed = musicService.getArtist(id, name, false, context, null);
cached.updateMetadata(refreshed);
deleteRemovedEntries(context, refreshed, cached);
FileUtil.serialize(context, refreshed, getCacheName(context, "artist", id));
@ -205,8 +195,8 @@ public class CachedMusicService implements MusicService {
// Update which entries exist
@Override
public void done(Void result) {
if(progressListener != null) {
if(cached.updateEntriesList(context, musicService.getInstance(context), refreshed)) {
if (progressListener != null) {
if (cached.updateEntriesList(context, refreshed)) {
progressListener.updateCache(CACHE_UPDATE_LIST);
}
}
@ -219,14 +209,14 @@ public class CachedMusicService implements MusicService {
}.execute();
}
if(dir == null) {
if (dir == null) {
dir = musicService.getArtist(id, name, refresh, context, progressListener);
FileUtil.serialize(context, dir, getCacheName(context, "artist", id));
// If a cached copy exists to check against, look for removes
deleteRemovedEntries(context, dir, cached);
}
dir.sortChildren(context, musicService.getInstance(context));
dir.sortChildren(context);
return dir;
}
@ -235,7 +225,7 @@ public class CachedMusicService implements MusicService {
public MusicDirectory getAlbum(final String id, final String name, final boolean refresh, final Context context, final ProgressListener progressListener) throws Exception {
MusicDirectory dir = null;
final MusicDirectory cached = FileUtil.deserialize(context, getCacheName(context, "album", id), MusicDirectory.class);
if(!refresh && cached != null) {
if (!refresh && cached != null) {
dir = cached;
new SilentBackgroundTask<Void>(context) {
@ -244,7 +234,7 @@ public class CachedMusicService implements MusicService {
@Override
protected Void doInBackground() throws Throwable {
refreshed = musicService.getAlbum(id, name, refresh, context, null);
refreshed = musicService.getAlbum(id, name, false, context, null);
updateAllSongs(context, refreshed);
metadataUpdated = cached.updateMetadata(refreshed);
deleteRemovedEntries(context, refreshed, cached);
@ -255,11 +245,11 @@ public class CachedMusicService implements MusicService {
// Update which entries exist
@Override
public void done(Void result) {
if(progressListener != null) {
if(cached.updateEntriesList(context, musicService.getInstance(context), refreshed)) {
if (progressListener != null) {
if (cached.updateEntriesList(context, refreshed)) {
progressListener.updateCache(CACHE_UPDATE_LIST);
}
if(metadataUpdated) {
if (metadataUpdated) {
progressListener.updateCache(CACHE_UPDATE_METADATA);
}
}
@ -272,7 +262,7 @@ public class CachedMusicService implements MusicService {
}.execute();
}
if(dir == null) {
if (dir == null) {
dir = musicService.getAlbum(id, name, refresh, context, progressListener);
updateAllSongs(context, dir);
FileUtil.serialize(context, dir, getCacheName(context, "album", id));
@ -280,7 +270,7 @@ public class CachedMusicService implements MusicService {
// If a cached copy exists to check against, look for removes
deleteRemovedEntries(context, dir, cached);
}
dir.sortChildren(context, musicService.getInstance(context));
dir.sortChildren(context);
return dir;
}
@ -294,16 +284,16 @@ public class CachedMusicService implements MusicService {
public MusicDirectory getPlaylist(boolean refresh, String id, String name, Context context, ProgressListener progressListener) throws Exception {
MusicDirectory dir = null;
MusicDirectory cachedPlaylist = FileUtil.deserialize(context, getCacheName(context, "playlist", id), MusicDirectory.class);
if(!refresh) {
if (!refresh) {
dir = cachedPlaylist;
}
if(dir == null) {
if (dir == null) {
dir = musicService.getPlaylist(refresh, id, name, context, progressListener);
updateAllSongs(context, dir);
FileUtil.serialize(context, dir, getCacheName(context, "playlist", id));
File playlistFile = FileUtil.getPlaylistFile(context, Util.getServerName(context, musicService.getInstance(context)), dir.getName());
if(cachedPlaylist == null || !playlistFile.exists() || !cachedPlaylist.getChildren().equals(dir.getChildren())) {
if (cachedPlaylist == null || !playlistFile.exists() || !cachedPlaylist.getChildren().equals(dir.getChildren())) {
FileUtil.writePlaylistFile(context, playlistFile, dir);
}
}
@ -315,13 +305,13 @@ public class CachedMusicService implements MusicService {
checkSettingsChanged(context);
List<Playlist> result = refresh ? null : cachedPlaylists.get();
if (result == null) {
if(!refresh) {
if (!refresh) {
result = FileUtil.deserialize(context, getCacheName(context, "playlist"), ArrayList.class);
}
if(result == null) {
if (result == null) {
result = musicService.getPlaylists(refresh, context, progressListener);
FileUtil.serialize(context, new ArrayList<Playlist>(result), getCacheName(context, "playlist"));
FileUtil.serialize(context, new ArrayList<>(result), getCacheName(context, "playlist"));
}
cachedPlaylists.set(result);
}
@ -377,17 +367,9 @@ public class CachedMusicService implements MusicService {
@Override
public void updateResult(List<Entry> objects, Entry result) {
// Make sure this playlist is supposed to be synced
boolean supposedToUnpin = false;
// Remove in reverse order so indexes are still correct as we iterate through
for(ListIterator<Integer> iterator = toRemove.listIterator(toRemove.size()); iterator.hasPrevious(); ) {
for (ListIterator<Integer> iterator = toRemove.listIterator(toRemove.size()); iterator.hasPrevious(); ) {
int index = iterator.previous();
if(supposedToUnpin) {
Entry entry = objects.get(index);
DownloadFile file = new DownloadFile(context, entry, true);
file.unpin();
}
objects.remove(index);
}
@ -439,24 +421,17 @@ public class CachedMusicService implements MusicService {
String recentlyAddedFile = getCacheName(context, type);
ArrayList<String> recents = FileUtil.deserialize(context, recentlyAddedFile, ArrayList.class);
if (recents == null) {
recents = new ArrayList<String>();
recents = new ArrayList<>();
}
// Add any new items
final int instance = musicService.getInstance(context);
isTagBrowsing = Util.isTagBrowsing(context, instance);
for (final Entry album : dir.getChildren()) {
if (!recents.contains(album.getId())) {
recents.add(album.getId());
String cacheName, parent;
if (isTagBrowsing) {
cacheName = "artist";
parent = album.getArtistId();
} else {
cacheName = "directory";
parent = album.getParent();
}
cacheName = "artist";
parent = album.getArtistId();
// Add album to artist
if (parent != null) {
@ -492,7 +467,7 @@ public class CachedMusicService implements MusicService {
artist.setId(album.getId());
artist.setName(album.getTitle());
new IndexesUpdater(context, isTagBrowsing ? "artists" : "indexes") {
new IndexesUpdater(context) {
private boolean changed = false;
@Override
@ -530,17 +505,17 @@ public class CachedMusicService implements MusicService {
FileUtil.serialize(context, dir, getCacheName(context, type, Integer.toString(offset)));
return dir;
} catch(IOException e) {
} catch (IOException e) {
Log.w(TAG, "Failed to refresh album list: ", e);
if(refresh) {
if (refresh) {
throw e;
}
MusicDirectory dir = FileUtil.deserialize(context, getCacheName(context, type, Integer.toString(offset)), MusicDirectory.class);
if(dir == null) {
if (dir == null) {
// If we are at start and no cache, throw error higher
if(offset == 0) {
if (offset == 0) {
throw e;
} else {
// Otherwise just pretend we are at the end of the list
@ -558,17 +533,17 @@ public class CachedMusicService implements MusicService {
MusicDirectory dir = musicService.getAlbumList(type, extra, size, offset, refresh, context, progressListener);
FileUtil.serialize(context, dir, getCacheName(context, type + extra, Integer.toString(offset)));
return dir;
} catch(IOException e) {
} catch (IOException e) {
Log.w(TAG, "Failed to refresh album list: ", e);
if(refresh) {
if (refresh) {
throw e;
}
MusicDirectory dir = FileUtil.deserialize(context, getCacheName(context, type + extra, Integer.toString(offset)), MusicDirectory.class);
if(dir == null) {
if (dir == null) {
// If we are at start and no cache, throw error higher
if(offset == 0) {
if (offset == 0) {
throw e;
} else {
// Otherwise just pretend we are at the end of the list
@ -585,11 +560,6 @@ public class CachedMusicService implements MusicService {
return musicService.getSongList(type, size, offset, context, progressListener);
}
@Override
public MusicDirectory getRandomSongs(int size, String artistId, Context context, ProgressListener progressListener) throws Exception {
return musicService.getRandomSongs(size, artistId, context, progressListener);
}
@Override
public MusicDirectory getRandomSongs(int size, String folder, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception {
return musicService.getRandomSongs(size, folder, genre, startYear, endYear, context, progressListener);
@ -609,13 +579,13 @@ public class CachedMusicService implements MusicService {
public List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
List<Genre> result = null;
if(!refresh) {
if (!refresh) {
result = FileUtil.deserialize(context, getCacheName(context, "genre"), ArrayList.class);
}
if(result == null) {
if (result == null) {
result = musicService.getGenres(refresh, context, progressListener);
FileUtil.serialize(context, new ArrayList<Genre>(result), getCacheName(context, "genre"));
FileUtil.serialize(context, new ArrayList<>(result), getCacheName(context, "genre"));
}
return result;
@ -628,12 +598,12 @@ public class CachedMusicService implements MusicService {
FileUtil.serialize(context, dir, getCacheName(context, "genreSongs", Integer.toString(offset)));
return dir;
} catch(IOException e) {
} catch (IOException e) {
MusicDirectory dir = FileUtil.deserialize(context, getCacheName(context, "genreSongs", Integer.toString(offset)), MusicDirectory.class);
if(dir == null) {
if (dir == null) {
// If we are at start and no cache, throw error higher
if(offset == 0) {
if (offset == 0) {
throw e;
} else {
// Otherwise just pretend we are at the end of the list
@ -646,98 +616,88 @@ public class CachedMusicService implements MusicService {
}
@Override
public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception {
User result = null;
try {
result = musicService.getUser(refresh, username, context, progressListener);
FileUtil.serialize(context, result, getCacheName(context, "user-" + username));
} catch(Exception e) {
// Don't care
}
if(result == null && !refresh) {
result = FileUtil.deserialize(context, getCacheName(context, "user-" + username), User.class);
}
return result;
public void startScan(Context c) throws Exception {
musicService.startScan(c);
}
@Override
public Bitmap getBitmap(String url, int size, Context context, ProgressListener progressListener, SilentBackgroundTask task) throws Exception {
return musicService.getBitmap(url, size, context, progressListener, task);
}
@Override
public void savePlayQueue(List<Entry> songs, Entry currentPlaying, int position, Context context, ProgressListener progressListener) throws Exception {
musicService.savePlayQueue(songs, currentPlaying, position, context, progressListener);
}
@Override
public PlayerQueue getPlayQueue(Context context, ProgressListener progressListener) throws Exception {
return musicService.getPlayQueue(context, progressListener);
}
@Override
public void setInstance(Integer instance) throws Exception {
public void setInstance(Integer instance) {
musicService.setInstance(instance);
}
private String getCacheName(Context context, String name, String id) {
String s = musicService.getRestUrl(context, null, false) + id;
String s = musicService.getRestUrl(context, null, false, null) + id;
return name + "-" + s.hashCode() + ".ser";
}
private String getCacheName(Context context, String name) {
String s = musicService.getRestUrl(context, null, false);
String s = musicService.getRestUrl(context, null, false, null);
return name + "-" + s.hashCode() + ".ser";
}
private void deleteRemovedEntries(Context context, MusicDirectory dir, MusicDirectory cached) {
if(cached != null) {
List<Entry> oldList = new ArrayList<Entry>();
oldList.addAll(cached.getChildren());
// Remove all current items from old list
for(Entry entry: dir.getChildren()) {
oldList.remove(entry);
}
if (cached != null) {
List<Entry> oldList = new ArrayList<>(cached.getChildren());
oldList.removeAll(dir.getChildren());
// Anything remaining has been removed from server
MediaStoreService store = new MediaStoreService(context);
for(Entry entry: oldList) {
for (Entry entry : oldList) {
File file = FileUtil.getEntryFile(context, entry);
FileUtil.recursiveDelete(file, store);
}
}
}
private void updateAllSongs(Context context, MusicDirectory dir) {
List<Entry> songs = dir.getSongs();
if (!songs.isEmpty()) {
SongDBHandler.getHandler(context).addSongs(musicService.getInstance(context), songs);
}
}
private void checkSettingsChanged(Context context) {
int instance = musicService.getInstance(context);
String newUrl = musicService.getRestUrl(context, null, false, null);
if (!Util.equals(newUrl, restUrl)) {
cachedMusicFolders.clear();
cachedIndexes.clear();
cachedPlaylists.clear();
restUrl = newUrl;
}
String newMusicFolderId = Util.getSelectedMusicFolderId(context, instance);
if (!Util.equals(newMusicFolderId, musicFolderId)) {
cachedIndexes.clear();
musicFolderId = newMusicFolderId;
}
}
private abstract class SerializeUpdater<T> {
final Context context;
final String cacheName;
final boolean singleUpdate;
public SerializeUpdater(Context context, String cacheName) {
this(context, cacheName, true);
}
public SerializeUpdater(Context context, String cacheName, boolean singleUpdate) {
public SerializeUpdater(Context context) {
this.context = context;
this.cacheName = getCacheName(context, cacheName);
this.singleUpdate = singleUpdate;
this.cacheName = getCacheName(context, "playlist");
this.singleUpdate = true;
}
public SerializeUpdater(Context context, String cacheName, String id) {
this(context, cacheName, id, true);
}
public SerializeUpdater(Context context, String cacheName, String id, boolean singleUpdate) {
this.context = context;
this.cacheName = getCacheName(context, cacheName, id);
this.singleUpdate = singleUpdate;
this.singleUpdate = true;
}
public ArrayList<T> getArrayList() {
return FileUtil.deserialize(context, cacheName, ArrayList.class);
}
public abstract boolean checkResult(T check);
public abstract void updateResult(List<T> objects, T result);
public void save(ArrayList<T> objects) {
FileUtil.serialize(context, objects, cacheName);
}
@ -746,47 +706,35 @@ public class CachedMusicService implements MusicService {
ArrayList<T> objects = getArrayList();
// Only execute if something to check against
if(objects != null) {
List<T> results = new ArrayList<T>();
for(T check: objects) {
if(checkResult(check)) {
if (objects != null) {
List<T> results = new ArrayList<>();
for (T check : objects) {
if (checkResult(check)) {
results.add(check);
if(singleUpdate) {
if (singleUpdate) {
break;
}
}
}
// Iterate through and update each object matched
for(T result: results) {
for (T result : results) {
updateResult(objects, result);
}
// Only reserialize if at least one match was found
if(results.size() > 0) {
if (results.size() > 0) {
save(objects);
}
}
}
}
private abstract class UserUpdater extends SerializeUpdater<User> {
String username;
public UserUpdater(Context context, String username) {
super(context, "users");
this.username = username;
}
@Override
public boolean checkResult(User check) {
return username.equals(check.getUsername());
}
}
private abstract class PlaylistUpdater extends SerializeUpdater<Playlist> {
String id;
final String id;
public PlaylistUpdater(Context context, String id) {
super(context, "playlist");
super(context);
this.id = id;
}
@ -795,199 +743,45 @@ public class CachedMusicService implements MusicService {
return id.equals(check.getId());
}
}
private abstract class MusicDirectoryUpdater extends SerializeUpdater<Entry> {
protected MusicDirectory musicDirectory;
MusicDirectory musicDirectory;
public MusicDirectoryUpdater(Context context, String cacheName, String id) {
super(context, cacheName, id, true);
}
public MusicDirectoryUpdater(Context context, String cacheName, String id, boolean singleUpdate) {
super(context, cacheName, id, singleUpdate);
super(context, cacheName, id);
}
@Override
public ArrayList<Entry> getArrayList() {
musicDirectory = FileUtil.deserialize(context, cacheName, MusicDirectory.class);
if(musicDirectory != null) {
if (musicDirectory != null) {
return new ArrayList<>(musicDirectory.getChildren());
} else {
return null;
}
}
public void save(ArrayList<Entry> objects) {
musicDirectory.replaceChildren(objects);
FileUtil.serialize(context, musicDirectory, cacheName);
}
}
private abstract class PlaylistDirectoryUpdater {
Context context;
public PlaylistDirectoryUpdater(Context context) {
this.context = context;
}
public abstract boolean checkResult(Entry check);
public abstract void updateResult(Entry result);
public void execute() {
List<Playlist> playlists = FileUtil.deserialize(context, getCacheName(context, "playlist"), ArrayList.class);
if(playlists == null) {
// No playlist list cache, nothing to update!
return;
}
for(Playlist playlist: playlists) {
new MusicDirectoryUpdater(context, "playlist", playlist.getId(), false) {
@Override
public boolean checkResult(Entry check) {
return PlaylistDirectoryUpdater.this.checkResult(check);
}
@Override
public void updateResult(List<Entry> objects, Entry result) {
PlaylistDirectoryUpdater.this.updateResult(result);
}
}.execute();
}
}
}
private abstract class GenericEntryUpdater {
Context context;
List<Entry> entries;
public GenericEntryUpdater(Context context, Entry entry) {
this.context = context;
this.entries = Arrays.asList(entry);
}
public GenericEntryUpdater(Context context, List<Entry> entries) {
this.context = context;
this.entries = entries;
}
public boolean checkResult(Entry entry, Entry check) {
return entry.getId().equals(check.getId());
}
public abstract void updateResult(Entry result);
public void execute() {
String cacheName, parent;
// Make sure it is up to date
isTagBrowsing = Util.isTagBrowsing(context, musicService.getInstance(context));
// Run through each entry, trying to update the directory it is in
final List<Entry> songs = new ArrayList<Entry>();
for(final Entry entry: entries) {
if(isTagBrowsing) {
// If starring album, needs to reference artist instead
if(entry.isDirectory()) {
if(entry.isAlbum()) {
cacheName = "artist";
parent = entry.getArtistId();
} else {
cacheName = "artists";
parent = null;
}
} else {
cacheName = "album";
parent = entry.getAlbumId();
}
} else {
if(entry.isDirectory() && !entry.isAlbum()) {
cacheName = "indexes";
parent = null;
} else {
cacheName = "directory";
parent = entry.getParent();
}
}
// Parent is only null when it is an artist
if(parent == null) {
new IndexesUpdater(context, cacheName) {
@Override
public boolean checkResult(Artist check) {
return GenericEntryUpdater.this.checkResult(entry, new Entry(check));
}
@Override
public void updateResult(List<Artist> objects, Artist result) {
// Don't try to put anything here, as the Entry update method will not be called since it's a artist!
}
}.execute();
} else {
new MusicDirectoryUpdater(context, cacheName, parent) {
@Override
public boolean checkResult(Entry check) {
return GenericEntryUpdater.this.checkResult(entry, check);
}
@Override
public void updateResult(List<Entry> objects, Entry result) {
GenericEntryUpdater.this.updateResult(result);
}
}.execute();
}
songs.add(entry);
}
// Only run through playlists once and check each song against it
if(songs.size() > 0) {
new PlaylistDirectoryUpdater(context) {
@Override
public boolean checkResult(Entry check) {
for(Entry entry: songs) {
if(GenericEntryUpdater.this.checkResult(entry, check)) {
return true;
}
}
return false;
}
@Override
public void updateResult(Entry result) {
GenericEntryUpdater.this.updateResult(result);
}
}.execute();
}
}
}
private class StarUpdater extends GenericEntryUpdater {
public StarUpdater(Context context, List<Entry> entries) {
super(context, entries);
}
@Override
public boolean checkResult(Entry entry, Entry check) {
if (!entry.getId().equals(check.getId())) {
return false;
}
return true;
}
@Override
public void updateResult(Entry result) {
}
};
private abstract class IndexesUpdater extends SerializeUpdater<Artist> {
Indexes indexes;
IndexesUpdater(Context context, String name) {
super(context, name, Util.getSelectedMusicFolderId(context, musicService.getInstance(context)));
IndexesUpdater(Context context) {
super(context, "artists", Util.getSelectedMusicFolderId(context, musicService.getInstance(context)));
}
@Override
public ArrayList<Artist> getArrayList() {
indexes = FileUtil.deserialize(context, cacheName, Indexes.class);
if(indexes == null) {
if (indexes == null) {
return null;
}
ArrayList<Artist> artists = new ArrayList<Artist>();
ArrayList<Artist> artists = new ArrayList<>();
artists.addAll(indexes.getArtists());
artists.addAll(indexes.getShortcuts());
return artists;
@ -999,34 +793,4 @@ public class CachedMusicService implements MusicService {
cachedIndexes.set(indexes);
}
}
protected void updateAllSongs(Context context, MusicDirectory dir) {
List<Entry> songs = dir.getSongs();
if(!songs.isEmpty()) {
SongDBHandler.getHandler(context).addSongs(musicService.getInstance(context), songs);
}
}
private void checkSettingsChanged(Context context) {
int instance = musicService.getInstance(context);
String newUrl = musicService.getRestUrl(context, null, false);
boolean newIsTagBrowsing = Util.isTagBrowsing(context, instance);
if (!Util.equals(newUrl, restUrl) || isTagBrowsing != newIsTagBrowsing) {
cachedMusicFolders.clear();
cachedIndexes.clear();
cachedPlaylists.clear();
restUrl = newUrl;
isTagBrowsing = newIsTagBrowsing;
}
String newMusicFolderId = Util.getSelectedMusicFolderId(context, instance);
if(!Util.equals(newMusicFolderId, musicFolderId)) {
cachedIndexes.clear();
musicFolderId = newMusicFolderId;
}
}
public RESTMusicService getMusicService() {
return musicService;
}
}

View File

@ -18,13 +18,6 @@
*/
package net.nullsum.audinaut.service;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
import android.net.wifi.WifiManager;
import android.os.PowerManager;
@ -38,6 +31,13 @@ import net.nullsum.audinaut.util.FileUtil;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.Util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import okhttp3.Response;
/**
@ -54,16 +54,15 @@ public class DownloadFile implements BufferFile {
private final File saveFile;
private final MediaStoreService mediaStoreService;
private final boolean save;
private final Long contentLength = null;
private DownloadTask downloadTask;
private boolean save;
private boolean failedDownload = false;
private int failed = 0;
private int bitRate;
private boolean isPlaying = false;
private boolean saveWhenDone = false;
private boolean completeWhenDone = false;
private Long contentLength = null;
private long currentSpeed = 0;
private boolean rateLimit = false;
public DownloadFile(Context context, MusicDirectory.Entry song, boolean save) {
@ -82,19 +81,16 @@ public class DownloadFile implements BufferFile {
public MusicDirectory.Entry getSong() {
return song;
}
public boolean isSong() {
return song.isSong();
}
public Context getContext() {
return context;
}
/**
* Returns the effective bit rate.
*/
public int getBitRate() {
if(!partialFile.exists()) {
if (!partialFile.exists()) {
bitRate = getActualBitrate();
}
if (bitRate > 0) {
@ -102,17 +98,18 @@ public class DownloadFile implements BufferFile {
}
return song.getBitRate() == null ? 160 : song.getBitRate();
}
private int getActualBitrate() {
int br = Util.getMaxBitrate(context);
if(br == 0 && song.getTranscodedSuffix() != null && "mp3".equals(song.getTranscodedSuffix().toLowerCase())) {
if(song.getBitRate() != null) {
if (br == 0 && song.getTranscodedSuffix() != null && "mp3".equals(song.getTranscodedSuffix().toLowerCase())) {
if (song.getBitRate() != null) {
br = Math.min(320, song.getBitRate());
} else {
br = 320;
}
} else if(song.getSuffix() != null && (song.getTranscodedSuffix() == null || song.getSuffix().equals(song.getTranscodedSuffix()))) {
} else if (song.getSuffix() != null && (song.getTranscodedSuffix() == null || song.getSuffix().equals(song.getTranscodedSuffix()))) {
// If just downsampling, don't try to upsample (ie: 128 kpbs -> 192 kpbs)
if(song.getBitRate() != null && (br == 0 || br > song.getBitRate())) {
if (song.getBitRate() != null && (br == 0 || br > song.getBitRate())) {
br = song.getBitRate();
}
}
@ -124,29 +121,16 @@ public class DownloadFile implements BufferFile {
return contentLength;
}
public long getCurrentSize() {
if(partialFile.exists()) {
return partialFile.length();
} else {
File file = getCompleteFile();
if(file.exists()) {
return file.length();
} else {
return 0L;
}
}
}
@Override
public long getEstimatedSize() {
if(contentLength != null) {
if (contentLength != null) {
return contentLength;
}
File file = getCompleteFile();
if(file.exists()) {
if (file.exists()) {
return file.length();
} else if(song.getDuration() == null) {
} else if (song.getDuration() == null) {
return 0;
} else {
int br = (getBitRate() * 1000) / 8;
@ -155,29 +139,16 @@ public class DownloadFile implements BufferFile {
}
}
public long getBytesPerSecond() {
return currentSpeed;
}
public synchronized void download() {
rateLimit = false;
preDownload();
downloadTask.execute();
}
public synchronized void downloadNow(MusicService musicService) {
rateLimit = true;
preDownload();
downloadTask.setMusicService(musicService);
try {
downloadTask.doInBackground();
} catch(InterruptedException e) {
// This should never be reached
}
}
private void preDownload() {
FileUtil.createDirectoryForParent(saveFile);
failedDownload = false;
if(!partialFile.exists()) {
if (!partialFile.exists()) {
bitRate = getActualBitrate();
}
downloadTask = new DownloadTask(context);
@ -211,9 +182,6 @@ public class DownloadFile implements BufferFile {
return saveFile;
}
public File getSaveFile() {
return saveFile;
}
public File getPartialFile() {
return partialFile;
@ -244,7 +212,7 @@ public class DownloadFile implements BufferFile {
@Override
public synchronized void onResume() {
if(!isWorkDone() && !isFailedMax() && !isDownloading() && !isDownloadCancelled()) {
if (!isWorkDone() && !isFailedMax() && !isDownloading() && !isDownloadCancelled()) {
download();
}
}
@ -264,6 +232,7 @@ public class DownloadFile implements BufferFile {
public boolean isFailed() {
return failedDownload;
}
public boolean isFailedMax() {
return failed > MAX_FAILURES;
}
@ -317,60 +286,52 @@ public class DownloadFile implements BufferFile {
}
}
public void renamePartial() {
Util.renameFile(partialFile, completeFile);
saveToStore();
}
public void setPlaying(boolean isPlaying) {
try {
if(saveWhenDone && !isPlaying) {
Util.renameFile(completeFile, saveFile);
renameInStore(completeFile, saveFile);
saveWhenDone = false;
} else if(completeWhenDone && !isPlaying) {
if(save) {
Util.renameFile(partialFile, saveFile);
saveToStore();
} else {
Util.renameFile(partialFile, completeFile);
saveToStore();
}
completeWhenDone = false;
if (saveWhenDone && !isPlaying) {
Util.renameFile(completeFile, saveFile);
renameInStore(completeFile, saveFile);
saveWhenDone = false;
} else if (completeWhenDone && !isPlaying) {
if (save) {
Util.renameFile(partialFile, saveFile);
saveToStore();
} else {
Util.renameFile(partialFile, completeFile);
saveToStore();
}
} catch(IOException ex) {
Log.w(TAG, "Failed to rename file " + completeFile + " to " + saveFile, ex);
completeWhenDone = false;
}
this.isPlaying = isPlaying;
}
public void renamePartial() {
try {
Util.renameFile(partialFile, completeFile);
saveToStore();
} catch(IOException ex) {
Log.w(TAG, "Failed to rename file " + partialFile + " to " + completeFile, ex);
}
}
public boolean getPlaying() {
return isPlaying;
}
private void deleteFromStore() {
try {
mediaStoreService.deleteFromMediaStore(this);
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to remove from store", e);
}
}
private void saveToStore() {
if(!Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_HIDE_MEDIA, false)) {
if (!Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_HIDE_MEDIA, false)) {
try {
mediaStoreService.saveInMediaStore(this);
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to save in media store", e);
}
}
}
private void renameInStore(File start, File end) {
try {
mediaStoreService.renameInMediaStore(start, end);
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to rename in store", e);
}
}
@ -425,7 +386,7 @@ public class DownloadFile implements BufferFile {
}
if (completeFile.exists()) {
if (save) {
if(isPlaying) {
if (isPlaying) {
saveWhenDone = true;
} else {
Util.renameFile(completeFile, saveFile);
@ -438,7 +399,7 @@ public class DownloadFile implements BufferFile {
return null;
}
if(musicService == null) {
if (musicService == null) {
musicService = MusicServiceFactory.getMusicService(context);
}
@ -446,15 +407,14 @@ public class DownloadFile implements BufferFile {
boolean compare;
try {
compare = (bitRate == 0) || (song.getDuration() == 0) || (partialFile.length() == 0) || (bitRate * song.getDuration() * 1000 / 8) > partialFile.length();
} catch(Exception e) {
} catch (Exception e) {
compare = true;
}
if(compare) {
if (compare) {
// Attempt partial HTTP GET, appending to the file if it exists.
Response response = musicService.getDownloadInputStream(context, song, partialFile.length(), bitRate, DownloadTask.this);
if(response.header("Content-Length") != null) {
if (response.header("Content-Length") != null) {
Log.i(TAG, "Content Length: " + contentLength);
contentLength = contentLength;
}
boolean partial = response.code() == 206;
@ -472,17 +432,17 @@ public class DownloadFile implements BufferFile {
if (isCancelled()) {
throw new Exception("Download of '" + song + "' was cancelled");
} else if(partialFile.length() == 0) {
} else if (partialFile.length() == 0) {
throw new Exception("Download of '" + song + "' failed. File is 0 bytes long.");
}
downloadAndSaveCoverArt(musicService);
}
if(isPlaying) {
if (isPlaying) {
completeWhenDone = true;
} else {
if(save) {
if (save) {
Util.renameFile(partialFile, saveFile);
} else {
Util.renameFile(partialFile, completeFile);
@ -490,20 +450,20 @@ public class DownloadFile implements BufferFile {
DownloadFile.this.saveToStore();
}
} catch(InterruptedException x) {
} catch (InterruptedException x) {
throw x;
} catch(FileNotFoundException x) {
} catch (FileNotFoundException x) {
Util.delete(completeFile);
Util.delete(saveFile);
if(!isCancelled()) {
if (!isCancelled()) {
failed = MAX_FAILURES + 1;
failedDownload = true;
Log.w(TAG, "Failed to download '" + song + "'.", x);
}
} catch(IOException x) {
} catch (IOException x) {
Util.delete(completeFile);
Util.delete(saveFile);
if(!isCancelled()) {
if (!isCancelled()) {
failedDownload = true;
Log.w(TAG, "Failed to download '" + song + "'.", x);
}
@ -529,7 +489,7 @@ public class DownloadFile implements BufferFile {
// Only run these if not interrupted, ie: cancelled
DownloadService downloadService = DownloadService.getInstance();
if(downloadService != null && !isCancelled()) {
if (downloadService != null && !isCancelled()) {
new CacheCleaner(context, downloadService).cleanSpace();
checkDownloads();
}
@ -539,7 +499,7 @@ public class DownloadFile implements BufferFile {
private void checkDownloads() {
DownloadService downloadService = DownloadService.getInstance();
if(downloadService != null) {
if (downloadService != null) {
downloadService.checkDownloads();
}
}
@ -549,16 +509,12 @@ public class DownloadFile implements BufferFile {
return "DownloadTask (" + song + ")";
}
public void setMusicService(MusicService musicService) {
this.musicService = musicService;
}
private void downloadAndSaveCoverArt(MusicService musicService) throws Exception {
private void downloadAndSaveCoverArt(MusicService musicService) {
try {
if (song.getCoverArt() != null) {
// Check if album art already exists, don't want to needlessly load into memory
File albumArtFile = FileUtil.getAlbumArtFile(context, song);
if(!albumArtFile.exists()) {
if (!albumArtFile.exists()) {
musicService.getCoverArt(context, song, 0, null, null);
}
}
@ -602,23 +558,18 @@ public class DownloadFile implements BufferFile {
long now = System.currentTimeMillis();
if (now - lastLog > 3000L) { // Only every so often.
Log.i(TAG, "Downloaded " + Util.formatBytes(count) + " of " + song);
currentSpeed = lastCount / ((now - lastLog) / 1000L);
lastLog = now;
lastCount = 0;
// Re-establish every few seconds whether screen is on or not
if(rateLimit) {
if (rateLimit) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if(pm.isScreenOn()) {
activeLimit = true;
} else {
activeLimit = false;
}
activeLimit = pm.isScreenOn();
}
}
// If screen is on and rateLimit is true, stop downloading from exhausting bandwidth
if(activeLimit) {
if (activeLimit) {
Thread.sleep(10L);
}
}

View File

@ -18,12 +18,6 @@
*/
package net.nullsum.audinaut.service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.atomic.AtomicBoolean;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -36,64 +30,67 @@ import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.KeyEvent;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.PlayerQueue;
import net.nullsum.audinaut.domain.PlayerState;
import net.nullsum.audinaut.util.CacheCleaner;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.FileUtil;
import net.nullsum.audinaut.util.Pair;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.SongDBHandler;
import net.nullsum.audinaut.util.Util;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import static net.nullsum.audinaut.domain.PlayerState.PREPARING;
/**
* @author Sindre Mehus
*/
public class DownloadServiceLifecycleSupport {
private static final String TAG = DownloadServiceLifecycleSupport.class.getSimpleName();
public static final String FILENAME_DOWNLOADS_SER = "downloadstate2.ser";
private static final String TAG = DownloadServiceLifecycleSupport.class.getSimpleName();
private static final int DEBOUNCE_TIME = 200;
private final DownloadService downloadService;
private final AtomicBoolean setup = new AtomicBoolean(false);
private final ReentrantLock lock = new ReentrantLock();
private Looper eventLooper;
private Handler eventHandler;
private BroadcastReceiver ejectEventReceiver;
private PhoneStateListener phoneStateListener;
private boolean externalStorageAvailable= true;
private ReentrantLock lock = new ReentrantLock();
private final AtomicBoolean setup = new AtomicBoolean(false);
private boolean externalStorageAvailable = true;
private long lastPressTime = 0;
private SilentBackgroundTask<Void> currentSavePlayQueueTask = null;
private Date lastChange = null;
/**
* This receiver manages the intent that could come from other applications.
*/
private BroadcastReceiver intentReceiver = new BroadcastReceiver() {
private final BroadcastReceiver intentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
eventHandler.post(new Runnable() {
@Override
public void run() {
String action = intent.getAction();
Log.i(TAG, "intentReceiver.onReceive: " + action);
if (DownloadService.CMD_PLAY.equals(action)) {
eventHandler.post(() -> {
String action = intent.getAction();
Log.i(TAG, "intentReceiver.onReceive: " + action);
switch (action) {
case DownloadService.CMD_PLAY:
downloadService.play();
} else if (DownloadService.CMD_NEXT.equals(action)) {
break;
case DownloadService.CMD_NEXT:
downloadService.next();
} else if (DownloadService.CMD_PREVIOUS.equals(action)) {
break;
case DownloadService.CMD_PREVIOUS:
downloadService.previous();
} else if (DownloadService.CMD_TOGGLEPAUSE.equals(action)) {
break;
case DownloadService.CMD_TOGGLEPAUSE:
downloadService.togglePlayPause();
} else if (DownloadService.CMD_PAUSE.equals(action)) {
break;
case DownloadService.CMD_PAUSE:
downloadService.pause();
} else if (DownloadService.CMD_STOP.equals(action)) {
break;
case DownloadService.CMD_STOP:
downloadService.pause();
downloadService.seekTo(0);
}
break;
}
});
}
@ -105,30 +102,27 @@ public class DownloadServiceLifecycleSupport {
}
public void onCreate() {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
eventLooper = Looper.myLooper();
eventHandler = new Handler(eventLooper);
new Thread(() -> {
Looper.prepare();
eventLooper = Looper.myLooper();
eventHandler = new Handler(eventLooper);
// Deserialize queue before starting looper
try {
lock.lock();
deserializeDownloadQueueNow();
// Deserialize queue before starting looper
try {
lock.lock();
deserializeDownloadQueueNow();
// Wait until PREPARING is done to mark lifecycle as ready to receive events
while(downloadService.getPlayerState() == PREPARING) {
Util.sleepQuietly(50L);
}
setup.set(true);
} finally {
lock.unlock();
// Wait until PREPARING is done to mark lifecycle as ready to receive events
while (downloadService.getPlayerState() == PREPARING) {
Util.sleepQuietly(50L);
}
Looper.loop();
setup.set(true);
} finally {
lock.unlock();
}
Looper.loop();
}, "DownloadServiceLifecycleSupport").start();
// Stop when SD card is ejected.
@ -173,78 +167,71 @@ public class DownloadServiceLifecycleSupport {
new CacheCleaner(downloadService, downloadService).clean();
}
public boolean isInitialized() {
return setup.get();
}
public void onStart(final Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if(eventHandler == null) {
if (eventHandler == null) {
Util.sleepQuietly(100L);
}
if(eventHandler == null) {
if (eventHandler == null) {
return;
}
eventHandler.post(new Runnable() {
@Override
public void run() {
if(!setup.get()) {
lock.lock();
lock.unlock();
eventHandler.post(() -> {
if (!setup.get()) {
lock.lock();
lock.unlock();
}
if (DownloadService.START_PLAY.equals(action)) {
int offlinePref = intent.getIntExtra(Constants.PREFERENCES_KEY_OFFLINE, 0);
if (offlinePref != 0) {
boolean offline = (offlinePref == 2);
Util.setOffline(downloadService, offline);
if (offline) {
downloadService.clearIncomplete();
} else {
downloadService.checkDownloads();
}
}
if(DownloadService.START_PLAY.equals(action)) {
int offlinePref = intent.getIntExtra(Constants.PREFERENCES_KEY_OFFLINE, 0);
if(offlinePref != 0) {
boolean offline = (offlinePref == 2);
Util.setOffline(downloadService, offline);
if (offline) {
downloadService.clearIncomplete();
} else {
downloadService.checkDownloads();
}
if (intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, false)) {
// Add shuffle parameters
SharedPreferences.Editor editor = Util.getPreferences(downloadService).edit();
String startYear = intent.getStringExtra(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR);
if (startYear != null) {
editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, startYear);
}
if(intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, false)) {
// Add shuffle parameters
SharedPreferences.Editor editor = Util.getPreferences(downloadService).edit();
String startYear = intent.getStringExtra(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR);
if(startYear != null) {
editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, startYear);
}
String endYear = intent.getStringExtra(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR);
if(endYear != null) {
editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, endYear);
}
String genre = intent.getStringExtra(Constants.PREFERENCES_KEY_SHUFFLE_GENRE);
if(genre != null) {
editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, genre);
}
editor.apply();
downloadService.clear();
downloadService.setShufflePlayEnabled(true);
} else {
downloadService.start();
String endYear = intent.getStringExtra(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR);
if (endYear != null) {
editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, endYear);
}
} else if(DownloadService.CMD_TOGGLEPAUSE.equals(action)) {
downloadService.togglePlayPause();
} else if(DownloadService.CMD_NEXT.equals(action)) {
downloadService.next();
} else if(DownloadService.CMD_PREVIOUS.equals(action)) {
downloadService.previous();
} else if(DownloadService.CANCEL_DOWNLOADS.equals(action)) {
downloadService.clearBackground();
} else if(intent.getExtras() != null) {
final KeyEvent event = (KeyEvent) intent.getExtras().get(Intent.EXTRA_KEY_EVENT);
if (event != null) {
handleKeyEvent(event);
String genre = intent.getStringExtra(Constants.PREFERENCES_KEY_SHUFFLE_GENRE);
if (genre != null) {
editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, genre);
}
editor.apply();
downloadService.clear();
downloadService.setShufflePlayEnabled(true);
} else {
downloadService.start();
}
} else if (DownloadService.CMD_TOGGLEPAUSE.equals(action)) {
downloadService.togglePlayPause();
} else if (DownloadService.CMD_NEXT.equals(action)) {
downloadService.next();
} else if (DownloadService.CMD_PREVIOUS.equals(action)) {
downloadService.previous();
} else if (DownloadService.CANCEL_DOWNLOADS.equals(action)) {
downloadService.clearBackground();
} else if (intent.getExtras() != null) {
final KeyEvent event = (KeyEvent) intent.getExtras().get(Intent.EXTRA_KEY_EVENT);
if (event != null) {
handleKeyEvent(event);
}
}
});
@ -266,29 +253,23 @@ public class DownloadServiceLifecycleSupport {
}
public void serializeDownloadQueue() {
serializeDownloadQueue(true);
}
public void serializeDownloadQueue(final boolean serializeRemote) {
if(!setup.get()) {
if (!setup.get()) {
return;
}
final List<DownloadFile> songs = new ArrayList<DownloadFile>(downloadService.getSongs());
eventHandler.post(new Runnable() {
@Override
public void run() {
if(lock.tryLock()) {
try {
serializeDownloadQueueNow(songs, serializeRemote);
} finally {
lock.unlock();
}
final List<DownloadFile> songs = new ArrayList<>(downloadService.getSongs());
eventHandler.post(() -> {
if (lock.tryLock()) {
try {
serializeDownloadQueueNow(songs);
} finally {
lock.unlock();
}
}
});
}
public void serializeDownloadQueueNow(List<DownloadFile> songs, boolean serializeRemote) {
private void serializeDownloadQueueNow(List<DownloadFile> songs) {
final PlayerQueue state = new PlayerQueue();
for (DownloadFile downloadFile : songs) {
state.songs.add(downloadFile.getSong());
@ -300,11 +281,9 @@ public class DownloadServiceLifecycleSupport {
state.currentPlayingPosition = downloadService.getPlayerPosition();
DownloadFile currentPlaying = downloadService.getCurrentPlaying();
if(currentPlaying != null) {
if (currentPlaying != null) {
state.renameCurrent = currentPlaying.isWorkDone() && !currentPlaying.isCompleteFileAvailable();
}
state.changed = lastChange = new Date();
Log.i(TAG, "Serialized currentPlayingIndex: " + state.currentPlayingIndex + ", currentPlayingPosition: " + state.currentPlayingPosition);
FileUtil.serialize(downloadService, state, FILENAME_DOWNLOADS_SER);
}
@ -321,24 +300,16 @@ public class DownloadServiceLifecycleSupport {
Log.i(TAG, "Deserialized currentPlayingIndex: " + state.currentPlayingIndex + ", currentPlayingPosition: " + state.currentPlayingPosition);
// Rename first thing before anything else starts
if(state.renameCurrent && state.currentPlayingIndex != -1 && state.currentPlayingIndex < state.songs.size()) {
if (state.renameCurrent && state.currentPlayingIndex != -1 && state.currentPlayingIndex < state.songs.size()) {
DownloadFile currentPlaying = new DownloadFile(downloadService, state.songs.get(state.currentPlayingIndex), false);
currentPlaying.renamePartial();
}
downloadService.restore(state.songs, state.toDelete, state.currentPlayingIndex, state.currentPlayingPosition);
if(state != null) {
lastChange = state.changed;
}
}
public Date getLastChange() {
return lastChange;
}
public void handleKeyEvent(KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() > 0) {
private void handleKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() > 0) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
downloadService.fastForward();
@ -347,25 +318,25 @@ public class DownloadServiceLifecycleSupport {
downloadService.rewind();
break;
}
} else if(event.getAction() == KeyEvent.ACTION_UP) {
} else if (event.getAction() == KeyEvent.ACTION_UP) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
if(lastPressTime < (System.currentTimeMillis() - 500)) {
if (lastPressTime < (System.currentTimeMillis() - 500)) {
lastPressTime = System.currentTimeMillis();
downloadService.togglePlayPause();
} else {
downloadService.next(false, true);
downloadService.next(true);
}
break;
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
if(lastPressTime < (System.currentTimeMillis() - DEBOUNCE_TIME)) {
if (lastPressTime < (System.currentTimeMillis() - DEBOUNCE_TIME)) {
lastPressTime = System.currentTimeMillis();
downloadService.previous();
}
break;
case KeyEvent.KEYCODE_MEDIA_NEXT:
if(lastPressTime < (System.currentTimeMillis() - DEBOUNCE_TIME)) {
if (lastPressTime < (System.currentTimeMillis() - DEBOUNCE_TIME)) {
lastPressTime = System.currentTimeMillis();
downloadService.next();
}
@ -380,7 +351,7 @@ public class DownloadServiceLifecycleSupport {
downloadService.stop();
break;
case KeyEvent.KEYCODE_MEDIA_PLAY:
if(downloadService.getPlayerState() != PlayerState.STARTED) {
if (downloadService.getPlayerState() != PlayerState.STARTED) {
downloadService.start();
}
break;
@ -401,28 +372,25 @@ public class DownloadServiceLifecycleSupport {
@Override
public void onCallStateChanged(final int state, String incomingNumber) {
eventHandler.post(new Runnable() {
@Override
public void run() {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
case TelephonyManager.CALL_STATE_OFFHOOK:
if (downloadService.getPlayerState() == PlayerState.STARTED) {
resumeAfterCall = true;
downloadService.pause(true);
eventHandler.post(() -> {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
case TelephonyManager.CALL_STATE_OFFHOOK:
if (downloadService.getPlayerState() == PlayerState.STARTED) {
resumeAfterCall = true;
downloadService.pause(true);
}
break;
case TelephonyManager.CALL_STATE_IDLE:
if (resumeAfterCall) {
resumeAfterCall = false;
if (downloadService.getPlayerState() == PlayerState.PAUSED_TEMP) {
downloadService.start();
}
break;
case TelephonyManager.CALL_STATE_IDLE:
if (resumeAfterCall) {
resumeAfterCall = false;
if(downloadService.getPlayerState() == PlayerState.PAUSED_TEMP) {
downloadService.start();
}
}
break;
default:
break;
}
}
break;
default:
break;
}
});
}

View File

@ -44,7 +44,7 @@ public class HeadphoneListenerService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(!Util.shouldStartOnHeadphones(this)) {
if (!Util.shouldStartOnHeadphones(this)) {
stopSelf();
}
@ -56,10 +56,10 @@ public class HeadphoneListenerService extends Service {
super.onDestroy();
try {
if(receiver != null) {
if (receiver != null) {
unregisterReceiver(receiver);
}
} catch(Exception e) {
} catch (Exception e) {
// Don't care
}
}

View File

@ -18,8 +18,6 @@
*/
package net.nullsum.audinaut.service;
import java.io.File;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@ -27,9 +25,11 @@ import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Log;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.util.FileUtil;
import net.nullsum.audinaut.util.Util;
import java.io.File;
/**
* @author Sindre Mehus
@ -67,7 +67,7 @@ public class MediaStoreService {
if (song.getYear() != null) {
values.put(MediaStore.Audio.AudioColumns.YEAR, song.getYear());
}
if(song.getTranscodedContentType() != null) {
if (song.getTranscodedContentType() != null) {
values.put(MediaStore.MediaColumns.MIME_TYPE, song.getTranscodedContentType());
} else {
values.put(MediaStore.MediaColumns.MIME_TYPE, song.getContentType());

View File

@ -18,24 +18,22 @@
*/
package net.nullsum.audinaut.service;
import java.util.List;
import okhttp3.Response;
import android.content.Context;
import android.graphics.Bitmap;
import net.nullsum.audinaut.domain.Genre;
import net.nullsum.audinaut.domain.Indexes;
import net.nullsum.audinaut.domain.PlayerQueue;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.MusicFolder;
import net.nullsum.audinaut.domain.Playlist;
import net.nullsum.audinaut.domain.SearchCritera;
import net.nullsum.audinaut.domain.SearchResult;
import net.nullsum.audinaut.domain.User;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import java.util.List;
import okhttp3.Response;
/**
* @author Sindre Mehus
@ -78,7 +76,6 @@ public interface MusicService {
MusicDirectory getSongList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception;
MusicDirectory getRandomSongs(int size, String artistId, Context context, ProgressListener progressListener) throws Exception;
MusicDirectory getRandomSongs(int size, String folder, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception;
Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) throws Exception;
@ -89,13 +86,7 @@ public interface MusicService {
MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception;
User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception;
Bitmap getBitmap(String url, int size, Context context, ProgressListener progressListener, SilentBackgroundTask task) throws Exception;
void savePlayQueue(List<MusicDirectory.Entry> songs, MusicDirectory.Entry currentPlaying, int position, Context context, ProgressListener progressListener) throws Exception;
PlayerQueue getPlayQueue(Context context, ProgressListener progressListener) throws Exception;
void startScan(Context c) throws Exception;
void setInstance(Integer instance) throws Exception;
}

View File

@ -26,7 +26,7 @@ package net.nullsum.audinaut.service;
*/
public class OfflineException extends Exception {
public OfflineException(String message) {
super(message);
public OfflineException() {
super(OfflineMusicService.ERRORMSG);
}
}

View File

@ -18,9 +18,29 @@
*/
package net.nullsum.audinaut.service;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.domain.Genre;
import net.nullsum.audinaut.domain.Indexes;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.MusicDirectory.Entry;
import net.nullsum.audinaut.domain.MusicFolder;
import net.nullsum.audinaut.domain.Playlist;
import net.nullsum.audinaut.domain.SearchCritera;
import net.nullsum.audinaut.domain.SearchResult;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.FileUtil;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.Util;
import java.io.BufferedReader;
import java.io.File;
import java.io.Reader;
import java.io.FileReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@ -28,53 +48,28 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.util.Log;
import java.util.SortedSet;
import okhttp3.Response;
import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.domain.Genre;
import net.nullsum.audinaut.domain.Indexes;
import net.nullsum.audinaut.domain.MusicDirectory.Entry;
import net.nullsum.audinaut.domain.PlayerQueue;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.MusicFolder;
import net.nullsum.audinaut.domain.Playlist;
import net.nullsum.audinaut.domain.SearchCritera;
import net.nullsum.audinaut.domain.SearchResult;
import net.nullsum.audinaut.domain.User;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.FileUtil;
import net.nullsum.audinaut.util.Pair;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.SongDBHandler;
import net.nullsum.audinaut.util.Util;
import java.io.*;
import java.util.Comparator;
import java.util.SortedSet;
/**
* @author Sindre Mehus
*/
public class OfflineMusicService implements MusicService {
public static final String ERRORMSG = "Not available in offline mode";
private static final String TAG = OfflineMusicService.class.getSimpleName();
private static final String ERRORMSG = "Not available in offline mode";
private static final Random random = new Random();
@Override
public void ping(Context context, ProgressListener progressListener) throws Exception {
public void ping(Context context, ProgressListener progressListener) {
}
@Override
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
List<Artist> artists = new ArrayList<Artist>();
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) {
List<Artist> artists = new ArrayList<>();
List<Entry> entries = new ArrayList<>();
File root = FileUtil.getMusicDirectory(context);
for (File file : FileUtil.listFiles(root)) {
if (file.isDirectory()) {
@ -83,31 +78,31 @@ public class OfflineMusicService implements MusicService {
artist.setIndex(file.getName().substring(0, 1));
artist.setName(file.getName());
artists.add(artist);
} else if(!file.getName().equals("albumart.jpg") && !file.getName().equals(".nomedia")) {
} else if (!file.getName().equals("albumart.jpg") && !file.getName().equals(".nomedia")) {
entries.add(createEntry(context, file));
}
}
Indexes indexes = new Indexes(0L, Collections.<Artist>emptyList(), artists, entries);
return indexes;
return new Indexes(Collections.emptyList(), artists, entries);
}
@Override
public MusicDirectory getMusicDirectory(String id, String artistName, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
return getMusicDirectory(id, artistName, refresh, context, progressListener, false);
public MusicDirectory getMusicDirectory(String id, String artistName, boolean refresh, Context context, ProgressListener progressListener) {
return getMusicDirectory(id, context);
}
private MusicDirectory getMusicDirectory(String id, String artistName, boolean refresh, Context context, ProgressListener progressListener, boolean isPodcast) throws Exception {
private MusicDirectory getMusicDirectory(String id, Context context) {
File dir = new File(id);
MusicDirectory result = new MusicDirectory();
result.setName(dir.getName());
Set<String> names = new HashSet<String>();
Set<String> names = new HashSet<>();
for (File file : FileUtil.listMediaFiles(dir)) {
String name = getName(file);
if (name != null & !names.contains(name)) {
names.add(name);
result.addChild(createEntry(context, file, name, true, isPodcast));
result.addChild(createEntry(context, file, name, true));
}
}
result.sortChildren(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_CUSTOM_SORT_ENABLED, true));
@ -116,12 +111,12 @@ public class OfflineMusicService implements MusicService {
@Override
public MusicDirectory getArtist(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public MusicDirectory getAlbum(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
private String getName(File file) {
@ -141,29 +136,24 @@ public class OfflineMusicService implements MusicService {
private Entry createEntry(Context context, File file) {
return createEntry(context, file, getName(file));
}
private Entry createEntry(Context context, File file, String name) {
return createEntry(context, file, name, true);
}
private Entry createEntry(Context context, File file, String name, boolean load) {
return createEntry(context, file, name, load, false);
}
private Entry createEntry(Context context, File file, String name, boolean load, boolean isPodcast) {
Entry entry;
entry = new Entry();
entry.setDirectory(file.isDirectory());
entry.setId(file.getPath());
entry.setParent(file.getParent());
entry.setSize(file.length());
String root = FileUtil.getMusicDirectory(context).getPath();
if(!file.getParentFile().getParentFile().getPath().equals(root)) {
entry.setGrandParent(file.getParentFile().getParent());
}
entry.setPath(file.getPath().replaceFirst("^" + root + "/" , ""));
entry.setPath(file.getPath().replaceFirst("^" + root + "/", ""));
String title = name;
if (file.isFile()) {
File artistFolder = file.getParentFile().getParentFile();
File albumFolder = file.getParentFile();
if(artistFolder.getPath().equals(root)) {
if (artistFolder.getPath().equals(root)) {
entry.setArtist(albumFolder.getName());
} else {
entry.setArtist(artistFolder.getName());
@ -171,16 +161,16 @@ public class OfflineMusicService implements MusicService {
entry.setAlbum(albumFolder.getName());
int index = name.indexOf('-');
if(index != -1) {
if (index != -1) {
try {
entry.setTrack(Integer.parseInt(name.substring(0, index)));
title = title.substring(index + 1);
} catch(Exception e) {
} catch (Exception e) {
// Failed parseInt, just means track filled out
}
}
if(load) {
if (load) {
entry.loadMetadata(file);
}
}
@ -196,35 +186,35 @@ public class OfflineMusicService implements MusicService {
}
@Override
public Bitmap getCoverArt(Context context, Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) throws Exception {
public Bitmap getCoverArt(Context context, Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) {
try {
return FileUtil.getAlbumArtBitmap(context, entry, size);
} catch(Exception e) {
} catch (Exception e) {
return null;
}
}
@Override
public Response getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception {
List<Artist> artists = new ArrayList<Artist>();
List<Entry> albums = new ArrayList<Entry>();
List<Entry> songs = new ArrayList<Entry>();
public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) {
List<Artist> artists = new ArrayList<>();
List<Entry> albums = new ArrayList<>();
List<Entry> songs = new ArrayList<>();
File root = FileUtil.getMusicDirectory(context);
int closeness = 0;
int closeness;
for (File artistFile : FileUtil.listFiles(root)) {
String artistName = artistFile.getName();
if (artistFile.isDirectory()) {
if((closeness = matchCriteria(criteria, artistName)) > 0) {
if ((closeness = matchCriteria(criteria, artistName)) > 0) {
Artist artist = new Artist();
artist.setId(artistFile.getPath());
artist.setIndex(artistFile.getName().substring(0, 1));
@ -237,45 +227,9 @@ public class OfflineMusicService implements MusicService {
}
}
Collections.sort(artists, new Comparator<Artist>() {
public int compare(Artist lhs, Artist rhs) {
if(lhs.getCloseness() == rhs.getCloseness()) {
return 0;
}
else if(lhs.getCloseness() > rhs.getCloseness()) {
return -1;
}
else {
return 1;
}
}
});
Collections.sort(albums, new Comparator<Entry>() {
public int compare(Entry lhs, Entry rhs) {
if(lhs.getCloseness() == rhs.getCloseness()) {
return 0;
}
else if(lhs.getCloseness() > rhs.getCloseness()) {
return -1;
}
else {
return 1;
}
}
});
Collections.sort(songs, new Comparator<Entry>() {
public int compare(Entry lhs, Entry rhs) {
if(lhs.getCloseness() == rhs.getCloseness()) {
return 0;
}
else if(lhs.getCloseness() > rhs.getCloseness()) {
return -1;
}
else {
return 1;
}
}
});
Collections.sort(artists, (lhs, rhs) -> Integer.compare(rhs.getCloseness(), lhs.getCloseness()));
Collections.sort(albums, (lhs, rhs) -> Integer.compare(rhs.getCloseness(), lhs.getCloseness()));
Collections.sort(songs, (lhs, rhs) -> Integer.compare(rhs.getCloseness(), lhs.getCloseness()));
// Respect counts in search criteria
int artistCount = Math.min(artists.size(), criteria.getArtistCount());
@ -290,26 +244,25 @@ public class OfflineMusicService implements MusicService {
private void recursiveAlbumSearch(String artistName, File file, SearchCritera criteria, Context context, List<Entry> albums, List<Entry> songs) {
int closeness;
for(File albumFile : FileUtil.listMediaFiles(file)) {
if(albumFile.isDirectory()) {
for (File albumFile : FileUtil.listMediaFiles(file)) {
if (albumFile.isDirectory()) {
String albumName = getName(albumFile);
if((closeness = matchCriteria(criteria, albumName)) > 0) {
if ((closeness = matchCriteria(criteria, albumName)) > 0) {
Entry album = createEntry(context, albumFile, albumName);
album.setArtist(artistName);
album.setCloseness(closeness);
albums.add(album);
}
for(File songFile : FileUtil.listMediaFiles(albumFile)) {
for (File songFile : FileUtil.listMediaFiles(albumFile)) {
String songName = getName(songFile);
if(songName == null) {
if (songName == null) {
continue;
}
if(songFile.isDirectory()) {
if (songFile.isDirectory()) {
recursiveAlbumSearch(artistName, songFile, criteria, context, albums, songs);
}
else if((closeness = matchCriteria(criteria, songName)) > 0){
} else if ((closeness = matchCriteria(criteria, songName)) > 0) {
Entry song = createEntry(context, albumFile, songName);
song.setArtist(artistName);
song.setAlbum(albumName);
@ -317,10 +270,9 @@ public class OfflineMusicService implements MusicService {
songs.add(song);
}
}
}
else {
} else {
String songName = getName(albumFile);
if((closeness = matchCriteria(criteria, songName)) > 0) {
if ((closeness = matchCriteria(criteria, songName)) > 0) {
Entry song = createEntry(context, albumFile, songName);
song.setArtist(artistName);
song.setAlbum(songName);
@ -330,28 +282,29 @@ public class OfflineMusicService implements MusicService {
}
}
}
private int matchCriteria(SearchCritera criteria, String name) {
if (criteria.getPattern().matcher(name).matches()) {
return Util.getStringDistance(
criteria.getQuery().toLowerCase(),
name.toLowerCase());
criteria.getQuery().toLowerCase(),
name.toLowerCase());
} else {
return 0;
}
}
@Override
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
List<Playlist> playlists = new ArrayList<Playlist>();
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) {
List<Playlist> playlists = new ArrayList<>();
File root = FileUtil.getPlaylistDirectory(context);
String lastServer = null;
boolean removeServer = true;
for (File folder : FileUtil.listFiles(root)) {
if(folder.isDirectory()) {
if (folder.isDirectory()) {
String server = folder.getName();
SortedSet<File> fileList = FileUtil.listFiles(folder);
for(File file: fileList) {
if(FileUtil.isPlaylistFile(file)) {
for (File file : fileList) {
if (FileUtil.isPlaylistFile(file)) {
String id = file.getName();
String filename = FileUtil.getBaseName(id);
String name = server + ": " + filename;
@ -366,41 +319,41 @@ public class OfflineMusicService implements MusicService {
buffer = new BufferedReader(reader);
String line = buffer.readLine();
while( (line = buffer.readLine()) != null ){
while ((line = buffer.readLine()) != null) {
// No matter what, end file can't have .complete in it
line = line.replace(".complete", "");
File entryFile = new File(line);
// Don't add file to playlist if it doesn't exist as cached or pinned!
File checkFile = entryFile;
if(!checkFile.exists()) {
if (!checkFile.exists()) {
// If normal file doens't exist, check if .complete version does
checkFile = new File(entryFile.getParent(), FileUtil.getBaseName(entryFile.getName())
+ ".complete." + FileUtil.getExtension(entryFile.getName()));
}
String entryName = getName(entryFile);
if(checkFile.exists() && entryName != null){
if (checkFile.exists() && entryName != null) {
songCount++;
}
}
playlist.setSongCount(Integer.toString(songCount));
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to count songs in playlist", e);
} finally {
Util.close(buffer);
Util.close(reader);
}
if(songCount > 0) {
if (songCount > 0) {
playlists.add(playlist);
}
}
}
if(!server.equals(lastServer) && fileList.size() > 0) {
if(lastServer != null) {
if (!server.equals(lastServer) && fileList.size() > 0) {
if (lastServer != null) {
removeServer = false;
}
lastServer = server;
@ -409,14 +362,14 @@ public class OfflineMusicService implements MusicService {
// Delete legacy playlist files
try {
folder.delete();
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to delete old playlist file: " + folder.getName());
}
}
}
if(removeServer) {
for(Playlist playlist: playlists) {
if (removeServer) {
for (Playlist playlist : playlists) {
playlist.setName(playlist.getName().substring(playlist.getId().length() + 2));
}
}
@ -434,7 +387,7 @@ public class OfflineMusicService implements MusicService {
BufferedReader buffer = null;
try {
int firstIndex = name.indexOf(id);
if(firstIndex != -1) {
if (firstIndex != -1) {
name = name.substring(id.length() + 2);
}
@ -444,23 +397,23 @@ public class OfflineMusicService implements MusicService {
MusicDirectory playlist = new MusicDirectory();
String line = buffer.readLine();
if(!"#EXTM3U".equals(line)) return playlist;
if (!"#EXTM3U".equals(line)) return playlist;
while( (line = buffer.readLine()) != null ){
while ((line = buffer.readLine()) != null) {
// No matter what, end file can't have .complete in it
line = line.replace(".complete", "");
File entryFile = new File(line);
// Don't add file to playlist if it doesn't exist as cached or pinned!
File checkFile = entryFile;
if(!checkFile.exists()) {
if (!checkFile.exists()) {
// If normal file doens't exist, check if .complete version does
checkFile = new File(entryFile.getParent(), FileUtil.getBaseName(entryFile.getName())
+ ".complete." + FileUtil.getExtension(entryFile.getName()));
+ ".complete." + FileUtil.getExtension(entryFile.getName()));
}
String entryName = getName(entryFile);
if(checkFile.exists() && entryName != null){
if (checkFile.exists() && entryName != null) {
playlist.addChild(createEntry(context, entryFile, entryName, false));
}
}
@ -474,68 +427,63 @@ public class OfflineMusicService implements MusicService {
@Override
public void createPlaylist(String id, String name, List<Entry> entries, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public void deletePlaylist(String id, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public void addToPlaylist(String id, List<Entry> toAdd, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public void removeFromPlaylist(String id, List<Integer> toRemove, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public void overwritePlaylist(String id, String name, int toRemove, List<Entry> toAdd, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public void updatePlaylist(String id, String name, String comment, boolean pub, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public MusicDirectory getAlbumList(String type, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public MusicDirectory getAlbumList(String type, String extra, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public MusicDirectory getSongList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
}
@Override
public MusicDirectory getRandomSongs(int size, String artistId, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
throw new OfflineException();
}
@Override
public MusicDirectory getRandomSongs(int size, String folder, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception {
public MusicDirectory getRandomSongs(int size, String folder, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) {
File root = FileUtil.getMusicDirectory(context);
List<File> children = new LinkedList<File>();
List<File> children = new LinkedList<>();
listFilesRecursively(root, children);
MusicDirectory result = new MusicDirectory();
@ -551,28 +499,13 @@ public class OfflineMusicService implements MusicService {
}
@Override
public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
public void startScan(Context c) throws Exception {
throw new OfflineException();
}
@Override
public Bitmap getBitmap(String url, int size, Context context, ProgressListener progressListener, SilentBackgroundTask task) throws Exception {
throw new OfflineException(ERRORMSG);
}
@Override
public void savePlayQueue(List<Entry> songs, Entry currentPlaying, int position, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
}
@Override
public PlayerQueue getPlayQueue(Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
}
@Override
public void setInstance(Integer instance) throws Exception{
throw new OfflineException(ERRORMSG);
public void setInstance(Integer instance) throws Exception {
throw new OfflineException();
}
private void listFilesRecursively(File parent, List<File> children) {

View File

@ -18,41 +18,41 @@
*/
package net.nullsum.audinaut.service.parser;
import java.io.IOException;
import java.io.InputStream;
import org.xmlpull.v1.XmlPullParser;
import android.content.Context;
import android.util.Log;
import android.util.Xml;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.Util;
import org.xmlpull.v1.XmlPullParser;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Sindre Mehus
*/
public abstract class AbstractParser {
abstract class AbstractParser {
private static final String TAG = AbstractParser.class.getSimpleName();
private static final String SUBSONIC_RESPONSE = "subsonic-response";
private static final String SUBSONIC = "subsonic";
protected final Context context;
protected final int instance;
final Context context;
private final int instance;
private XmlPullParser parser;
private boolean rootElementFound;
public AbstractParser(Context context, int instance) {
AbstractParser(Context context, int instance) {
this.context = context;
this.instance = instance;
}
protected Context getContext() {
Context getContext() {
return context;
}
protected void handleError() throws Exception {
void handleError() throws Exception {
int code = getInteger("code");
String message;
switch (code) {
@ -69,7 +69,7 @@ public abstract class AbstractParser {
message = context.getResources().getString(R.string.parser_not_authenticated);
break;
case 41:
Util.setBlockTokenUse(context, instance, true);
Util.setBlockTokenUse(context, instance);
// Throw IOException so RESTMusicService knows to retry
throw new IOException();
@ -80,77 +80,56 @@ public abstract class AbstractParser {
message = get("message");
break;
}
throw new SubsonicRESTException(code, message);
throw new SubsonicRESTException(message);
}
protected void updateProgress(ProgressListener progressListener, int messageId) {
if (progressListener != null) {
progressListener.updateProgress(messageId);
}
}
protected void updateProgress(ProgressListener progressListener, String message) {
void updateProgress(ProgressListener progressListener, String message) {
if (progressListener != null) {
progressListener.updateProgress(message);
}
}
protected String getText() {
String getText() {
return parser.getText();
}
protected String get(String name) {
String get(String name) {
return parser.getAttributeValue(null, name);
}
protected boolean getBoolean(String name) {
return "true".equals(get(name));
boolean getBoolean() {
return "true".equals(get("isDir"));
}
protected Integer getInteger(String name) {
Integer getInteger(String name) {
String s = get(name);
try {
return (s == null || "".equals(s)) ? null : Integer.valueOf(s);
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to parse " + s + " into integer");
return null;
}
}
protected Long getLong(String name) {
String s = get(name);
return s == null ? null : Long.valueOf(s);
}
protected Float getFloat(String name) {
String s = get(name);
return s == null ? null : Float.valueOf(s);
}
protected void init(InputStream inputStream) throws Exception {
void init(InputStream inputStream) throws Exception {
parser = Xml.newPullParser();
parser.setInput(inputStream, "UTF-8");
rootElementFound = false;
}
protected int nextParseEvent() throws Exception {
try {
return parser.next();
} catch(Exception e) {
throw e;
}
int nextParseEvent() throws Exception {
return parser.next();
}
protected String getElementName() {
String getElementName() {
String name = parser.getName();
if (SUBSONIC_RESPONSE.equals(name)) {
rootElementFound = true;
String version = get("version");
}
return name;
}
protected void validate() throws Exception {
void validate() throws Exception {
if (!rootElementFound) {
throw new Exception(context.getResources().getString(R.string.background_task_parse_error));
}

View File

@ -19,9 +19,9 @@
package net.nullsum.audinaut.service.parser;
import android.content.Context;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.util.ProgressListener;
import org.xmlpull.v1.XmlPullParser;
import java.io.InputStream;
@ -35,7 +35,7 @@ public class EntryListParser extends MusicDirectoryEntryParser {
super(context, instance);
}
public MusicDirectory parse(InputStream inputStream, ProgressListener progressListener) throws Exception {
public MusicDirectory parse(InputStream inputStream) throws Exception {
init(inputStream);
MusicDirectory dir = new MusicDirectory();
@ -44,17 +44,23 @@ public class EntryListParser extends MusicDirectoryEntryParser {
eventType = nextParseEvent();
if (eventType == XmlPullParser.START_TAG) {
String name = getElementName();
if ("album".equals(name)) {
MusicDirectory.Entry entry = parseEntry("");
if(get("isDir") == null) {
entry.setDirectory(true);
switch (name) {
case "album": {
MusicDirectory.Entry entry = parseEntry("");
if (get("isDir") == null) {
entry.setDirectory(true);
}
dir.addChild(entry);
break;
}
dir.addChild(entry);
} else if ("song".equals(name)) {
MusicDirectory.Entry entry = parseEntry("");
dir.addChild(entry);
} else if ("error".equals(name)) {
handleError();
case "song": {
MusicDirectory.Entry entry = parseEntry("");
dir.addChild(entry);
break;
}
case "error":
handleError();
break;
}
}
} while (eventType != XmlPullParser.END_DOCUMENT);

View File

@ -19,6 +19,7 @@
package net.nullsum.audinaut.service.parser;
import android.content.Context;
import org.xmlpull.v1.XmlPullParser;
import java.io.InputStream;

View File

@ -20,34 +20,29 @@ package net.nullsum.audinaut.service.parser;
import android.content.Context;
import android.text.Html;
import android.util.Log;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.Genre;
import net.nullsum.audinaut.util.ProgressListener;
import org.xmlpull.v1.XmlPullParser;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* @author Joshua Bahnsen
*/
public class GenreParser extends AbstractParser {
private static final String TAG = GenreParser.class.getSimpleName();
public GenreParser(Context context, int instance) {
super(context, instance);
}
public List<Genre> parse(InputStream inputStream, ProgressListener progressListener) throws Exception {
public List<Genre> parse(InputStream inputStream) throws Exception {
init(inputStream);
List<Genre> result = new ArrayList<Genre>();
List<Genre> result = new ArrayList<>();
Genre genre = null;
@ -56,36 +51,33 @@ public class GenreParser extends AbstractParser {
eventType = nextParseEvent();
if (eventType == XmlPullParser.START_TAG) {
String name = getElementName();
if ("genre".equals(name)) {
genre = new Genre();
genre.setSongCount(getInteger("songCount"));
genre.setAlbumCount(getInteger("albumCount"));
} else if ("error".equals(name)) {
handleError();
} else {
genre = null;
switch (name) {
case "genre":
genre = new Genre();
genre.setSongCount(getInteger("songCount"));
genre.setAlbumCount(getInteger("albumCount"));
break;
case "error":
handleError();
break;
default:
genre = null;
break;
}
} else if (eventType == XmlPullParser.TEXT) {
if (genre != null) {
String value = getText();
if (genre != null) {
genre.setName(Html.fromHtml(value).toString());
genre.setIndex(value.substring(0, 1));
result.add(genre);
genre = null;
}
genre.setName(Html.fromHtml(value).toString());
genre.setIndex(value.substring(0, 1));
result.add(genre);
genre = null;
}
}
} while (eventType != XmlPullParser.END_DOCUMENT);
validate();
Collections.sort(result, new Comparator<Genre>() {
@Override
public int compare(Genre genre1, Genre genre2) {
return genre1.getName().compareTo(genre2.getName());
}
});
Collections.sort(result, (genre1, genre2) -> genre1.getName().compareTo(genre2.getName()));
return result;
}
}

View File

@ -18,25 +18,26 @@
*/
package net.nullsum.audinaut.service.parser;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import org.xmlpull.v1.XmlPullParser;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.domain.Indexes;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.util.ProgressListener;
import android.util.Log;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.Util;
import org.xmlpull.v1.XmlPullParser;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Sindre Mehus
*/
@ -51,64 +52,70 @@ public class IndexesParser extends MusicDirectoryEntryParser {
long t0 = System.currentTimeMillis();
init(inputStream);
List<Artist> artists = new ArrayList<Artist>();
List<Artist> shortcuts = new ArrayList<Artist>();
List<MusicDirectory.Entry> entries = new ArrayList<MusicDirectory.Entry>();
Long lastModified = null;
List<Artist> artists = new ArrayList<>();
List<Artist> shortcuts = new ArrayList<>();
List<MusicDirectory.Entry> entries = new ArrayList<>();
int eventType;
String index = "#";
String ignoredArticles = null;
boolean changed = false;
Map<String, Artist> artistList = new HashMap<String, Artist>();
Map<String, Artist> artistList = new HashMap<>();
do {
eventType = nextParseEvent();
if (eventType == XmlPullParser.START_TAG) {
String name = getElementName();
if ("indexes".equals(name) || "artists".equals(name)) {
changed = true;
lastModified = getLong("lastModified");
ignoredArticles = get("ignoredArticles");
} else if ("index".equals(name)) {
index = get("name");
switch (name) {
case "indexes":
case "artists":
changed = true;
ignoredArticles = get("ignoredArticles");
break;
case "index":
index = get("name");
} else if ("artist".equals(name)) {
Artist artist = new Artist();
artist.setId(get("id"));
artist.setName(get("name"));
artist.setIndex(index);
break;
case "artist":
Artist artist = new Artist();
artist.setId(get("id"));
artist.setName(get("name"));
artist.setIndex(index);
// Combine the id's for the two artists
if(artistList.containsKey(artist.getName())) {
Artist originalArtist = artistList.get(artist.getName());
originalArtist.setId(originalArtist.getId() + ";" + artist.getId());
} else {
artistList.put(artist.getName(), artist);
artists.add(artist);
}
// Combine the id's for the two artists
if (artistList.containsKey(artist.getName())) {
Artist originalArtist = artistList.get(artist.getName());
originalArtist.setId(originalArtist.getId() + ";" + artist.getId());
} else {
artistList.put(artist.getName(), artist);
artists.add(artist);
}
if (artists.size() % 10 == 0) {
String msg = getContext().getResources().getString(R.string.parser_artist_count, artists.size());
updateProgress(progressListener, msg);
}
} else if ("shortcut".equals(name)) {
Artist shortcut = new Artist();
shortcut.setId(get("id"));
shortcut.setName(get("name"));
shortcut.setIndex("*");
shortcuts.add(shortcut);
} else if("child".equals(name)) {
MusicDirectory.Entry entry = parseEntry("");
entries.add(entry);
} else if ("error".equals(name)) {
handleError();
if (artists.size() % 10 == 0) {
String msg = getContext().getResources().getString(R.string.parser_artist_count, artists.size());
updateProgress(progressListener, msg);
}
break;
case "shortcut":
Artist shortcut = new Artist();
shortcut.setId(get("id"));
shortcut.setName(get("name"));
shortcut.setIndex("*");
shortcuts.add(shortcut);
break;
case "child":
MusicDirectory.Entry entry = parseEntry("");
entries.add(entry);
break;
case "error":
handleError();
break;
}
}
} while (eventType != XmlPullParser.END_DOCUMENT);
validate();
if(ignoredArticles != null) {
if (ignoredArticles != null) {
SharedPreferences.Editor prefs = Util.getPreferences(context).edit();
prefs.putString(Constants.CACHE_KEY_IGNORE, ignoredArticles);
prefs.apply();
@ -124,6 +131,6 @@ public class IndexesParser extends MusicDirectoryEntryParser {
String msg = getContext().getResources().getString(R.string.parser_artist_count, artists.size());
updateProgress(progressListener, msg);
return new Indexes(lastModified == null ? 0L : lastModified, shortcuts, artists, entries);
return new Indexes(shortcuts, artists, entries);
}
}

View File

@ -25,21 +25,21 @@ import net.nullsum.audinaut.domain.MusicDirectory;
/**
* @author Sindre Mehus
*/
public class MusicDirectoryEntryParser extends AbstractParser {
public MusicDirectoryEntryParser(Context context, int instance) {
class MusicDirectoryEntryParser extends AbstractParser {
MusicDirectoryEntryParser(Context context, int instance) {
super(context, instance);
}
protected MusicDirectory.Entry parseEntry(String artist) {
MusicDirectory.Entry parseEntry(String artist) {
MusicDirectory.Entry entry = new MusicDirectory.Entry();
entry.setId(get("id"));
entry.setParent(get("parent"));
entry.setArtistId(get("artistId"));
entry.setTitle(get("title"));
if(entry.getTitle() == null) {
if (entry.getTitle() == null) {
entry.setTitle(get("name"));
}
entry.setDirectory(getBoolean("isDir"));
entry.setDirectory(getBoolean());
entry.setCoverArt(get("coverArt"));
entry.setArtist(get("artist"));
entry.setYear(getInteger("year"));
@ -53,27 +53,14 @@ public class MusicDirectoryEntryParser extends AbstractParser {
entry.setSuffix(get("suffix"));
entry.setTranscodedContentType(get("transcodedContentType"));
entry.setTranscodedSuffix(get("transcodedSuffix"));
entry.setSize(getLong("size"));
entry.setDuration(getInteger("duration"));
entry.setBitRate(getInteger("bitRate"));
entry.setPath(get("path"));
entry.setDiscNumber(getInteger("discNumber"));
String type = get("type");
} else if(!"".equals(artist)) {
} else if (!"".equals(artist)) {
entry.setPath(artist + "/" + entry.getTitle());
}
return entry;
}
protected MusicDirectory.Entry parseArtist() {
MusicDirectory.Entry entry = new MusicDirectory.Entry();
entry.setId(get("id"));
entry.setTitle(get("name"));
entry.setPath(entry.getTitle());
entry.setDirectory(true);
return entry;
}
}

View File

@ -19,48 +19,42 @@
package net.nullsum.audinaut.service.parser;
import android.content.Context;
import android.util.Log;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.Util;
import org.xmlpull.v1.XmlPullParser;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import static net.nullsum.audinaut.domain.MusicDirectory.*;
import static net.nullsum.audinaut.domain.MusicDirectory.Entry;
/**
* @author Sindre Mehus
*/
public class MusicDirectoryParser extends MusicDirectoryEntryParser {
private static final String TAG = MusicDirectoryParser.class.getSimpleName();
public MusicDirectoryParser(Context context, int instance) {
super(context, instance);
}
public MusicDirectory parse(String artist, InputStream inputStream, ProgressListener progressListener) throws Exception {
public MusicDirectory parse(String artist, InputStream inputStream) throws Exception {
init(inputStream);
MusicDirectory dir = new MusicDirectory();
int eventType;
boolean isArtist = false;
Map<String, Entry> titleMap = new HashMap<String, Entry>();
Map<String, Entry> titleMap = new HashMap<>();
do {
eventType = nextParseEvent();
if (eventType == XmlPullParser.START_TAG) {
String name = getElementName();
if ("child".equals(name) || "song".equals(name)) {
Entry entry = parseEntry(artist);
entry.setGrandParent(dir.getParent());
// Only check for songs
if(!entry.isDirectory()) {
if (!entry.isDirectory()) {
// Check if duplicates
String disc = (entry.getDiscNumber() != null) ? Integer.toString(entry.getDiscNumber()) : "";
String track = (entry.getTrack() != null) ? Integer.toString(entry.getTrack()) : "";
@ -84,13 +78,9 @@ public class MusicDirectoryParser extends MusicDirectoryEntryParser {
} else if ("directory".equals(name) || "artist".equals(name) || ("album".equals(name) && !isArtist)) {
dir.setName(get("name"));
dir.setId(get("id"));
if(Util.isTagBrowsing(context, instance)) {
dir.setParent(get("artistId"));
} else {
dir.setParent(get("parent"));
}
dir.setParent(get("artistId"));
isArtist = true;
} else if("album".equals(name)) {
} else if ("album".equals(name)) {
Entry entry = parseEntry(artist);
entry.setDirectory(true);
dir.addChild(entry);

View File

@ -18,16 +18,15 @@
*/
package net.nullsum.audinaut.service.parser;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import net.nullsum.audinaut.domain.MusicFolder;
import org.xmlpull.v1.XmlPullParser;
import android.content.Context;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicFolder;
import net.nullsum.audinaut.util.ProgressListener;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* @author Sindre Mehus
@ -38,10 +37,10 @@ public class MusicFoldersParser extends AbstractParser {
super(context, instance);
}
public List<MusicFolder> parse(InputStream inputStream, ProgressListener progressListener) throws Exception {
public List<MusicFolder> parse(InputStream inputStream) throws Exception {
init(inputStream);
List<MusicFolder> result = new ArrayList<MusicFolder>();
List<MusicFolder> result = new ArrayList<>();
int eventType;
do {
eventType = nextParseEvent();

View File

@ -1,83 +0,0 @@
/*
This file is part of Subsonic.
Subsonic 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.
Subsonic 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.
You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2015 (C) Scott Jackson
*/
package net.nullsum.audinaut.service.parser;
import android.content.Context;
import org.xmlpull.v1.XmlPullParser;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.TimeZone;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.PlayerQueue;
import net.nullsum.audinaut.util.ProgressListener;
public class PlayQueueParser extends MusicDirectoryEntryParser {
private static final String TAG = PlayQueueParser.class.getSimpleName();
public PlayQueueParser(Context context, int instance) {
super(context, instance);
}
public PlayerQueue parse(InputStream inputStream, ProgressListener progressListener) throws Exception {
init(inputStream);
PlayerQueue state = new PlayerQueue();
String currentId = null;
int eventType;
do {
eventType = nextParseEvent();
if (eventType == XmlPullParser.START_TAG) {
String name = getElementName();
if("playQueue".equals(name)) {
currentId = get("current");
state.currentPlayingPosition = getInteger("position");
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
state.changed = dateFormat.parse(get("changed"));
} catch (ParseException e) {
state.changed = null;
}
} else if ("entry".equals(name)) {
MusicDirectory.Entry entry = parseEntry("");
// Only add songs
state.songs.add(entry);
} else if ("error".equals(name)) {
handleError();
}
}
} while (eventType != XmlPullParser.END_DOCUMENT);
if(currentId != null) {
for (MusicDirectory.Entry entry : state.songs) {
if (entry.getId().equals(currentId)) {
state.currentPlayingIndex = state.songs.indexOf(entry);
}
}
} else {
state.currentPlayingIndex = 0;
state.currentPlayingPosition = 0;
}
validate();
return state;
}
}

View File

@ -19,9 +19,9 @@
package net.nullsum.audinaut.service.parser;
import android.content.Context;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.util.ProgressListener;
import org.xmlpull.v1.XmlPullParser;
import java.io.InputStream;
@ -35,7 +35,7 @@ public class PlaylistParser extends MusicDirectoryEntryParser {
super(context, instance);
}
public MusicDirectory parse(InputStream inputStream, ProgressListener progressListener) throws Exception {
public MusicDirectory parse(InputStream inputStream) throws Exception {
init(inputStream);
MusicDirectory dir = new MusicDirectory();
@ -44,13 +44,17 @@ public class PlaylistParser extends MusicDirectoryEntryParser {
eventType = nextParseEvent();
if (eventType == XmlPullParser.START_TAG) {
String name = getElementName();
if ("entry".equals(name)) {
dir.addChild(parseEntry(""));
} else if ("error".equals(name)) {
handleError();
} else if ("playlist".equals(name)) {
dir.setName(get("name"));
dir.setId(get("id"));
switch (name) {
case "entry":
dir.addChild(parseEntry(""));
break;
case "error":
handleError();
break;
case "playlist":
dir.setName(get("name"));
dir.setId(get("id"));
break;
}
}
} while (eventType != XmlPullParser.END_DOCUMENT);

View File

@ -21,7 +21,7 @@ package net.nullsum.audinaut.service.parser;
import android.content.Context;
import net.nullsum.audinaut.domain.Playlist;
import net.nullsum.audinaut.util.ProgressListener;
import org.xmlpull.v1.XmlPullParser;
import java.io.InputStream;
@ -37,10 +37,10 @@ public class PlaylistsParser extends AbstractParser {
super(context, instance);
}
public List<Playlist> parse(InputStream inputStream, ProgressListener progressListener) throws Exception {
public List<Playlist> parse(InputStream inputStream) throws Exception {
init(inputStream);
List<Playlist> result = new ArrayList<Playlist>();
List<Playlist> result = new ArrayList<>();
int eventType;
do {
eventType = nextParseEvent();

View File

@ -19,9 +19,9 @@
package net.nullsum.audinaut.service.parser;
import android.content.Context;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.util.ProgressListener;
import org.xmlpull.v1.XmlPullParser;
import java.io.InputStream;
@ -35,7 +35,7 @@ public class RandomSongsParser extends MusicDirectoryEntryParser {
super(context, instance);
}
public MusicDirectory parse(InputStream inputStream, ProgressListener progressListener) throws Exception {
public MusicDirectory parse(InputStream inputStream) throws Exception {
init(inputStream);
MusicDirectory dir = new MusicDirectory();

View File

@ -19,16 +19,16 @@
package net.nullsum.audinaut.service.parser;
import android.content.Context;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.SearchResult;
import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.util.ProgressListener;
import org.xmlpull.v1.XmlPullParser;
import java.io.InputStream;
import java.util.List;
import java.util.ArrayList;
import java.util.List;
/**
* @author Sindre Mehus
@ -39,30 +39,35 @@ public class SearchResult2Parser extends MusicDirectoryEntryParser {
super(context, instance);
}
public SearchResult parse(InputStream inputStream, ProgressListener progressListener) throws Exception {
public SearchResult parse(InputStream inputStream) throws Exception {
init(inputStream);
List<Artist> artists = new ArrayList<Artist>();
List<MusicDirectory.Entry> albums = new ArrayList<MusicDirectory.Entry>();
List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>();
List<Artist> artists = new ArrayList<>();
List<MusicDirectory.Entry> albums = new ArrayList<>();
List<MusicDirectory.Entry> songs = new ArrayList<>();
int eventType;
do {
eventType = nextParseEvent();
if (eventType == XmlPullParser.START_TAG) {
String name = getElementName();
if ("artist".equals(name)) {
Artist artist = new Artist();
artist.setId(get("id"));
artist.setName(get("name"));
artists.add(artist);
} else if ("album".equals(name)) {
MusicDirectory.Entry entry = parseEntry("");
entry.setDirectory(true);
albums.add(entry);
} else if ("song".equals(name)) {
songs.add(parseEntry(""));
} else if ("error".equals(name)) {
handleError();
switch (name) {
case "artist":
Artist artist = new Artist();
artist.setId(get("id"));
artist.setName(get("name"));
artists.add(artist);
break;
case "album":
MusicDirectory.Entry entry = parseEntry("");
entry.setDirectory(true);
albums.add(entry);
break;
case "song":
songs.add(parseEntry(""));
break;
case "error":
handleError();
break;
}
}
} while (eventType != XmlPullParser.END_DOCUMENT);

View File

@ -4,16 +4,10 @@ package net.nullsum.audinaut.service.parser;
* @author Sindre Mehus
* @version $Id$
*/
public class SubsonicRESTException extends Exception {
class SubsonicRESTException extends Exception {
private final int code;
public SubsonicRESTException(int code, String message) {
public SubsonicRESTException(String message) {
super(message);
this.code = code;
}
public int getCode() {
return code;
}
}

View File

@ -16,7 +16,12 @@
package net.nullsum.audinaut.service.parser;
import android.content.Context;
import android.util.Log;
import net.nullsum.audinaut.domain.MusicFolder;
import net.nullsum.audinaut.domain.User;
import net.nullsum.audinaut.domain.User.Setting;
import net.nullsum.audinaut.service.MusicService;
import net.nullsum.audinaut.service.MusicServiceFactory;
import org.xmlpull.v1.XmlPullParser;
@ -24,24 +29,15 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import net.nullsum.audinaut.domain.MusicFolder;
import net.nullsum.audinaut.domain.User;
import net.nullsum.audinaut.domain.User.MusicFolderSetting;
import net.nullsum.audinaut.domain.User.Setting;
import net.nullsum.audinaut.service.MusicService;
import net.nullsum.audinaut.service.MusicServiceFactory;
import net.nullsum.audinaut.util.ProgressListener;
public class UserParser extends AbstractParser {
private static final String TAG = UserParser.class.getSimpleName();
public UserParser(Context context, int instance) {
super(context, instance);
}
public List<User> parse(InputStream inputStream, ProgressListener progressListener) throws Exception {
public List<User> parse(InputStream inputStream) throws Exception {
init(inputStream);
List<User> result = new ArrayList<User>();
List<User> result = new ArrayList<>();
List<MusicFolder> musicFolders = null;
User user = null;
int eventType;
@ -53,33 +49,26 @@ public class UserParser extends AbstractParser {
tagName = getElementName();
if ("user".equals(tagName)) {
user = new User();
user.setUsername(get("username"));
user.setEmail(get("email"));
for(String role: User.ROLES) {
parseSetting(user, role);
}
result.add(user);
} else if ("error".equals(tagName)) {
handleError();
}
} else if(eventType == XmlPullParser.TEXT) {
if("folder".equals(tagName)) {
} else if (eventType == XmlPullParser.TEXT) {
if ("folder".equals(tagName)) {
String id = getText();
if(musicFolders == null) {
if (musicFolders == null) {
musicFolders = getMusicFolders();
}
if(user != null) {
if(user.getMusicFolderSettings() == null) {
if (user != null) {
if (user.getMusicFolderSettings() == null) {
for (MusicFolder musicFolder : musicFolders) {
user.addMusicFolder(musicFolder);
}
}
for(Setting musicFolder: user.getMusicFolderSettings()) {
if(musicFolder.getName().equals(id)) {
for (Setting musicFolder : user.getMusicFolderSettings()) {
if (musicFolder.getName().equals(id)) {
musicFolder.setValue(true);
break;
}
@ -94,15 +83,8 @@ public class UserParser extends AbstractParser {
return result;
}
private List<MusicFolder> getMusicFolders() throws Exception{
private List<MusicFolder> getMusicFolders() throws Exception {
MusicService musicService = MusicServiceFactory.getMusicService(context);
return musicService.getMusicFolders(false, context, null);
}
private void parseSetting(User user, String name) {
String value = get(name);
if(value != null) {
user.addSetting(name, "true".equals(value));
}
}
}

View File

@ -22,7 +22,6 @@ package net.nullsum.audinaut.service.sync;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.NetworkErrorException;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@ -58,17 +57,17 @@ public class AuthenticatorService extends Service {
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) {
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) {
return null;
}
@ -78,12 +77,12 @@ public class AuthenticatorService extends Service {
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) {
return null;
}
}

View File

@ -21,24 +21,25 @@ package net.nullsum.audinaut.updates;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.Util;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Scott
*/
public class Updater {
protected String TAG = Updater.class.getSimpleName();
protected int version;
protected Context context;
private final int version;
String TAG = Updater.class.getSimpleName();
private Context context;
public Updater(int version) {
// 5.2 should show as 520 instead of 52
if(version < 100) {
if (version < 100) {
version *= 10;
}
this.version = version;
@ -46,34 +47,41 @@ public class Updater {
public void checkUpdates(Context context) {
this.context = context;
List<Updater> updaters = new ArrayList<Updater>();
List<Updater> updaters = new ArrayList<>();
updaters.add(new UpdaterSongPress());
SharedPreferences prefs = Util.getPreferences(context);
int lastVersion = prefs.getInt(Constants.LAST_VERSION, 0);
if(lastVersion == 0) {
if (lastVersion == 0) {
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(Constants.LAST_VERSION, version);
editor.apply();
}
else if(version > lastVersion) {
} else if (version > lastVersion) {
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(Constants.LAST_VERSION, version);
editor.apply();
Log.i(TAG, "Updating from version " + lastVersion + " to " + version);
for(Updater updater: updaters) {
if(updater.shouldUpdate(lastVersion)) {
for (Updater updater : updaters) {
if (updater.shouldUpdate(lastVersion)) {
new BackgroundUpdate(context, updater).execute();
}
}
}
}
public String getName() {
private String getName() {
return this.TAG;
}
private boolean shouldUpdate(int version) {
return this.version > version;
}
void update(Context context) {
}
private class BackgroundUpdate extends SilentBackgroundTask<Void> {
private final Updater updater;
@ -86,17 +94,10 @@ public class Updater {
protected Void doInBackground() {
try {
updater.update(context);
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to run update for " + updater.getName());
}
return null;
}
}
public boolean shouldUpdate(int version) {
return this.version > version;
}
public void update(Context context) {
}
}

View File

@ -21,7 +21,7 @@ import android.content.SharedPreferences;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.Util;
public class UpdaterSongPress extends Updater {
class UpdaterSongPress extends Updater {
public UpdaterSongPress() {
super(521);
TAG = this.getClass().getSimpleName();
@ -33,7 +33,7 @@ public class UpdaterSongPress extends Updater {
boolean playNowAfter = prefs.getBoolean("playNowAfter", true);
// Migrate the old preference so behavior stays the same
if(playNowAfter == false) {
if (!playNowAfter) {
SharedPreferences.Editor editor = prefs.edit();
editor.putString(Constants.PREFERENCES_KEY_SONG_PRESS_ACTION, "single");
editor.apply();

View File

@ -18,85 +18,73 @@
*/
package net.nullsum.audinaut.util;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.view.ErrorDialog;
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import org.xmlpull.v1.XmlPullParserException;
import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.view.ErrorDialog;
/**
* @author Sindre Mehus
*/
public abstract class BackgroundTask<T> implements ProgressListener {
static final BlockingQueue<BackgroundTask.Task> queue = new LinkedBlockingQueue<>(10);
private static final String TAG = BackgroundTask.class.getSimpleName();
private final Context context;
protected AtomicBoolean cancelled = new AtomicBoolean(false);
protected OnCancelListener cancelListener;
protected Runnable onCompletionListener = null;
protected Task task;
private static final int DEFAULT_CONCURRENCY = 8;
private static final Collection<Thread> threads = Collections.synchronizedCollection(new ArrayList<Thread>());
protected static final BlockingQueue<BackgroundTask.Task> queue = new LinkedBlockingQueue<BackgroundTask.Task>(10);
private static Handler handler = null;
static {
try {
handler = new Handler(Looper.getMainLooper());
} catch(Exception e) {
} catch (Exception e) {
// Not called from main thread
}
}
public BackgroundTask(Context context) {
final AtomicBoolean cancelled = new AtomicBoolean(false);
private final Context context;
Task task;
BackgroundTask(Context context) {
this.context = context;
if(threads.size() < DEFAULT_CONCURRENCY) {
for(int i = threads.size(); i < DEFAULT_CONCURRENCY; i++) {
if (threads.size() < DEFAULT_CONCURRENCY) {
for (int i = threads.size(); i < DEFAULT_CONCURRENCY; i++) {
Thread thread = new Thread(new TaskRunnable(), String.format("BackgroundTask_%d", i));
threads.add(thread);
thread.start();
}
}
if(handler == null) {
if (handler == null) {
try {
handler = new Handler(Looper.getMainLooper());
} catch(Exception e) {
} catch (Exception e) {
// Not called from main thread
}
}
}
public static void stopThreads() {
for(Thread thread: threads) {
thread.interrupt();
}
threads.clear();
queue.clear();
private AppCompatActivity getActivity() {
return (context instanceof AppCompatActivity) ? ((AppCompatActivity) context) : null;
}
protected Activity getActivity() {
return (context instanceof Activity) ? ((Activity) context) : null;
}
protected Context getContext() {
return context;
}
protected Handler getHandler() {
Handler getHandler() {
return handler;
}
@ -108,8 +96,8 @@ public abstract class BackgroundTask<T> implements ProgressListener {
protected void error(Throwable error) {
Log.w(TAG, "Got exception: " + error, error);
Activity activity = getActivity();
if(activity != null) {
AppCompatActivity activity = getActivity();
if (activity != null) {
new ErrorDialog(activity, getErrorMessage(error), true);
}
}
@ -140,39 +128,27 @@ public abstract class BackgroundTask<T> implements ProgressListener {
}
public void cancel() {
if(cancelled.compareAndSet(false, true)) {
if(isRunning()) {
if(cancelListener != null) {
cancelListener.onCancel();
} else {
task.cancel();
}
if (cancelled.compareAndSet(false, true)) {
if (isRunning()) {
task.cancel();
}
task = null;
}
}
public boolean isCancelled() {
return cancelled.get();
}
public void setOnCancelListener(OnCancelListener listener) {
cancelListener = listener;
}
public boolean isRunning() {
if(task == null) {
return false;
} else {
return task.isRunning();
}
return task != null && task.isRunning();
}
@Override
public abstract void updateProgress(final String message);
@Override
public void updateProgress(int messageId) {
updateProgress(context.getResources().getString(messageId));
public void updateProgress() {
updateProgress(context.getResources().getString(R.string.settings_testing_connection));
}
@Override
@ -180,17 +156,13 @@ public abstract class BackgroundTask<T> implements ProgressListener {
}
public void setOnCompletionListener(Runnable onCompletionListener) {
this.onCompletionListener = onCompletionListener;
}
protected class Task {
class Task {
private final AtomicBoolean taskStart = new AtomicBoolean(false);
private Thread thread;
private AtomicBoolean taskStart = new AtomicBoolean(false);
private void execute() throws Exception {
// Don't run if cancelled already
if(isCancelled()) {
if (isCancelled()) {
return;
}
@ -199,60 +171,54 @@ public abstract class BackgroundTask<T> implements ProgressListener {
taskStart.set(true);
final T result = doInBackground();
if(isCancelled()) {
if (isCancelled()) {
taskStart.set(false);
return;
}
if(handler != null) {
handler.post(new Runnable() {
@Override
public void run() {
if (!isCancelled()) {
try {
onDone(result);
} catch (Throwable t) {
if(!isCancelled()) {
try {
onError(t);
} catch(Exception e) {
// Don't care
}
if (handler != null) {
handler.post(() -> {
if (!isCancelled()) {
try {
onDone(result);
} catch (Throwable t) {
if (!isCancelled()) {
try {
onError(t);
} catch (Exception e) {
// Don't care
}
}
}
taskStart.set(false);
}
taskStart.set(false);
});
} else {
taskStart.set(false);
}
} catch(InterruptedException interrupt) {
if(taskStart.get()) {
} catch (InterruptedException interrupt) {
if (taskStart.get()) {
// Don't exit root thread if task cancelled
throw interrupt;
}
} catch(final Throwable t) {
if(isCancelled()) {
} catch (final Throwable t) {
if (isCancelled()) {
taskStart.set(false);
return;
}
if(handler != null) {
handler.post(new Runnable() {
@Override
public void run() {
if(!isCancelled()) {
try {
onError(t);
} catch(Exception e) {
// Don't care
}
if (handler != null) {
handler.post(() -> {
if (!isCancelled()) {
try {
onError(t);
} catch (Exception e) {
// Don't care
}
taskStart.set(false);
}
taskStart.set(false);
});
} else {
taskStart.set(false);
@ -263,28 +229,21 @@ public abstract class BackgroundTask<T> implements ProgressListener {
}
public void cancel() {
if(taskStart.compareAndSet(true, false)) {
if (taskStart.compareAndSet(true, false)) {
if (thread != null) {
thread.interrupt();
}
}
}
public boolean isCancelled() {
if(Thread.interrupted()) {
return true;
} else if(BackgroundTask.this.isCancelled()) {
return true;
} else {
return false;
}
return Thread.interrupted() || BackgroundTask.this.isCancelled();
}
public void onDone(T result) {
done(result);
if(onCompletionListener != null) {
onCompletionListener.run();
}
}
public void onError(Throwable t) {
error(t);
}
@ -304,22 +263,18 @@ public abstract class BackgroundTask<T> implements ProgressListener {
@Override
public void run() {
Looper.prepare();
while(running) {
while (running) {
try {
Task task = queue.take();
task.execute();
} catch(InterruptedException stop) {
} catch (InterruptedException stop) {
Log.e(TAG, "Thread died");
running = false;
threads.remove(Thread.currentThread());
} catch(Throwable t) {
} catch (Throwable t) {
Log.e(TAG, "Unexpected crash in BackgroundTask thread", t);
}
}
}
}
public static interface OnCancelListener {
void onCancel();
}
}

View File

@ -0,0 +1,66 @@
package net.nullsum.audinaut.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
class BlurBuilder {
private static final float BITMAP_SCALE = 0.4f;
private static final float BLUR_RADIUS = 25f;
public static Bitmap blur(Context context, Bitmap image) {
Bitmap newImage = image;
for(int i = 0; i<3; i++) {
newImage = blur_real(context, newImage);
}
newImage = changeBitmapContrastBrightness(newImage, 0.5f, 48);
return newImage;
}
private static Bitmap blur_real(Context context, Bitmap image) {
int width = Math.round(image.getWidth() * BITMAP_SCALE);
int height = Math.round(image.getHeight() * BITMAP_SCALE);
Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
RenderScript rs = RenderScript.create(context);
ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
theIntrinsic.setRadius(BLUR_RADIUS);
theIntrinsic.setInput(tmpIn);
theIntrinsic.forEach(tmpOut);
tmpOut.copyTo(outputBitmap);
return outputBitmap;
}
public static Bitmap changeBitmapContrastBrightness(Bitmap bmp, float contrast, float brightness)
{
ColorMatrix cm = new ColorMatrix(new float[]
{
contrast, 0, 0, 0, brightness,
0, contrast, 0, 0, brightness,
0, 0, contrast, 0, brightness,
0, 0, 0, 1, 0
});
Bitmap ret = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
Canvas canvas = new Canvas(ret);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(cm));
canvas.drawBitmap(bmp, 0, 0, paint);
return ret;
}
}

View File

@ -19,10 +19,16 @@ import java.io.File;
public interface BufferFile {
File getFile();
Long getContentLength();
long getEstimatedSize();
boolean isWorkDone();
void onStart();
void onStop();
void onResume();
}

View File

@ -18,17 +18,8 @@ package net.nullsum.audinaut.util;
import java.io.File;
import java.net.Socket;
import android.content.Context;
import net.nullsum.audinaut.util.FileProxy;
public class BufferProxy extends FileProxy {
private static final String TAG = BufferProxy.class.getSimpleName();
protected BufferFile progress;
public BufferProxy(Context context) {
super(context);
}
private BufferFile progress;
protected ProxyTask getTask(Socket client) {
return new BufferFileTask(client);
@ -51,11 +42,12 @@ public class BufferProxy extends FileProxy {
@Override
Long getContentLength() {
Long contentLength = progress.getContentLength();
if(contentLength == null && progress.isWorkDone()) {
if (contentLength == null && progress.isWorkDone()) {
contentLength = file.length();
}
return contentLength;
}
@Override
long getFileSize() {
return progress.getEstimatedSize();
@ -65,10 +57,12 @@ public class BufferProxy extends FileProxy {
public void onStart() {
progress.onStart();
}
@Override
public void onStop() {
progress.onStop();
}
@Override
public void onResume() {
progress.onResume();

View File

@ -1,22 +1,21 @@
package net.nullsum.audinaut.util;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import android.content.Context;
import android.util.Log;
import android.os.StatFs;
import android.util.Log;
import net.nullsum.audinaut.domain.Playlist;
import net.nullsum.audinaut.service.DownloadFile;
import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.service.MediaStoreService;
import java.util.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
/**
* @author Sindre Mehus
@ -41,9 +40,11 @@ public class CacheCleaner {
public void clean() {
new BackgroundCleanup(context).execute();
}
public void cleanSpace() {
new BackgroundSpaceCleanup(context).execute();
}
public void cleanPlaylists(List<Playlist> playlists) {
new BackgroundPlaylistsCleanup(context, playlists).execute();
}
@ -59,7 +60,7 @@ public class CacheCleaner {
}
private long getMinimumDelete(List<File> files, List<File> pinned) {
if(files.size() == 0) {
if (files.size() == 0) {
return 0L;
}
@ -75,8 +76,8 @@ public class CacheCleaner {
// Ensure that file system is not more than 95% full.
StatFs stat = new StatFs(files.get(0).getPath());
long bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize();
long bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
long bytesTotalFs = stat.getBlockCountLong() * stat.getBlockSizeLong();
long bytesAvailableFs = stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
long bytesUsedFs = bytesTotalFs - bytesAvailableFs;
long minFsAvailability = bytesTotalFs - MIN_FREE_SPACE;
@ -99,7 +100,7 @@ public class CacheCleaner {
long bytesDeleted = 0L;
for (File file : files) {
if(!deletePartials && bytesDeleted > bytesToDelete) break;
if (!deletePartials && bytesDeleted > bytesToDelete) break;
if (bytesToDelete > bytesDeleted || (deletePartials && (file.getName().endsWith(".partial") || file.getName().contains(".partial.")))) {
if (!undeletable.contains(file) && !file.getName().equals(Constants.ALBUM_ART_FILE)) {
@ -134,22 +135,11 @@ public class CacheCleaner {
}
private void sortByAscendingModificationTime(List<File> files) {
Collections.sort(files, new Comparator<File>() {
@Override
public int compare(File a, File b) {
if (a.lastModified() < b.lastModified()) {
return -1;
}
if (a.lastModified() > b.lastModified()) {
return 1;
}
return 0;
}
});
Collections.sort(files, (a, b) -> Long.compare(a.lastModified(), b.lastModified()));
}
private Set<File> findUndeletableFiles() {
Set<File> undeletable = new HashSet<File>(5);
Set<File> undeletable = new HashSet<>(5);
for (DownloadFile downloadFile : downloadService.getDownloads()) {
undeletable.add(downloadFile.getPartialFile());
@ -163,30 +153,30 @@ public class CacheCleaner {
private void cleanupCoverArt(Context context) {
File dir = FileUtil.getAlbumArtDirectory(context);
List<File> files = new ArrayList<File>();
List<File> files = new ArrayList<>();
long bytesUsed = 0L;
for(File file: dir.listFiles()) {
if(file.isFile()) {
for (File file : dir.listFiles()) {
if (file.isFile()) {
files.add(file);
bytesUsed += file.length();
}
}
// Don't waste time sorting if under limit already
if(bytesUsed < MAX_COVER_ART_SPACE) {
if (bytesUsed < MAX_COVER_ART_SPACE) {
return;
}
sortByAscendingModificationTime(files);
long bytesDeleted = 0L;
for(File file: files) {
for (File file : files) {
// End as soon as the space used is below the threshold
if(bytesUsed < MAX_COVER_ART_SPACE) {
if (bytesUsed < MAX_COVER_ART_SPACE) {
break;
}
long bytes = file.length();
if(file.delete()) {
if (file.delete()) {
bytesUsed -= bytes;
bytesDeleted += bytes;
}
@ -208,9 +198,9 @@ public class CacheCleaner {
}
try {
List<File> files = new ArrayList<File>();
List<File> pinned = new ArrayList<File>();
List<File> dirs = new ArrayList<File>();
List<File> files = new ArrayList<>();
List<File> pinned = new ArrayList<>();
List<File> dirs = new ArrayList<>();
findCandidatesForDeletion(FileUtil.getMusicDirectory(context), files, pinned, dirs);
sortByAscendingModificationTime(files);
@ -243,13 +233,13 @@ public class CacheCleaner {
}
try {
List<File> files = new ArrayList<File>();
List<File> pinned = new ArrayList<File>();
List<File> dirs = new ArrayList<File>();
List<File> files = new ArrayList<>();
List<File> pinned = new ArrayList<>();
List<File> dirs = new ArrayList<>();
findCandidatesForDeletion(FileUtil.getMusicDirectory(context), files, pinned, dirs);
long bytesToDelete = getMinimumDelete(files, pinned);
if(bytesToDelete > 0L) {
if (bytesToDelete > 0L) {
sortByAscendingModificationTime(files);
Set<File> undeletable = findUndeletableFiles();
deleteFiles(files, undeletable, bytesToDelete, false);
@ -279,7 +269,7 @@ public class CacheCleaner {
playlistFiles.remove(FileUtil.getPlaylistFile(context, server, playlist.getName()));
}
for(File playlist : playlistFiles) {
for (File playlist : playlistFiles) {
playlist.delete();
}
} catch (RuntimeException x) {

View File

@ -37,7 +37,6 @@ public final class Constants {
public static final String INTENT_EXTRA_NAME_DIRECTORY = "subsonic.directory";
public static final String INTENT_EXTRA_NAME_CHILD_ID = "subsonic.child.id";
public static final String INTENT_EXTRA_NAME_ARTIST = "subsonic.artist";
public static final String INTENT_EXTRA_NAME_TITLE = "subsonic.title";
public static final String INTENT_EXTRA_NAME_AUTOPLAY = "subsonic.playall";
public static final String INTENT_EXTRA_NAME_QUERY = "subsonic.query";
public static final String INTENT_EXTRA_NAME_PLAYLIST_ID = "subsonic.playlist.id";
@ -48,18 +47,14 @@ public final class Constants {
public static final String INTENT_EXTRA_NAME_ALBUM_LIST_SIZE = "subsonic.albumlistsize";
public static final String INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET = "subsonic.albumlistoffset";
public static final String INTENT_EXTRA_NAME_SHUFFLE = "subsonic.shuffle";
public static final String INTENT_EXTRA_REQUEST_SEARCH = "subsonic.requestsearch";
public static final String INTENT_EXTRA_NAME_EXIT = "subsonic.exit" ;
public static final String INTENT_EXTRA_NAME_EXIT = "subsonic.exit";
public static final String INTENT_EXTRA_NAME_DOWNLOAD = "subsonic.download";
public static final String INTENT_EXTRA_NAME_DOWNLOAD_VIEW = "subsonic.download_view";
public static final String INTENT_EXTRA_VIEW_ALBUM = "subsonic.view_album";
public static final String INTENT_EXTRA_NAME_SHARE = "subsonic.share";
public static final String INTENT_EXTRA_FRAGMENT_TYPE = "fragmentType";
public static final String INTENT_EXTRA_REFRESH_LISTINGS = "refreshListings";
public static final String INTENT_EXTRA_SEARCH_SONG = "searchSong";
public static final String INTENT_EXTRA_TOP_TRACKS = "topTracks";
public static final String INTENT_EXTRA_PLAY_LAST = "playLast";
public static final String INTENT_EXTRA_ENTRY = "passedEntry";
// Preferences keys.
public static final String PREFERENCES_KEY_SERVER_KEY = "server";
@ -72,11 +67,12 @@ public final class Constants {
public static final String PREFERENCES_KEY_SERVER_INTERNAL_URL = "serverInternalUrl";
public static final String PREFERENCES_KEY_SERVER_LOCAL_NETWORK_SSID = "serverLocalNetworkSSID";
public static final String PREFERENCES_KEY_TEST_CONNECTION = "serverTestConnection";
public static final String PREFERENCES_KEY_START_SCAN = "serverStartScan";
public static final String PREFERENCES_KEY_OPEN_BROWSER = "openBrowser";
public static final String PREFERENCES_KEY_MUSIC_FOLDER_ID = "musicFolderId";
public static final String PREFERENCES_KEY_USERNAME = "username";
public static final String PREFERENCES_KEY_PASSWORD = "password";
public static final String PREFERENCES_KEY_INSTALL_TIME = "installTime";
public static final String PREFERENCES_KEY_AUTH_METHOD = "authMethod";
public static final String PREFERENCES_KEY_THEME = "theme";
public static final String PREFERENCES_KEY_FULL_SCREEN = "fullScreen";
public static final String PREFERENCES_KEY_DISPLAY_TRACK = "displayTrack";
@ -98,7 +94,6 @@ public final class Constants {
public static final String PREFERENCES_KEY_SHUFFLE_START_YEAR = "startYear";
public static final String PREFERENCES_KEY_SHUFFLE_END_YEAR = "endYear";
public static final String PREFERENCES_KEY_SHUFFLE_GENRE = "genre";
public static final String PREFERENCES_KEY_KEEP_SCREEN_ON = "keepScreenOn";
public static final String PREFERENCES_EQUALIZER_ON = "equalizerOn";
public static final String PREFERENCES_EQUALIZER_SETTINGS = "equalizerSettings";
public static final String PREFERENCES_KEY_PERSISTENT_NOTIFICATION = "persistentNotification";
@ -106,7 +101,6 @@ public final class Constants {
public static final String PREFERENCES_KEY_REMOVE_PLAYED = "removePlayed";
public static final String PREFERENCES_KEY_KEEP_PLAYED_CNT = "keepPlayedCount";
public static final String PREFERENCES_KEY_SHUFFLE_MODE = "shuffleMode2";
public static final String PREFERENCES_KEY_SHUFFLE_MODE_EXTRA = "shuffleModeExtra";
public static final String PREFERENCES_KEY_SYNC_ENABLED = "syncEnabled";
public static final String PREFERENCES_KEY_SYNC_INTERVAL = "syncInterval";
public static final String PREFERENCES_KEY_SYNC_WIFI = "syncWifi";
@ -115,31 +109,22 @@ public final class Constants {
public static final String PREFERENCES_KEY_PAUSE_DISCONNECT = "pauseOnDisconnect";
public static final String PREFERENCES_KEY_HIDE_WIDGET = "hideWidget";
public static final String PREFERENCES_KEY_CUSTOM_SORT_ENABLED = "customSortEnabled";
public static final String PREFERENCES_KEY_SHARED_ENABLED = "sharedEnabled";
public static final String PREFERENCES_KEY_OPEN_TO_TAB = "openToTab";
// public static final String PREFERENCES_KEY_PLAY_NOW_AFTER = "playNowAfter";
public static final String PREFERENCES_KEY_SONG_PRESS_ACTION = "songPressAction";
public static final String PREFERENCES_KEY_LARGE_ALBUM_ART = "largeAlbumArt";
public static final String PREFERENCES_KEY_BLURRED_BACKGROUND = "blurredBackground";
public static final String PREFERENCES_KEY_PLAYLIST_NAME = "suggestedPlaylistName";
public static final String PREFERENCES_KEY_PLAYLIST_ID = "suggestedPlaylistId";
public static final String PREFERENCES_KEY_RECENT_COUNT = "mostRecentCount";
public static final String PREFERENCES_KEY_REPLAY_GAIN = "replayGain";
public static final String PREFERENCES_KEY_REPLAY_GAIN_BUMP = "replayGainBump2";
public static final String PREFERENCES_KEY_REPLAY_GAIN_UNTAGGED = "replayGainUntagged2";
public static final String PREFERENCES_KEY_REPLAY_GAIN_TYPE= "replayGainType";
public static final String PREFERENCES_KEY_REPLAY_GAIN_TYPE = "replayGainType";
public static final String PREFERENCES_KEY_ALBUMS_PER_FOLDER = "albumsPerFolder";
public static final String PREFERENCES_KEY_FIRST_LEVEL_ARTIST = "firstLevelArtist";
public static final String PREFERENCES_KEY_START_ON_HEADPHONES = "startOnHeadphones";
public static final String PREFERENCES_KEY_COLOR_ACTION_BAR = "colorActionBar";
public static final String PREFERENCES_KEY_SHUFFLE_BY_ALBUM = "shuffleByAlbum";
public static final String PREFERENCES_KEY_RESUME_PLAY_QUEUE_NEVER = "neverResumePlayQueue";
public static final String PREFERENCES_KEY_BATCH_MODE = "batchMode";
public static final String PREFERENCES_KEY_HEADS_UP_NOTIFICATION = "headsUpNotification";
public static final String OFFLINE_STAR_COUNT = "starCount";
public static final String OFFLINE_STAR_ID = "starID";
public static final String OFFLINE_STAR_SEARCH = "starTitle";
public static final String OFFLINE_STAR_SETTING = "starSetting";
public static final String CACHE_KEY_IGNORE = "ignoreArticles";
public static final String CACHE_AUDIO_SESSION_ID = "audioSessionId";
@ -152,21 +137,17 @@ public final class Constants {
public static final String MAIN_SLIDE_PANEL_STATE = "slidePanelState";
public static final String FRAGMENT_LIST = "fragmentList";
public static final String FRAGMENT_LIST2 = "fragmentList2";
public static final String FRAGMENT_EXTRA = "fragmentExtra";
public static final String FRAGMENT_DOWNLOAD_FLIPPER = "fragmentDownloadFlipper";
public static final String FRAGMENT_NAME = "fragmentName";
public static final String FRAGMENT_POSITION = "fragmentPosition";
// Name of the preferences file.
public static final String PREFERENCES_FILE_NAME = "net.nullsum.audinaut_preferences";
public static final String OFFLINE_SYNC_NAME = "net.nullsum.audinaut.offline";
public static final String OFFLINE_SYNC_DEFAULT = "syncDefaults";
// Account prefs
public static final String SYNC_ACCOUNT_NAME = "Subsonic Account";
public static final String SYNC_ACCOUNT_TYPE = "Audinaut";
public static final String SYNC_ACCOUNT_PLAYLIST_AUTHORITY = "net.nullsum.audinaut.playlists.provider";
public static final String SYNC_ACCOUNT_MOST_RECENT_AUTHORITY = "net.nullsum.audinaut.mostrecent.provider";
public static final String TASKER_EXTRA_BUNDLE = "com.twofortyfouram.locale.intent.extra.BUNDLE";

View File

@ -1,11 +1,7 @@
package net.nullsum.audinaut.util;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import java.util.ArrayDeque;
import java.util.Deque;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import net.nullsum.audinaut.adapter.SectionAdapter;
import net.nullsum.audinaut.fragments.SubsonicFragment;
@ -14,14 +10,15 @@ import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.view.SongView;
import net.nullsum.audinaut.view.UpdateView;
import java.util.ArrayDeque;
import java.util.Deque;
public class DownloadFileItemHelperCallback extends ItemTouchHelper.SimpleCallback {
private static final String TAG = DownloadFileItemHelperCallback.class.getSimpleName();
private SubsonicFragment fragment;
private boolean mainList;
private final SubsonicFragment fragment;
private final boolean mainList;
private final Deque pendingOperations = new ArrayDeque();
private BackgroundTask pendingTask = null;
private Deque pendingOperations = new ArrayDeque();
public DownloadFileItemHelperCallback(SubsonicFragment fragment, boolean mainList) {
super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
@ -54,39 +51,40 @@ public class DownloadFileItemHelperCallback extends ItemTouchHelper.SimpleCallba
}
}
public DownloadService getDownloadService() {
private DownloadService getDownloadService() {
return fragment.getDownloadService();
}
public SectionAdapter getSectionAdapter() {
private SectionAdapter getSectionAdapter() {
return fragment.getCurrentAdapter();
}
private void updateDownloadService() {
if(pendingTask == null) {
if (pendingTask == null) {
final DownloadService downloadService = getDownloadService();
if(downloadService == null) {
if (downloadService == null) {
return;
}
pendingTask = new SilentBackgroundTask<Void>(downloadService) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
boolean running = true;
while(running) {
while (running) {
Object nextOperation = null;
synchronized (pendingOperations) {
if(!pendingOperations.isEmpty()) {
if (!pendingOperations.isEmpty()) {
nextOperation = pendingOperations.remove();
}
}
if(nextOperation != null) {
if(nextOperation instanceof Pair) {
if (nextOperation != null) {
if (nextOperation instanceof Pair) {
Pair<Integer, Integer> swap = (Pair) nextOperation;
downloadService.swap(mainList, swap.getFirst(), swap.getSecond());
} else if(nextOperation instanceof DownloadFile) {
} else if (nextOperation instanceof DownloadFile) {
DownloadFile downloadFile = (DownloadFile) nextOperation;
if(mainList) {
if (mainList) {
downloadService.remove(downloadFile);
} else {
downloadService.removeBackground(downloadFile);
@ -101,7 +99,7 @@ public class DownloadFileItemHelperCallback extends ItemTouchHelper.SimpleCallba
pendingTask = null;
// Start a task if this is non-empty. Means someone added while we were running operations
if(!pendingOperations.isEmpty()) {
if (!pendingOperations.isEmpty()) {
updateDownloadService();
}
}

View File

@ -20,64 +20,47 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.annotation.AttrRes;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.util.SparseArray;
import android.util.TypedValue;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import androidx.annotation.AttrRes;
import androidx.annotation.DrawableRes;
import net.nullsum.audinaut.R;
import java.util.WeakHashMap;
public class DrawableTint {
private static final Map<Integer, Integer> attrMap = new HashMap<>();
private static final SparseArray<Integer> attrMap = new SparseArray<>();
private static final WeakHashMap<Integer, Drawable> tintedDrawables = new WeakHashMap<>();
public static Drawable getTintedDrawable(Context context, @DrawableRes int drawableRes) {
return getTintedDrawable(context, drawableRes, R.attr.colorAccent);
}
public static Drawable getTintedDrawable(Context context, @DrawableRes int drawableRes, @AttrRes int colorAttr) {
if(tintedDrawables.containsKey(drawableRes)) {
return tintedDrawables.get(drawableRes);
public static Drawable getTintedDrawableFromColor(Context context) {
if (tintedDrawables.containsKey(R.drawable.abc_spinner_mtrl_am_alpha)) {
return tintedDrawables.get(R.drawable.abc_spinner_mtrl_am_alpha);
}
int color = getColorRes(context, colorAttr);
Drawable background = context.getResources().getDrawable(drawableRes);
int color = context.getResources().getColor(android.R.color.white);
Drawable background = context.getResources().getDrawable(R.drawable.abc_spinner_mtrl_am_alpha);
background.setColorFilter(color, PorterDuff.Mode.SRC_IN);
tintedDrawables.put(drawableRes, background);
tintedDrawables.put(R.drawable.abc_spinner_mtrl_am_alpha, background);
return background;
}
public static Drawable getTintedDrawableFromColor(Context context, @DrawableRes int drawableRes, @ColorRes int colorRes) {
if(tintedDrawables.containsKey(drawableRes)) {
return tintedDrawables.get(drawableRes);
}
int color = context.getResources().getColor(colorRes);
Drawable background = context.getResources().getDrawable(drawableRes);
background.setColorFilter(color, PorterDuff.Mode.SRC_IN);
tintedDrawables.put(drawableRes, background);
return background;
}
public static int getColorRes(Context context, @AttrRes int colorAttr) {
int color;
if(attrMap.containsKey(colorAttr)) {
color = attrMap.get(colorAttr);
} else {
Integer color = attrMap.get(colorAttr);
if (color == null) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
theme.resolveAttribute(colorAttr, typedValue, true);
color = typedValue.data;
attrMap.put(colorAttr, color);
}
return color;
}
public static int getDrawableRes(Context context, @AttrRes int drawableAttr) {
if(attrMap.containsKey(drawableAttr)) {
return attrMap.get(drawableAttr);
} else {
Integer attr = attrMap.get(drawableAttr);
if (attr == null) {
int[] attrs = new int[]{drawableAttr};
TypedArray typedArray = context.obtainStyledAttributes(attrs);
@DrawableRes int drawableRes = typedArray.getResourceId(0, 0);
@ -85,14 +68,7 @@ public class DrawableTint {
attrMap.put(drawableAttr, drawableRes);
return drawableRes;
}
}
public static Drawable getTintedAttrDrawable(Context context, @AttrRes int drawableAttr, @AttrRes int colorAttr) {
if(tintedDrawables.containsKey(drawableAttr)) {
return getTintedDrawable(context, attrMap.get(drawableAttr), colorAttr);
}
@DrawableRes int drawableRes = getDrawableRes(context, drawableAttr);
return getTintedDrawable(context, drawableRes, colorAttr);
return attr;
}
public static void wipeTintCache() {

View File

@ -1,20 +0,0 @@
/*
This file is part of Subsonic.
Subsonic 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.
Subsonic 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.
You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Scott Jackson
*/
package net.nullsum.audinaut.util;
public final class EnvironmentVariables {
public static final String PASTEBIN_DEV_KEY = "";
}

View File

@ -15,6 +15,8 @@
package net.nullsum.audinaut.util;
import android.util.Log;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
@ -23,18 +25,9 @@ import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import android.content.Context;
import android.util.Log;
import net.nullsum.audinaut.util.ServerProxy;
public class FileProxy extends ServerProxy {
private static final String TAG = FileProxy.class.getSimpleName();
public FileProxy(Context context) {
super(context);
}
protected ProxyTask getTask(Socket client) {
return new StreamFileTask(client);
}
@ -48,7 +41,7 @@ public class FileProxy extends ServerProxy {
@Override
public boolean processRequest() {
if(!super.processRequest()) {
if (!super.processRequest()) {
return false;
}
@ -60,11 +53,7 @@ public class FileProxy extends ServerProxy {
}
// Make sure to not try to read past where the file is downloaded
if(cbSkip != 0 && cbSkip >= file.length()) {
return false;
}
return true;
return !(cbSkip != 0 && cbSkip >= file.length());
}
File getFile(String path) {
@ -74,6 +63,7 @@ public class FileProxy extends ServerProxy {
Long getContentLength() {
return file.length();
}
long getFileSize() {
return file.length();
}
@ -84,12 +74,12 @@ public class FileProxy extends ServerProxy {
// Create HTTP header
String headers;
if(cbSkip == 0) {
if (cbSkip == 0) {
headers = "HTTP/1.0 200 OK\r\n";
} else {
headers = "HTTP/1.0 206 OK\r\n";
headers += "Content-Range: bytes " + cbSkip + "-" + (file.length() - 1) + "/";
if(contentLength == null) {
if (contentLength == null) {
headers += "*";
} else {
headers += contentLength;
@ -102,21 +92,21 @@ public class FileProxy extends ServerProxy {
String name = file.getPath();
int index = name.lastIndexOf('.');
String ext = "";
if(index != -1) {
if (index != -1) {
ext = name.substring(index + 1).toLowerCase();
}
if("mp3".equals(ext)) {
if ("mp3".equals(ext)) {
headers += "Content-Type: audio/mpeg\r\n";
} else {
headers += "Content-Type: " + "application/octet-stream" + "\r\n";
}
long fileSize;
if(contentLength == null) {
if (contentLength == null) {
fileSize = getFileSize();
} else {
fileSize = contentLength;
if(cbSkip > 0) {
if (cbSkip > 0) {
headers += "Content-Length: " + (fileSize - cbSkip) + "\r\n";
} else {
headers += "Content-Length: " + fileSize + "\r\n";
@ -132,7 +122,7 @@ public class FileProxy extends ServerProxy {
OutputStream output = null;
byte[] buff = new byte[64 * 1024];
try {
output = new BufferedOutputStream(client.getOutputStream(), 32*1024);
output = new BufferedOutputStream(client.getOutputStream(), 32 * 1024);
output.write(headers.getBytes());
// Make sure to have file lock
@ -165,7 +155,7 @@ public class FileProxy extends ServerProxy {
}
// Done regardless of whether or not it thinks it is
if(isWorkDone()) {
if (isWorkDone()) {
break;
}
@ -178,14 +168,12 @@ public class FileProxy extends ServerProxy {
// Release file lock, use of stream proxy means nothing else is using it
onStop();
}
catch (SocketException socketException) {
} catch (SocketException socketException) {
Log.e(TAG, "SocketException() thrown, proxy client has probably closed. This can exit harmlessly");
// Release file lock, use of stream proxy means nothing else is using it
onStop();
}
catch (Exception e) {
} catch (Exception e) {
Log.e(TAG, "Exception thrown from streaming task:");
Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
}
@ -196,8 +184,7 @@ public class FileProxy extends ServerProxy {
output.close();
}
client.close();
}
catch (IOException e) {
} catch (IOException e) {
Log.e(TAG, "IOException while cleaning up streaming task:");
Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
}
@ -206,12 +193,15 @@ public class FileProxy extends ServerProxy {
public void onStart() {
}
public void onStop() {
}
public void onResume() {
}
public boolean isWorkDone() {
return cbSkip >= file.length();
}

View File

@ -18,6 +18,27 @@
*/
package net.nullsum.audinaut.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.core.content.ContextCompat;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.domain.Genre;
import net.nullsum.audinaut.domain.Indexes;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.MusicFolder;
import net.nullsum.audinaut.domain.Playlist;
import net.nullsum.audinaut.service.MediaStoreService;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
@ -28,34 +49,15 @@ import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Date;
import java.util.Collections;
import java.util.HashMap;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Environment;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.domain.Genre;
import net.nullsum.audinaut.domain.Indexes;
import net.nullsum.audinaut.domain.Playlist;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.MusicFolder;
import net.nullsum.audinaut.service.MediaStoreService;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
/**
* @author Sindre Mehus
*/
@ -64,11 +66,11 @@ public class FileUtil {
private static final String TAG = FileUtil.class.getSimpleName();
private static final String[] FILE_SYSTEM_UNSAFE = {"/", "\\", "..", ":", "\"", "?", "*", "<", ">", "|"};
private static final String[] FILE_SYSTEM_UNSAFE_DIR = {"\\", "..", ":", "\"", "?", "*", "<", ">", "|"};
private static final List<String> MUSIC_FILE_EXTENSIONS = Arrays.asList("mp3", "ogg", "aac", "flac", "m4a", "wav", "wma");
private static final List<String> PLAYLIST_FILE_EXTENSIONS = Arrays.asList("m3u");
private static final List<String> MUSIC_FILE_EXTENSIONS = Arrays.asList("mp3", "ogg", "opus", "aac", "flac", "m4a", "wav", "wma");
private static final List<String> PLAYLIST_FILE_EXTENSIONS = Collections.singletonList("m3u");
private static final int MAX_FILENAME_LENGTH = 254 - ".complete.mp3".length();
private static File DEFAULT_MUSIC_DIR;
private static final Kryo kryo = new Kryo();
private static File DEFAULT_MUSIC_DIR;
private static HashMap<String, MusicDirectory.Entry> entryLookup;
static {
@ -80,27 +82,8 @@ public class FileUtil {
kryo.register(Genre.class);
}
public static File getAnySong(Context context) {
File dir = getMusicDirectory(context);
return getAnySong(context, dir);
}
private static File getAnySong(Context context, File dir) {
for(File file: dir.listFiles()) {
if(file.isDirectory()) {
return getAnySong(context, file);
}
String extension = getExtension(file.getName());
if(MUSIC_FILE_EXTENSIONS.contains(extension)) {
return file;
}
}
return null;
}
public static File getEntryFile(Context context, MusicDirectory.Entry entry) {
if(entry.isDirectory()) {
if (entry.isDirectory()) {
return getAlbumDirectory(context, entry);
} else {
return getSongFile(context, entry);
@ -120,7 +103,7 @@ public class FileUtil {
}
fileName.append(fileSystemSafe(song.getTitle()));
if(fileName.length() >= MAX_FILENAME_LENGTH) {
if (fileName.length() >= MAX_FILENAME_LENGTH) {
fileName.setLength(MAX_FILENAME_LENGTH);
}
@ -138,6 +121,7 @@ public class FileUtil {
File playlistDir = getPlaylistDirectory(context, server);
return new File(playlistDir, fileSystemSafe(name) + ".m3u");
}
public static void writePlaylistFile(Context context, File file, MusicDirectory playlist) throws IOException {
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw);
@ -145,25 +129,27 @@ public class FileUtil {
fw.write("#EXTM3U\n");
for (MusicDirectory.Entry e : playlist.getChildren()) {
String filePath = FileUtil.getSongFile(context, e).getAbsolutePath();
if(! new File(filePath).exists()){
if (!new File(filePath).exists()) {
String ext = FileUtil.getExtension(filePath);
String base = FileUtil.getBaseName(filePath);
filePath = base + ".complete." + ext;
}
fw.write(filePath + "\n");
}
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to save playlist: " + playlist.getName());
} finally {
bw.close();
fw.close();
}
}
public static File getPlaylistDirectory(Context context) {
File playlistDir = new File(getSubsonicDirectory(context), "playlists");
ensureDirectoryExistsAndIsReadWritable(playlistDir);
return playlistDir;
}
public static File getPlaylistDirectory(Context context, String server) {
File playlistDir = new File(getPlaylistDirectory(context), server);
ensureDirectoryExistsAndIsReadWritable(playlistDir);
@ -171,9 +157,9 @@ public class FileUtil {
}
public static File getAlbumArtFile(Context context, MusicDirectory.Entry entry) {
if(entry.getId().indexOf(ImageLoader.PLAYLIST_PREFIX) != -1) {
if (entry.getId().contains(ImageLoader.PLAYLIST_PREFIX)) {
File dir = getAlbumArtDirectory(context);
return new File(dir, Util.md5Hex(ImageLoader.PLAYLIST_PREFIX + entry.getTitle()) + ".jpeg");
return new File(dir, Util.md5Hex(ImageLoader.PLAYLIST_PREFIX + entry.getTitle()) + ".jpeg");
} else {
File albumDir = getAlbumDirectory(context, entry);
File artFile;
@ -191,10 +177,11 @@ public class FileUtil {
}
}
public static File getAlbumArtFile(File albumDir) {
private static File getAlbumArtFile(File albumDir) {
return new File(albumDir, Constants.ALBUM_ART_FILE);
}
public static File getHexAlbumArtFile(Context context, File albumDir) {
private static File getHexAlbumArtFile(Context context, File albumDir) {
return new File(getAlbumArtDirectory(context), Util.md5Hex(albumDir.getPath()) + ".jpeg");
}
@ -214,25 +201,7 @@ public class FileUtil {
return null;
}
public static File getMiscDirectory(Context context) {
File dir = new File(getSubsonicDirectory(context), "misc");
ensureDirectoryExistsAndIsReadWritable(dir);
ensureDirectoryExistsAndIsReadWritable(new File(dir, ".nomedia"));
return dir;
}
public static File getMiscFile(Context context, String url) {
return new File(getMiscDirectory(context), Util.md5Hex(url) + ".jpeg");
}
public static Bitmap getMiscBitmap(Context context, String url, int size) {
return null;
}
public static Bitmap getSampledBitmap(byte[] bytes, int size) {
return getSampledBitmap(bytes, size, true);
}
public static Bitmap getSampledBitmap(byte[] bytes, int size, boolean allowUnscaled) {
final BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opt);
@ -240,19 +209,17 @@ public class FileUtil {
opt.inSampleSize = Util.calculateInSampleSize(opt, size, Util.getScaledHeight(opt.outHeight, opt.outWidth, size));
opt.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opt);
if(bitmap == null) {
if (bitmap == null) {
return null;
} else {
return getScaledBitmap(bitmap, size, allowUnscaled);
return getScaledBitmap(bitmap, size);
}
}
public static Bitmap getScaledBitmap(Bitmap bitmap, int size) {
return getScaledBitmap(bitmap, size, true);
}
public static Bitmap getScaledBitmap(Bitmap bitmap, int size, boolean allowUnscaled) {
private static Bitmap getScaledBitmap(Bitmap bitmap, int size) {
// Don't waste time scaling if the difference is minor
// Large album arts still need to be scaled since displayed as is on now playing!
if(allowUnscaled && size < 400 && bitmap.getWidth() < (size * 1.1)) {
if (size < 400 && bitmap.getWidth() < (size * 1.1)) {
return bitmap;
} else {
return Bitmap.createScaledBitmap(bitmap, size, Util.getScaledHeight(bitmap, size), true);
@ -267,12 +234,11 @@ public class FileUtil {
}
public static File getArtistDirectory(Context context, Artist artist) {
File dir = new File(getMusicDirectory(context).getPath() + "/" + fileSystemSafe(artist.getName()));
return dir;
return new File(getMusicDirectory(context).getPath() + "/" + fileSystemSafe(artist.getName()));
}
public static File getArtistDirectory(Context context, MusicDirectory.Entry artist) {
File dir = new File(getMusicDirectory(context).getPath() + "/" + fileSystemSafe(artist.getTitle()));
return dir;
return new File(getMusicDirectory(context).getPath() + "/" + fileSystemSafe(artist.getTitle()));
}
public static File getAlbumDirectory(Context context, MusicDirectory.Entry entry) {
@ -280,26 +246,26 @@ public class FileUtil {
if (entry.getPath() != null) {
File f = new File(fileSystemSafeDir(entry.getPath()));
String folder = getMusicDirectory(context).getPath();
if(entry.isDirectory()) {
if (entry.isDirectory()) {
folder += "/" + f.getPath();
} else if(f.getParent() != null) {
} else if (f.getParent() != null) {
folder += "/" + f.getParent();
}
dir = new File(folder);
} else {
MusicDirectory.Entry firstSong;
if(!Util.isOffline(context)) {
if (!Util.isOffline(context)) {
firstSong = lookupChild(context, entry, false);
if(firstSong != null) {
if (firstSong != null) {
File songFile = FileUtil.getSongFile(context, firstSong);
dir = songFile.getParentFile();
}
}
if(dir == null) {
if (dir == null) {
String artist = fileSystemSafe(entry.getArtist());
String album = fileSystemSafe(entry.getAlbum());
if("unnamed".equals(album)) {
if ("unnamed".equals(album)) {
album = fileSystemSafe(entry.getTitle());
}
dir = new File(getMusicDirectory(context).getPath() + "/" + artist + "/" + album);
@ -310,30 +276,30 @@ public class FileUtil {
public static MusicDirectory.Entry lookupChild(Context context, MusicDirectory.Entry entry, boolean allowDir) {
// Initialize lookupMap if first time called
String lookupName = Util.getCacheName(context, "entryLookup");
if(entryLookup == null) {
String lookupName = Util.getCacheName(context);
if (entryLookup == null) {
entryLookup = deserialize(context, lookupName, HashMap.class);
// Create it if
if(entryLookup == null) {
entryLookup = new HashMap<String, MusicDirectory.Entry>();
if (entryLookup == null) {
entryLookup = new HashMap<>();
}
}
// Check if this lookup has already been done before
MusicDirectory.Entry child = entryLookup.get(entry.getId());
if(child != null) {
if (child != null) {
return child;
}
// Do a special lookup since 4.7+ doesn't match artist/album to entry.getPath
String s = Util.getRestUrl(context, null, false) + entry.getId();
String cacheName = (Util.isTagBrowsing(context) ? "album-" : "directory-") + s.hashCode() + ".ser";
String s = Util.getRestUrl(context, null, false, null) + entry.getId();
String cacheName = "album-" + s.hashCode() + ".ser";
MusicDirectory entryDir = FileUtil.deserialize(context, cacheName, MusicDirectory.class);
if(entryDir != null) {
if (entryDir != null) {
List<MusicDirectory.Entry> songs = entryDir.getChildren(allowDir, true);
if(songs.size() > 0) {
if (songs.size() > 0) {
child = songs.get(0);
entryLookup.put(entry.getId(), child);
serialize(context, entryLookup, lookupName);
@ -353,22 +319,14 @@ public class FileUtil {
}
}
private static File createDirectory(Context context, String name) {
File dir = new File(getSubsonicDirectory(context), name);
if (!dir.exists() && !dir.mkdirs()) {
Log.e(TAG, "Failed to create " + name);
}
return dir;
}
public static File getSubsonicDirectory(Context context) {
return context.getExternalFilesDir(null);
}
public static File getDefaultMusicDirectory(Context context) {
if(DEFAULT_MUSIC_DIR == null) {
if (DEFAULT_MUSIC_DIR == null) {
File[] dirs;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
dirs = context.getExternalMediaDirs();
} else {
dirs = ContextCompat.getExternalFilesDirs(context, null);
@ -380,7 +338,7 @@ public class FileUtil {
Log.e(TAG, "Failed to create default dir " + DEFAULT_MUSIC_DIR);
// Some devices seem to have screwed up the new media directory API. Go figure. Try again with standard locations
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
dirs = ContextCompat.getExternalFilesDirs(context, null);
DEFAULT_MUSIC_DIR = new File(getBestDir(dirs), "music");
@ -395,13 +353,14 @@ public class FileUtil {
return DEFAULT_MUSIC_DIR;
}
private static File getBestDir(File[] dirs) {
// Past 5.0 we can query directly for SD Card
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
for(int i = 0; i < dirs.length; i++) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
for (File dir : dirs) {
try {
if (dirs[i] != null && Environment.isExternalStorageRemovable(dirs[i])) {
return dirs[i];
if (dir != null && Environment.isExternalStorageRemovable(dir)) {
return dir;
}
} catch (Exception e) {
Log.e(TAG, "Failed to check if is external", e);
@ -410,8 +369,8 @@ public class FileUtil {
}
// Before 5.0, we have to guess. Most of the time the SD card is last
for(int i = dirs.length - 1; i >= 0; i--) {
if(dirs[i] != null) {
for (int i = dirs.length - 1; i >= 0; i--) {
if (dirs[i] != null) {
return dirs[i];
}
}
@ -425,38 +384,43 @@ public class FileUtil {
File dir = new File(path);
return ensureDirectoryExistsAndIsReadWritable(dir) ? dir : getDefaultMusicDirectory(context);
}
public static boolean deleteMusicDirectory(Context context) {
public static void deleteMusicDirectory(Context context) {
File musicDirectory = FileUtil.getMusicDirectory(context);
MediaStoreService mediaStore = new MediaStoreService(context);
return recursiveDelete(musicDirectory, mediaStore);
recursiveDelete(musicDirectory, mediaStore);
}
public static void deleteSerializedCache(Context context) {
for(File file: context.getCacheDir().listFiles()) {
if(file.getName().indexOf(".ser") != -1) {
for (File file : context.getCacheDir().listFiles()) {
if (file.getName().contains(".ser")) {
file.delete();
}
}
}
public static boolean deleteArtworkCache(Context context) {
public static void deleteArtworkCache(Context context) {
File artDirectory = FileUtil.getAlbumArtDirectory(context);
return recursiveDelete(artDirectory);
recursiveDelete(artDirectory);
}
public static boolean recursiveDelete(File dir) {
return recursiveDelete(dir, null);
private static void recursiveDelete(File dir) {
recursiveDelete(dir, null);
}
public static boolean recursiveDelete(File dir, MediaStoreService mediaStore) {
if (dir != null && dir.exists()) {
File[] list = dir.listFiles();
if(list != null) {
for(File file: list) {
if(file.isDirectory()) {
if(!recursiveDelete(file, mediaStore)) {
if (list != null) {
for (File file : list) {
if (file.isDirectory()) {
if (!recursiveDelete(file, mediaStore)) {
return false;
}
} else if(file.exists()) {
if(!file.delete()) {
} else if (file.exists()) {
if (!file.delete()) {
return false;
} else if(mediaStore != null) {
} else if (mediaStore != null) {
mediaStore.deleteFromMediaStore(file);
}
}
@ -470,7 +434,7 @@ public class FileUtil {
public static void deleteEmptyDir(File dir) {
try {
File[] children = dir.listFiles();
if(children == null) {
if (children == null) {
return;
}
@ -484,33 +448,12 @@ public class FileUtil {
if (children.length == 0) {
Util.delete(dir);
}
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Error while trying to delete empty dir", e);
}
}
public static void unpinSong(Context context, File saveFile) {
// Don't try to unpin a song which isn't actually pinned
if(saveFile.getName().contains(".complete")) {
return;
}
// Unpin file, rename to .complete
File completeFile = new File(saveFile.getParent(), FileUtil.getBaseName(saveFile.getName()) +
".complete." + FileUtil.getExtension(saveFile.getName()));
if(!saveFile.renameTo(completeFile)) {
Log.w(TAG, "Failed to upin " + saveFile + " to " + completeFile);
} else {
try {
new MediaStoreService(context).renameInMediaStore(completeFile, saveFile);
} catch(Exception e) {
Log.w(TAG, "Failed to write to media store");
}
}
}
public static boolean ensureDirectoryExistsAndIsReadWritable(File dir) {
private static boolean ensureDirectoryExistsAndIsReadWritable(File dir) {
if (dir == null) {
return false;
}
@ -540,13 +483,14 @@ public class FileUtil {
}
return true;
}
public static boolean verifyCanWrite(File dir) {
if(ensureDirectoryExistsAndIsReadWritable(dir)) {
if (ensureDirectoryExistsAndIsReadWritable(dir)) {
try {
File tmp = new File(dir, "checkWrite");
tmp.createNewFile();
if(tmp.exists()) {
if(tmp.delete()) {
if (tmp.exists()) {
if (tmp.delete()) {
return true;
} else {
Log.w(TAG, "Failed to delete temp file, retrying");
@ -554,7 +498,7 @@ public class FileUtil {
// This should never be reached since this is a file Audinaut created!
Thread.sleep(100L);
tmp = new File(dir, "checkWrite");
if(tmp.delete()) {
if (tmp.delete()) {
return true;
} else {
Log.w(TAG, "Failed retry to delete temp file");
@ -565,7 +509,7 @@ public class FileUtil {
Log.w(TAG, "Temp file does not actually exist");
return false;
}
} catch(Exception e) {
} catch (Exception e) {
Log.w(TAG, "Failed to create tmp file", e);
return false;
}
@ -575,12 +519,12 @@ public class FileUtil {
}
/**
* Makes a given filename safe by replacing special characters like slashes ("/" and "\")
* with dashes ("-").
*
* @param filename The filename in question.
* @return The filename with special characters replaced by hyphens.
*/
* Makes a given filename safe by replacing special characters like slashes ("/" and "\")
* with dashes ("-").
*
* @param filename The filename in question.
* @return The filename with special characters replaced by hyphens.
*/
private static String fileSystemSafe(String filename) {
if (filename == null || filename.trim().length() == 0) {
return "unnamed";
@ -618,10 +562,10 @@ public class FileUtil {
File[] files = dir.listFiles();
if (files == null) {
Log.w(TAG, "Failed to list children for " + dir.getPath());
return new TreeSet<File>();
return new TreeSet<>();
}
return new TreeSet<File>(Arrays.asList(files));
return new TreeSet<>(Arrays.asList(files));
}
public static SortedSet<File> listMediaFiles(File dir) {
@ -641,11 +585,6 @@ public class FileUtil {
return MUSIC_FILE_EXTENSIONS.contains(extension);
}
public static boolean isMusicFile(File file) {
String extension = getExtension(file.getName());
return MUSIC_FILE_EXTENSIONS.contains(extension);
}
public static boolean isPlaylistFile(File file) {
String extension = getExtension(file.getName());
return PLAYLIST_FILE_EXTENSIONS.contains(extension);
@ -675,32 +614,7 @@ public class FileUtil {
return index == -1 ? name : name.substring(0, index);
}
public static Long[] getUsedSize(Context context, File file) {
long number = 0L;
long permanent = 0L;
long size = 0L;
if(file.isFile()) {
if(isMediaFile(file)) {
if(file.getAbsolutePath().indexOf(".complete") == -1) {
permanent++;
}
return new Long[] {1L, permanent, file.length()};
} else {
return new Long[] {0L, 0L, 0L};
}
} else {
for (File child : FileUtil.listFiles(file)) {
Long[] pair = getUsedSize(context, child);
number += pair[0];
permanent += pair[1];
size += pair[2];
}
return new Long[] {number, permanent, size};
}
}
public static <T extends Serializable> boolean serialize(Context context, T obj, String fileName) {
public static <T extends Serializable> void serialize(Context context, T obj, String fileName) {
Output out = null;
try {
RandomAccessFile file = new RandomAccessFile(context.getCacheDir() + "/" + fileName, "rw");
@ -708,44 +622,28 @@ public class FileUtil {
synchronized (kryo) {
kryo.writeObject(out, obj);
}
return true;
} catch (Throwable x) {
Log.w(TAG, "Failed to serialize object to " + fileName);
return false;
} finally {
Util.close(out);
}
}
public static <T extends Serializable> T deserialize(Context context, String fileName, Class<T> tClass) {
return deserialize(context, fileName, tClass, 0);
}
public static <T extends Serializable> T deserialize(Context context, String fileName, Class<T> tClass, int hoursOld) {
Input in = null;
try {
File file = new File(context.getCacheDir(), fileName);
if(!file.exists()) {
if (!file.exists()) {
return null;
}
if(hoursOld != 0) {
Date fileDate = new Date(file.lastModified());
// Convert into hours
long age = (new Date().getTime() - fileDate.getTime()) / 1000 / 3600;
if(age > hoursOld) {
return null;
}
}
RandomAccessFile randomFile = new RandomAccessFile(file, "r");
in = new Input(new FileInputStream(randomFile.getFD()));
synchronized (kryo) {
T result = kryo.readObject(in, tClass);
return result;
return kryo.readObject(in, tClass);
}
} catch(FileNotFoundException e) {
} catch (FileNotFoundException e) {
// Different error message
Log.w(TAG, "No serialization for object from " + fileName);
return null;
@ -757,7 +655,7 @@ public class FileUtil {
}
}
public static <T extends Serializable> boolean serializeCompressed(Context context, T obj, String fileName) {
public static <T extends Serializable> void serializeCompressed(Context context, T obj, String fileName) {
Output out = null;
try {
RandomAccessFile file = new RandomAccessFile(context.getCacheDir() + "/" + fileName, "rw");
@ -765,10 +663,8 @@ public class FileUtil {
synchronized (kryo) {
kryo.writeObject(out, obj);
}
return true;
} catch (Throwable x) {
Log.w(TAG, "Failed to serialize compressed object to " + fileName);
return false;
} finally {
Util.close(out);
}
@ -781,10 +677,9 @@ public class FileUtil {
in = new Input(new InflaterInputStream(new FileInputStream(file.getFD())));
synchronized (kryo) {
T result = kryo.readObject(in, tClass);
return result;
return kryo.readObject(in, tClass);
}
} catch(FileNotFoundException e) {
} catch (FileNotFoundException e) {
// Different error message
Log.w(TAG, "No serialization compressed for object from " + fileName);
return null;

Some files were not shown because too many files have changed in this diff Show More