Compare commits

...

160 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
519 changed files with 1582 additions and 1663 deletions

View File

@ -1,6 +1,57 @@
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

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,26 +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 195
versionName '0.3.0'
minSdkVersion 21
targetSdkVersion 30
versionCode 202
versionName '0.5.1'
setProperty("archivesBaseName", "Audinaut $versionName")
resConfigs "de", "es", "fr", "hu", "nl", "pt-rPT", "ru", "sv"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
exclude 'META-INF/beans.xml'
}
lintOptions {
@ -36,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

@ -13,9 +13,11 @@
<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" />
@ -44,6 +46,8 @@
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" />

View File

@ -15,11 +15,8 @@
package net.nullsum.audinaut.activity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AlertDialog;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@ -30,6 +27,10 @@ import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
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;
import net.nullsum.audinaut.service.MusicService;
@ -58,7 +59,7 @@ 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 = findViewById(R.id.edit_shuffle_checkbox);
@ -218,12 +219,12 @@ public class EditPlayActionActivity extends SubsonicActivity {
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,11 +19,12 @@
package net.nullsum.audinaut.activity;
import android.app.Activity;
import android.app.SearchManager;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.Util;
@ -32,7 +33,7 @@ import net.nullsum.audinaut.util.Util;
*
* @author Sindre Mehus
*/
public class QueryReceiverActivity extends Activity {
public class QueryReceiverActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
@ -64,10 +65,10 @@ public class QueryReceiverActivity extends Activity {
intent.putExtra(Constants.INTENT_EXTRA_VIEW_ALBUM, true);
if (albumId.indexOf("ar-") == 0) {
intent.putExtra(Constants.INTENT_EXTRA_NAME_ARTIST, true);
albumId = albumId.replace("ar-", "");
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

@ -19,7 +19,8 @@
package net.nullsum.audinaut.activity;
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;

View File

@ -21,22 +21,11 @@ package net.nullsum.audinaut.activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.support.design.widget.NavigationView;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@ -53,6 +42,18 @@ import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.google.android.material.navigation.NavigationView;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.fragments.SubsonicFragment;
import net.nullsum.audinaut.service.DownloadService;
@ -66,8 +67,6 @@ import net.nullsum.audinaut.util.UserUtil;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.view.UpdateView;
import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@ -80,11 +79,16 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
private static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
private static String theme;
private static boolean fullScreen;
private static boolean actionbarColored;
private static ImageLoader IMAGE_LOADER;
static {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);
// If Android Pie or older, set night mode by system clock
if (Build.VERSION.SDK_INT<29) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);
} else {
// Else, for Android 10+, follow system dark mode setting
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}
}
final List<SubsonicFragment> backStack = new ArrayList<>();
@ -123,7 +127,6 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
touchscreen = false;
}
setUncaughtExceptionHandler();
applyTheme();
applyFullscreen();
super.onCreate(bundle);
@ -144,7 +147,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: {
// If request is cancelled, the result arrays are empty.
@ -175,7 +178,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
private void createCustomActionBarView() {
actionBarSpinner = (Spinner) getLayoutInflater().inflate(R.layout.actionbar_spinner, null);
if ((this instanceof SubsonicFragmentActivity || this instanceof SettingsActivity) && (Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true) || ThemeUtil.getThemeRes(this) != R.style.Theme_Audinaut_Light_No_Color)) {
if ((this instanceof SubsonicFragmentActivity || this instanceof SettingsActivity) && ThemeUtil.getThemeRes(this) != R.style.Theme_Audinaut_Light) {
actionBarSpinner.setBackground(DrawableTint.getTintedDrawableFromColor(this));
}
spinnerAdapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
@ -193,7 +196,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
// Make sure to update theme
SharedPreferences prefs = Util.getPreferences(this);
if (theme != null && !theme.equals(ThemeUtil.getTheme(this)) || fullScreen != prefs.getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false) || actionbarColored != prefs.getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)) {
if (theme != null && !theme.equals(ThemeUtil.getTheme(this)) || fullScreen != prefs.getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false)) {
restart();
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
DrawableTint.wipeTintCache();
@ -482,7 +485,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
item.setChecked(true);
}
}
drawerHeaderToggle.setImageResource(R.drawable.main_select_server_dark);
drawerHeaderToggle.setImageResource(R.drawable.main_select_server);
showingTabs = true;
}
@ -499,7 +502,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
}
}
drawerList.getMenu().setGroupCheckable(MENU_GROUP_SERVER, true, true);
drawerHeaderToggle.setImageResource(R.drawable.main_select_tabs_dark);
drawerHeaderToggle.setImageResource(R.drawable.main_select_tabs);
showingTabs = false;
}
@ -741,7 +744,6 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
}
ThemeUtil.applyTheme(this, theme);
actionbarColored = Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true);
}
private void applyFullscreen() {
@ -827,7 +829,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
if (service != null) {
new SilentBackgroundTask<Void>(this) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
service.clearIncomplete();
return null;
}
@ -836,7 +838,6 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
}
Util.setActiveServer(this, instance);
invalidate();
UserUtil.refreshCurrentUser(this);
updateDrawerHeader();
}
}
@ -864,7 +865,6 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
service.setOnline(isOffline);
}
UserUtil.seedCurrentUser(this);
this.updateDrawerHeader();
drawer.closeDrawers();
}
@ -883,50 +883,4 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
return R.id.drawer_library;
}
}
private void setUncaughtExceptionHandler() {
Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
if (!(handler instanceof SubsonicActivity.SubsonicUncaughtExceptionHandler)) {
Thread.setDefaultUncaughtExceptionHandler(new SubsonicActivity.SubsonicUncaughtExceptionHandler(this));
}
}
/**
* Logs the stack trace of uncaught exceptions to a file on the SD card.
*/
private static class SubsonicUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private final Thread.UncaughtExceptionHandler defaultHandler;
private final Context context;
private SubsonicUncaughtExceptionHandler(Context context) {
this.context = context;
defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
File file = null;
PrintWriter printWriter = null;
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo("net.nullsum.audinaut", 0);
file = new File(Environment.getExternalStorageDirectory(), "audinaut-stacktrace.txt");
printWriter = new PrintWriter(file);
printWriter.println("Subsonic version name: " + packageInfo.versionName);
printWriter.println("Subsonic version code: " + packageInfo.versionCode);
printWriter.println();
throwable.printStackTrace(printWriter);
Log.i(TAG, "Stack trace written to " + file);
} catch (Throwable x) {
Log.e(TAG, "Failed to write stack trace to " + file, x);
} finally {
Util.close(printWriter);
if (defaultHandler != null) {
defaultHandler.uncaughtException(thread, throwable);
}
}
}
}
}

View File

@ -27,17 +27,19 @@ import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.sothree.slidinguppanel.PanelSlideListener;
import com.sothree.slidinguppanel.PanelState;
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
import com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicDirectory;
@ -55,7 +57,6 @@ 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;
@ -68,8 +69,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
private static boolean infoDialogDisplayed;
private static boolean sessionInitialized = false;
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;
@ -166,9 +168,11 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
}
slideUpPanel = findViewById(R.id.slide_up_panel);
panelSlideListener = new SlidingUpPanelLayout.PanelSlideListener() {
panelSlideListener = new PanelSlideListener() {
@Override
public void onPanelSlide(View panel, float slideOffset) {}
public void onPanelSlide(View panel, float slideOffset) {
Util.hideKeyboard(panel);
}
@Override
public void onPanelStateChanged(View panel, PanelState previousState, PanelState newState) {
@ -232,7 +236,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
rewindButton = findViewById(R.id.download_rewind);
rewindButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
if (getDownloadService() != null) {
getDownloadService().rewind();
}
@ -243,7 +247,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
previousButton = findViewById(R.id.download_previous);
previousButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
if (getDownloadService() != null) {
getDownloadService().previous();
}
@ -254,7 +258,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
startButton = findViewById(R.id.download_start);
startButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
PlayerState state = getDownloadService().getPlayerState();
if (state == PlayerState.STARTED) {
getDownloadService().pause();
@ -268,7 +272,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
nextButton = findViewById(R.id.download_next);
nextButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
if (getDownloadService() != null) {
getDownloadService().next();
}
@ -279,7 +283,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
fastforwardButton = findViewById(R.id.download_fastforward);
fastforwardButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
if (getDownloadService() == null) {
return null;
}
@ -310,7 +314,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
super.onNewIntent(intent);
if (currentFragment != null && intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY) != null) {
if (slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
if (isNowPlayingOpen()) {
closeNowPlaying();
}
@ -329,7 +333,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
replaceFragment(fragment, fragment.getSupportTag());
}
} else if (intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, false)) {
if (slideUpPanel.getPanelState() != SlidingUpPanelLayout.PanelState.EXPANDED) {
if (!isNowPlayingOpen()) {
openNowPlaying();
}
} else {
@ -342,6 +346,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
@Override
public void onResume() {
resuming = true;
super.onResume();
if (getIntent().hasExtra(Constants.INTENT_EXTRA_VIEW_ALBUM)) {
@ -362,9 +367,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
getIntent().removeExtra(Constants.INTENT_EXTRA_VIEW_ALBUM);
}
UserUtil.seedCurrentUser(this);
createAccount();
runWhenServiceAvailable(() -> getDownloadService().addOnSongChangedListener(SubsonicFragmentActivity.this));
resuming = false;
}
@Override
@ -410,17 +415,19 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
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 onBackPressed() {
if (slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED && secondaryFragment == null) {
slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
} else if (slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
removeCurrent();
if (isNowPlayingOpen()) {
if (secondaryFragment == null) {
closeNowPlaying();
} else {
removeCurrent();
}
} else {
super.onBackPressed();
}
@ -428,7 +435,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
@Override
public SubsonicFragment getCurrentFragment() {
if (slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
if (isNowPlayingOpen()) {
if (secondaryFragment == null) {
return nowPlayingFragment;
} else {
@ -441,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);
@ -459,7 +466,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
@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);
@ -476,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);
@ -487,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();
}
}
@ -527,15 +534,19 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
@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 boolean isNowPlayingOpen() {
return slideUpPanel.getPanelState() == PanelState.EXPANDED;
}
private SubsonicFragment getNewFragment(String fragmentType) {
switch (fragmentType) {
case "Playlist":
@ -598,13 +609,13 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
return true;
}
}
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);
@ -693,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,12 +19,13 @@
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 androidx.appcompat.app.AppCompatActivity;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.Util;
@ -35,7 +36,7 @@ import net.nullsum.audinaut.util.Util;
*
* @author Sindre Mehus
*/
public class VoiceQueryReceiverActivity extends Activity {
public class VoiceQueryReceiverActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {

View File

@ -16,12 +16,13 @@
package net.nullsum.audinaut.adapter;
import android.content.Context;
import android.support.v7.widget.PopupMenu;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.appcompat.widget.PopupMenu;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.domain.MusicDirectory.Entry;

View File

@ -17,21 +17,17 @@ 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 androidx.appcompat.view.ActionMode;
import androidx.recyclerview.widget.RecyclerView;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.activity.SubsonicFragmentActivity;
import net.nullsum.audinaut.util.Constants;
@ -390,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;
}
@ -427,11 +412,6 @@ 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);
}
}
});
}

View File

@ -17,7 +17,6 @@ package net.nullsum.audinaut.fragments;
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;
@ -25,6 +24,8 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.ItemTouchHelper;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.adapter.DownloadFileAdapter;
import net.nullsum.audinaut.adapter.SectionAdapter;
@ -91,7 +92,7 @@ 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<>();
@ -139,7 +140,7 @@ public class DownloadFragment extends SelectRecyclerFragment<DownloadFile> imple
case R.id.menu_remove_all:
Util.confirmDialog(context, R.string.download_menu_remove_all, "", (dialog, which) -> new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
getDownloadService().clearBackground();
return null;
}

View File

@ -65,7 +65,7 @@ public class MainFragment extends SelectRecyclerFragment<Integer> {
}
@Override
public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) {
return Collections.singletonList(0);
}

View File

@ -17,10 +17,6 @@ package net.nullsum.audinaut.fragments;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
@ -38,6 +34,10 @@ import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.ViewFlipper;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.activity.SubsonicFragmentActivity;
import net.nullsum.audinaut.adapter.DownloadFileAdapter;
@ -56,17 +56,12 @@ import net.nullsum.audinaut.util.MenuUtil;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.view.AutoRepeatButton;
import net.nullsum.audinaut.view.FadeOutAnimation;
import net.nullsum.audinaut.view.FastScroller;
import net.nullsum.audinaut.view.UpdateView;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import static net.nullsum.audinaut.domain.MusicDirectory.Entry;
import static net.nullsum.audinaut.domain.PlayerState.COMPLETED;
@ -86,7 +81,9 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
private TextView emptyTextView;
private TextView songTitleTextView;
private ImageView albumArtImageView;
private ImageView albumArtBackgroundView;
private View albumArtBackgroundView;
private ImageView albumArtBackgroundImageView;
private View nowPlayingView;
private RecyclerView playlistView;
private TextView positionTextView;
private TextView durationTextView;
@ -102,11 +99,9 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
private ImageButton repeatButton;
private View toggleListButton;
private ScheduledExecutorService executorService;
private DownloadFile currentPlaying;
private int swipeDistance;
private int swipeVelocity;
private ScheduledFuture<?> hideControlsFuture;
private List<DownloadFile> songList;
private DownloadFileAdapter songListAdapter;
private boolean seekInProgress = false;
@ -154,6 +149,8 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
songTitleTextView = rootView.findViewById(R.id.download_song_title);
albumArtImageView = rootView.findViewById(R.id.download_album_art_image);
albumArtBackgroundView = rootView.findViewById(R.id.download_album_art_background);
albumArtBackgroundImageView = rootView.findViewById(R.id.download_album_art_background_image);
nowPlayingView = rootView.findViewById(R.id.now_playing_top);
positionTextView = rootView.findViewById(R.id.download_position);
durationTextView = rootView.findViewById(R.id.download_duration);
statusTextView = rootView.findViewById(R.id.download_status);
@ -191,12 +188,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
warnIfStorageUnavailable();
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
getDownloadService().previous();
return null;
}
}.execute();
setControlsVisible(true);
});
previousButton.setOnRepeatListener(() -> changeProgress(true));
@ -204,12 +200,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
warnIfStorageUnavailable();
new SilentBackgroundTask<Boolean>(context) {
@Override
protected Boolean doInBackground() throws Throwable {
protected Boolean doInBackground() {
getDownloadService().next();
return true;
}
}.execute();
setControlsVisible(true);
});
nextButton.setOnRepeatListener(() -> changeProgress(false));
@ -222,7 +217,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
pauseButton.setOnClickListener(view -> new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
getDownloadService().pause();
return null;
}
@ -230,7 +225,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
stopButton.setOnClickListener(view -> new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
getDownloadService().reset();
return null;
}
@ -240,7 +235,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
warnIfStorageUnavailable();
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
start();
return null;
}
@ -264,29 +259,23 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
break;
}
updateRepeatButton();
setControlsVisible(true);
});
toggleListButton.setOnClickListener(view -> {
toggleFullscreenAlbumArt();
setControlsVisible(true);
});
View overlay = rootView.findViewById(R.id.download_overlay_buttons);
final int overlayHeight = overlay != null ? overlay.getHeight() : -1;
albumArtImageView.setOnClickListener(view -> {
if (overlayHeight == -1 || lastY < (view.getBottom() - overlayHeight)) {
toggleFullscreenAlbumArt();
setControlsVisible(true);
}
});
// seems pointless, but allows swipe gestures to work
albumArtImageView.setOnClickListener(view -> {});
progressBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(final SeekBar seekBar) {
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
getDownloadService().seekTo(progressBar.getProgress());
return null;
}
@ -307,7 +296,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
public void onProgressChanged(final SeekBar seekBar, final int position, final boolean fromUser) {
if (fromUser) {
positionTextView.setText(Util.formatDuration(position / 1000));
setControlsVisible(true);
}
}
});
@ -318,16 +306,16 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
DownloadService downloadService = getDownloadService();
menuInflater.inflate(R.menu.nowplaying, menu);
if (Util.isOffline(context)) {
menuInflater.inflate(R.menu.nowplaying_offline, menu);
} else {
menuInflater.inflate(R.menu.nowplaying, menu);
}
if (downloadService != null && downloadService.isRemovePlayed()) {
menu.findItem(R.id.menu_remove_played).setChecked(true);
menu.findItem(R.id.menu_save_playlist).setEnabled(false);
}
if (downloadService != null) {
if (downloadService.isRemovePlayed()) {
menu.findItem(R.id.menu_remove_played).setChecked(true);
}
SharedPreferences prefs = Util.getPreferences(context);
boolean equalizerOn = prefs.getBoolean(Constants.PREFERENCES_EQUALIZER_ON, false);
if (equalizerOn) {
@ -421,7 +409,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
case R.id.menu_remove_all:
Util.confirmDialog(context, R.string.download_menu_remove_all, "", (dialog, which) -> new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
getDownloadService().setShufflePlayEnabled(false);
getDownloadService().clear();
return null;
@ -444,7 +432,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
case R.id.menu_shuffle:
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
getDownloadService().shuffle();
return null;
}
@ -472,7 +460,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
if (controller != null) {
SubsonicFragment fragment = new EqualizerFragment();
replaceFragment(fragment);
setControlsVisible(true);
return true;
}
@ -509,19 +496,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
private void onResumeHandlers() {
executorService = Executors.newSingleThreadScheduledExecutor();
setControlsVisible(true);
final DownloadService downloadService = getDownloadService();
if (downloadService == null || downloadService.getCurrentPlaying() == null || startFlipped) {
playlistFlipper.setDisplayedChild(1);
startFlipped = false;
}
if (currentPlaying == null && downloadService != null && null == downloadService.getCurrentPlaying()) {
setAlbumArt(null, false);
}
context.runWhenServiceAvailable(() -> {
if (primaryFragment) {
DownloadService downloadService1 = getDownloadService();
@ -539,12 +513,9 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
private void onPauseHandlers() {
if (executorService != null) {
DownloadService downloadService = getDownloadService();
if (downloadService != null) {
downloadService.removeOnSongChangeListener(this);
}
playlistFlipper.setDisplayedChild(0);
DownloadService downloadService = getDownloadService();
if (downloadService != null) {
downloadService.removeOnSongChangeListener(this);
}
}
@ -581,29 +552,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
return songListAdapter;
}
private void scheduleHideControls() {
if (hideControlsFuture != null) {
hideControlsFuture.cancel(false);
}
final Handler handler = new Handler();
Runnable runnable = () -> handler.post(() -> setControlsVisible(false));
hideControlsFuture = executorService.schedule(runnable, 3000L, TimeUnit.MILLISECONDS);
}
private void setControlsVisible(boolean visible) {
try {
long duration = 1700L;
FadeOutAnimation.createAndStart(rootView.findViewById(R.id.download_overlay_buttons), !visible, duration);
if (visible) {
scheduleHideControls();
}
} catch (Exception ignored) {
}
}
// Scroll to current playing/downloading.
private void scrollToCurrent() {
if (getDownloadService() == null || songListAdapter == null) {
@ -675,7 +623,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
int seekTo;
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
if (rewind) {
seekTo = downloadService.rewind();
} else {
@ -693,7 +641,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
@Override
public boolean onDown(MotionEvent me) {
setControlsVisible(true);
return false;
}
@ -713,6 +660,8 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
else if (e2.getX() - e1.getX() > swipeDistance && Math.abs(velocityX) > swipeVelocity) {
action = ACTION_PREVIOUS;
}
/*
* too finnicky, no visual feedback
// Top to Bottom swipe
else if (e2.getY() - e1.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) {
action = ACTION_FORWARD;
@ -721,13 +670,14 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
else if (e1.getY() - e2.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) {
action = ACTION_REWIND;
}
*/
if (action > 0) {
final int performAction = action;
warnIfStorageUnavailable();
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
switch (performAction) {
case ACTION_NEXT:
downloadService.next();
@ -775,7 +725,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
warnIfStorageUnavailable();
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
getDownloadService().play(item);
return null;
}
@ -810,11 +760,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
setAlbumArt(song, true);
DownloadService downloadService = getDownloadService();
if (downloadService.isShufflePlayEnabled()) {
setSubtitle(context.getResources().getString(R.string.download_playerstate_playing_shuffle));
} else {
setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex + 1, currentPlayingSize));
}
} else {
songTitleTextView.setText(null);
setAlbumArt(null, false);
@ -943,7 +888,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
getImageLoader().loadImage(albumArtImageView, song, true, crossfade);
if (Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_BLURRED_BACKGROUND, true)) {
albumArtBackgroundView.setVisibility(ImageView.VISIBLE);
getImageLoader().loadBlurImage(albumArtBackgroundView, song, true, crossfade);
getImageLoader().loadBlurImage(albumArtBackgroundImageView, song, true, crossfade);
} else {
albumArtBackgroundView.setVisibility(ImageView.GONE);
}

View File

@ -1,8 +1,6 @@
package net.nullsum.audinaut.fragments;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -10,6 +8,10 @@ import android.view.MenuItem;
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;
@ -203,7 +205,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
task.execute();
if (searchItem != null) {
searchItem.collapseActionView();
MenuItemCompat.collapseActionView(searchItem);
}
}

View File

@ -169,7 +169,7 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
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());
@ -184,8 +184,7 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
}
Indexes indexes = new Indexes();
//indexes.setArtists = artists;
indexes.sortChildren(context);
indexes.sortChildren();
items = new ArrayList<>(indexes.getArtists());
entries = dir.getChildren(false, true);

View File

@ -1,9 +1,6 @@
package net.nullsum.audinaut.fragments;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -11,6 +8,10 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
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.EntryGridAdapter;

View File

@ -1,7 +1,6 @@
package net.nullsum.audinaut.fragments;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@ -9,6 +8,8 @@ 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;

View File

@ -16,8 +16,6 @@
package net.nullsum.audinaut.fragments;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@ -25,6 +23,9 @@ import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.adapter.SectionAdapter;
import net.nullsum.audinaut.service.MusicService;
@ -176,7 +177,7 @@ 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<>();

View File

@ -51,7 +51,7 @@ 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) {
decades.add(String.valueOf(i));

View File

@ -28,6 +28,7 @@ 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.View;
@ -224,7 +225,7 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
this.findPreference("clearCache").setOnPreferenceClickListener(preference -> {
Util.confirmDialog(context, (dialog, which) -> new LoadingTask<Void>(context, false) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
FileUtil.deleteMusicDirectory(context);
FileUtil.deleteSerializedCache(context);
FileUtil.deleteArtworkCache(context);
@ -469,6 +470,12 @@ 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);
@ -523,13 +530,24 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
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);
screen.addPreference(serverUrlPreference);
screen.addPreference(serverInternalUrlPreference);
screen.addPreference(serverLocalNetworkSSIDPreference);
screen.addPreference(serverUsernamePreference);
screen.addPreference(serverPasswordPreference);
screen.addPreference(authMethodPreference);
screen.addPreference(serverTestConnectionPreference);
screen.addPreference(serverStartScanPreference);
screen.addPreference(serverOpenBrowser);
screen.addPreference(serverRemoveServerPreference);
@ -599,6 +617,42 @@ 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;

View File

@ -18,7 +18,6 @@
*/
package net.nullsum.audinaut.fragments;
import android.app.Activity;
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.Context;
@ -26,13 +25,6 @@ import android.content.SharedPreferences;
import android.media.MediaMetadataRetriever;
import android.os.Bundle;
import android.os.StatFs;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Menu;
@ -46,6 +38,16 @@ import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.core.view.MenuItemCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.activity.SubsonicActivity;
import net.nullsum.audinaut.adapter.SectionAdapter;
@ -151,7 +153,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
void onFinishSetupOptionsMenu(final Menu menu) {
searchItem = menu.findItem(R.id.menu_global_search);
if (searchItem != null) {
searchView = (SearchView) searchItem.getActionView();
searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
SearchManager searchManager = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
SearchableInfo searchableInfo = searchManager.getSearchableInfo(context.getComponentName());
if (searchableInfo == null) {
@ -1151,7 +1153,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
new LoadingTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
MediaStoreService mediaStore = new MediaStoreService(context);
FileUtil.recursiveDelete(dir, mediaStore);
return null;
@ -1176,7 +1178,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
private void deleteSongs(final List<Entry> songs) {
new LoadingTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
getDownloadService().delete(songs);
return null;
}
@ -1287,7 +1289,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
private void playNow(final List<Entry> entries, final Entry song, final String playlistName, final String playlistId) {
new LoadingTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
playNowInTask(entries, song, playlistName, playlistId);
return null;
}
@ -1331,7 +1333,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
return getCurrentAdapter().getSelected();
}
void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
List<Entry> songs = getSelectedEntries();
if (!songs.isEmpty()) {
download(songs, append, !append, playNext, shuffle);
@ -1444,7 +1446,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
boolean playNowOverride = false;
List<Entry> songs = new ArrayList<>();
public RecursiveLoader(Activity context) {
public RecursiveLoader(AppCompatActivity context) {
super(context);
musicService = MusicServiceFactory.getMusicService(context);
}

View File

@ -139,30 +139,6 @@ public class AudinautSearchProvider extends ContentProvider {
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;
id = entry.getAlbumId();
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

@ -34,6 +34,7 @@ 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;
@ -239,11 +240,15 @@ public class AudinautWidgetProvider extends AppWidgetProvider {
// Set correct drawable for pause state
if (playing) {
views.setImageViewResource(R.id.control_play, R.drawable.media_pause_dark);
views.setImageViewResource(R.id.control_play, R.drawable.widget_media_pause);
} else {
views.setImageViewResource(R.id.control_play, R.drawable.media_start_dark);
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;
@ -286,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

@ -24,21 +24,30 @@ import net.nullsum.audinaut.service.DownloadService;
import net.nullsum.audinaut.util.Constants;
public class PlayActionReceiver extends BroadcastReceiver {
private Bundle lastdata = null;
@Override
public void onReceive(Context context, Intent intent) {
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)) {
Bundle data = intent.getBundleExtra(Constants.TASKER_EXTRA_BUNDLE);
Boolean startShuffled = data.getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE);
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

@ -30,7 +30,6 @@ 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.FileUtil;
import net.nullsum.audinaut.util.ProgressListener;
import net.nullsum.audinaut.util.SilentBackgroundTask;
@ -617,35 +616,22 @@ 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 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";
}
@ -672,7 +658,7 @@ public class CachedMusicService implements MusicService {
private void checkSettingsChanged(Context context) {
int instance = musicService.getInstance(context);
String newUrl = musicService.getRestUrl(context, null, false);
String newUrl = musicService.getRestUrl(context, null, false, null);
if (!Util.equals(newUrl, restUrl)) {
cachedMusicFolders.clear();
cachedIndexes.clear();

View File

@ -32,9 +32,10 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
import android.support.v4.util.LruCache;
import android.util.Log;
import androidx.collection.LruCache;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.activity.SubsonicActivity;
import net.nullsum.audinaut.audiofx.AudioEffectsController;
@ -60,7 +61,6 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static net.nullsum.audinaut.domain.PlayerState.COMPLETED;
import static net.nullsum.audinaut.domain.PlayerState.DOWNLOADING;
import static net.nullsum.audinaut.domain.PlayerState.IDLE;
import static net.nullsum.audinaut.domain.PlayerState.PAUSED;
@ -949,8 +949,7 @@ public class DownloadService extends Service {
public synchronized void next(boolean forceStart) {
// If only one song, just skip within song
if (size() == 1 || (currentPlaying != null && !currentPlaying.isSong())) {
fastForward();
if (currentPlaying != null && !currentPlaying.isSong()) {
return;
} else if (playerState == PREPARING || playerState == PREPARED) {
return;
@ -958,8 +957,7 @@ public class DownloadService extends Service {
int index = getCurrentPlayingIndex();
int nextPlayingIndex = getNextPlayingIndex();
// Make sure to actually go to next when repeat song is on
if (index == nextPlayingIndex) {
if (index == nextPlayingIndex && size() > 1) {
nextPlayingIndex++;
}
if (index != -1 && nextPlayingIndex < size()) {

View File

@ -28,7 +28,6 @@ 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.ProgressListener;
import net.nullsum.audinaut.util.SilentBackgroundTask;
@ -87,7 +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;
void startScan(Context c) throws Exception;
void setInstance(Integer instance) throws Exception;
}

View File

@ -31,7 +31,6 @@ 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.ProgressListener;
@ -62,14 +61,15 @@ public class OfflineMusicService implements MusicService {
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 {
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()) {
@ -87,11 +87,11 @@ public class OfflineMusicService implements MusicService {
}
@Override
public MusicDirectory getMusicDirectory(String id, String artistName, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
public MusicDirectory getMusicDirectory(String id, String artistName, boolean refresh, Context context, ProgressListener progressListener) {
return getMusicDirectory(id, context);
}
private MusicDirectory getMusicDirectory(String id, Context context) throws Exception {
private MusicDirectory getMusicDirectory(String id, Context context) {
File dir = new File(id);
MusicDirectory result = new MusicDirectory();
result.setName(dir.getName());
@ -186,7 +186,7 @@ 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) {
@ -205,7 +205,7 @@ public class OfflineMusicService implements MusicService {
}
@Override
public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception {
public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) {
List<Artist> artists = new ArrayList<>();
List<Entry> albums = new ArrayList<>();
List<Entry> songs = new ArrayList<>();
@ -294,7 +294,7 @@ public class OfflineMusicService implements MusicService {
}
@Override
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) {
List<Playlist> playlists = new ArrayList<>();
File root = FileUtil.getPlaylistDirectory(context);
String lastServer = null;
@ -481,7 +481,7 @@ public class OfflineMusicService implements MusicService {
}
@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<>();
listFilesRecursively(root, children);
@ -499,7 +499,7 @@ public class OfflineMusicService implements MusicService {
}
@Override
public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception {
public void startScan(Context c) throws Exception {
throw new OfflineException();
}

View File

@ -23,6 +23,9 @@ import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.util.Log;
import androidx.annotation.Nullable;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.Genre;
import net.nullsum.audinaut.domain.Indexes;
import net.nullsum.audinaut.domain.MusicDirectory;
@ -30,7 +33,6 @@ 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.fragments.MainFragment;
import net.nullsum.audinaut.service.parser.EntryListParser;
import net.nullsum.audinaut.service.parser.ErrorParser;
@ -42,7 +44,6 @@ import net.nullsum.audinaut.service.parser.PlaylistParser;
import net.nullsum.audinaut.service.parser.PlaylistsParser;
import net.nullsum.audinaut.service.parser.RandomSongsParser;
import net.nullsum.audinaut.service.parser.SearchResult2Parser;
import net.nullsum.audinaut.service.parser.UserParser;
import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.FileUtil;
import net.nullsum.audinaut.util.Pair;
@ -54,14 +55,13 @@ import net.nullsum.audinaut.util.Util;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.FormBody;
import okhttp3.FormBody.Builder;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
@ -75,7 +75,7 @@ public class RESTMusicService implements MusicService {
@Override
public void ping(Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "ping");
String url = getRestUrl(context, "ping", null);
Request request = new Request.Builder()
.url(url)
@ -87,7 +87,7 @@ public class RESTMusicService implements MusicService {
}
public List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "getMusicFolders");
String url = getRestUrl(context, "getMusicFolders", null);
Request request = new Request.Builder()
.url(url)
@ -100,19 +100,21 @@ public class RESTMusicService implements MusicService {
@Override
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "getArtists");
Builder builder = new FormBody.Builder();
Map<String, String> parameters = new HashMap<>();
if (musicFolderId != null) {
builder.add("musicFolderId", musicFolderId);
parameters.put("musicFolderId", musicFolderId);
} else {
parameters = null;
}
RequestBody formBody = builder.build();
String url = getRestUrl(context, "getArtists", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -158,15 +160,14 @@ public class RESTMusicService implements MusicService {
}
private MusicDirectory getMusicDirectoryImpl(String id, String name, Context context) throws Exception {
String url = getRestUrl(context, "getMusicDirectory");
Map<String, String> parameters = new HashMap<>();
RequestBody formBody = new FormBody.Builder()
.add("id", id)
.build();
parameters.put("id", id);
String url = getRestUrl(context, "getMusicDirectory", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -176,15 +177,15 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getArtist(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "getArtist");
RequestBody formBody = new FormBody.Builder()
.add("id", id)
.build();
Map<String, String> parameters = new HashMap<>();
parameters.put("id", id);
String url = getRestUrl(context, "getArtist", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -194,15 +195,14 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getAlbum(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "getAlbum");
Map<String, String> parameters = new HashMap<>();
RequestBody formBody = new FormBody.Builder()
.add("id", id)
.build();
parameters.put("id", id);
String url = getRestUrl(context, "getAlbum", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -212,20 +212,17 @@ public class RESTMusicService implements MusicService {
@Override
public SearchResult search(SearchCritera critera, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "search3");
Map<String, String> parameters = new HashMap<>();
Builder builder = new FormBody.Builder();
parameters.put("query", critera.getQuery());
parameters.put("artistCount", Integer.toString(critera.getArtistCount()));
parameters.put("albumCount", Integer.toString(critera.getAlbumCount()));
parameters.put("songCount", Integer.toString(critera.getSongCount()));
builder.add("query", critera.getQuery());
builder.add("artistCount", Integer.toString(critera.getArtistCount()));
builder.add("albumCount", Integer.toString(critera.getAlbumCount()));
builder.add("songCount", Integer.toString(critera.getSongCount()));
RequestBody formBody = builder.build();
String url = getRestUrl(context, "search3", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -235,15 +232,14 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getPlaylist(boolean refresh, String id, String name, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "getPlaylist");
Map<String, String> parameters = new HashMap<>();
RequestBody formBody = new FormBody.Builder()
.add("id", id)
.build();
parameters.put("id", id);
String url = getRestUrl(context, "getPlaylist", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -253,7 +249,7 @@ public class RESTMusicService implements MusicService {
@Override
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "getPlaylists");
String url = getRestUrl(context, "getPlaylists", null);
Request request = new Request.Builder()
.url(url)
@ -266,27 +262,24 @@ public class RESTMusicService implements MusicService {
@Override
public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "createPlaylist");
Builder builder = new FormBody.Builder();
Map<String, String> parameters = new HashMap<>();
if (id != null) {
builder.add("playlistId", id);
parameters.put("playlistId", id);
}
if (name != null) {
builder.add("name", name);
parameters.put("name", name);
}
for (MusicDirectory.Entry entry : entries) {
builder.add("songId", getOfflineSongId(entry.getId(), context, progressListener));
parameters.put("songId", getOfflineSongId(entry.getId(), context, progressListener));
}
RequestBody formBody = builder.build();
String url = getRestUrl(context, "createPlaylist", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -296,15 +289,14 @@ public class RESTMusicService implements MusicService {
@Override
public void deletePlaylist(String id, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "deletePlaylist");
Map<String, String> parameters = new HashMap<>();
RequestBody formBody = new FormBody.Builder()
.add("id", id)
.build();
parameters.put("id", id);
String url = getRestUrl(context, "deletePlaylist", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -314,19 +306,17 @@ public class RESTMusicService implements MusicService {
@Override
public void addToPlaylist(String id, List<MusicDirectory.Entry> toAdd, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "updatePlaylist");
Map<String, String> parameters = new HashMap<>();
Builder builder = new FormBody.Builder();
builder.add("playlistId", id);
parameters.put("playlistId", id);
for (MusicDirectory.Entry song : toAdd) {
builder.add("songIdToAdd", getOfflineSongId(song.getId(), context, progressListener));
parameters.put("songIdToAdd", getOfflineSongId(song.getId(), context, progressListener));
}
RequestBody formBody = builder.build();
String url = getRestUrl(context, "updatePlaylist", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -336,20 +326,18 @@ public class RESTMusicService implements MusicService {
@Override
public void removeFromPlaylist(String id, List<Integer> toRemove, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "updatePlaylist");
Map<String, String> parameters = new HashMap<>();
Builder builder = new FormBody.Builder();
builder.add("playlistId", id);
parameters.put("playlistId", id);
for (Integer song : toRemove) {
builder.add("songIndexToRemove", Integer.toString(song));
parameters.put("songIndexToRemove", Integer.toString(song));
}
RequestBody formBody = builder.build();
String url = getRestUrl(context, "updatePlaylist", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -359,25 +347,23 @@ public class RESTMusicService implements MusicService {
@Override
public void overwritePlaylist(String id, String name, int toRemove, List<MusicDirectory.Entry> toAdd, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "updatePlaylist");
Map<String, String> parameters = new HashMap<>();
Builder builder = new FormBody.Builder();
builder.add("playlistId", id);
builder.add("name", name);
parameters.put("playlistId", id);
parameters.put("name", name);
for (MusicDirectory.Entry song : toAdd) {
builder.add("songIdToAdd", getOfflineSongId(song.getId(), context, progressListener));
parameters.put("songIdToAdd", getOfflineSongId(song.getId(), context, progressListener));
}
for (int i = 0; i < toRemove; i++) {
builder.add("songIndexToRemove", Integer.toString(i));
parameters.put("songIndexToRemove", Integer.toString(i));
}
RequestBody formBody = builder.build();
String url = getRestUrl(context, "updatePlaylist", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -387,19 +373,17 @@ public class RESTMusicService implements MusicService {
@Override
public void updatePlaylist(String id, String name, String comment, boolean pub, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "updatePlaylist");
Map<String, String> parameters = new HashMap<>();
Builder builder = new FormBody.Builder();
builder.add("playlistId", id);
builder.add("name", name);
builder.add("comment", comment);
builder.add("public", Boolean.toString(pub));
parameters.put("playlistId", id);
parameters.put("name", name);
parameters.put("comment", comment);
parameters.put("public", Boolean.toString(pub));
RequestBody formBody = builder.build();
String url = getRestUrl(context, "updatePlaylist", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -409,27 +393,25 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getAlbumList(String type, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "getAlbumList2");
Map<String, String> parameters = new HashMap<>();
Builder builder = new FormBody.Builder();
builder.add("type", type);
builder.add("size", Integer.toString(size));
builder.add("offset", Integer.toString(offset));
parameters.put("type", type);
parameters.put("size", Integer.toString(size));
parameters.put("offset", Integer.toString(offset));
// Add folder if it was set and is non null
int instance = getInstance(context);
if (Util.getAlbumListsPerFolder(context, instance)) {
String folderId = Util.getSelectedMusicFolderId(context, instance);
if (folderId != null) {
builder.add("musicFolderId", folderId);
parameters.put("musicFolderId", folderId);
}
}
RequestBody formBody = builder.build();
String url = getRestUrl(context, "getAlbumList2", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -439,37 +421,35 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getAlbumList(String type, String extra, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "getAlbumList2");
Map<String, String> parameters = new HashMap<>();
Builder builder = new FormBody.Builder();
builder.add("size", Integer.toString(size));
builder.add("offset", Integer.toString(offset));
parameters.put("size", Integer.toString(size));
parameters.put("offset", Integer.toString(offset));
int instance = getInstance(context);
if ("genres".equals(type)) {
builder.add("type", "byGenre");
builder.add("genre", extra);
parameters.put("type", "byGenre");
parameters.put("genre", extra);
} else if ("years".equals(type)) {
int decade = Integer.parseInt(extra);
builder.add("type", "byYear");
builder.add("fromYear", Integer.toString(decade + 9));
builder.add("toYear", Integer.toString(decade));
parameters.put("type", "byYear");
parameters.put("fromYear", Integer.toString(decade + 9));
parameters.put("toYear", Integer.toString(decade));
}
// Add folder if it was set and is non null
if (Util.getAlbumListsPerFolder(context, instance)) {
String folderId = Util.getSelectedMusicFolderId(context, instance);
if (folderId != null) {
builder.add("musicFolderId", folderId);
parameters.put("musicFolderId", folderId);
}
}
RequestBody formBody = builder.build();
String url = getRestUrl(context, "getAlbumList2", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -479,9 +459,10 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getSongList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
Builder builder = new FormBody.Builder();
builder.add("size", Integer.toString(size));
builder.add("offset", Integer.toString(offset));
Map<String, String> parameters = new HashMap<>();
parameters.put("size", Integer.toString(size));
parameters.put("offset", Integer.toString(offset));
String method;
switch (type) {
@ -501,13 +482,10 @@ public class RESTMusicService implements MusicService {
method = "getNewaddedSongs";
}
String url = getRestUrl(context, method);
RequestBody formBody = builder.build();
String url = getRestUrl(context, method, parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -517,11 +495,12 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getRandomSongs(int size, String musicFolderId, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception {
Builder builder = new FormBody.Builder();
builder.add("size", Integer.toString(size));
Map<String, String> parameters = new HashMap<>();
parameters.put("size", Integer.toString(size));
if (genre != null && !"".equals(genre)) {
builder.add("genre", genre);
parameters.put("genre", genre);
}
if (startYear != null && !"".equals(startYear)) {
// Check to make sure user isn't doing 2015 -> 2010 since Subsonic will return no results
@ -540,19 +519,16 @@ public class RESTMusicService implements MusicService {
}
}
builder.add("fromYear", startYear);
parameters.put("fromYear", startYear);
}
if (endYear != null && !"".equals(endYear)) {
builder.add("toYear", endYear);
parameters.put("toYear", endYear);
}
String url = getRestUrl(context, "getRandomSongs");
RequestBody formBody = builder.build();
String url = getRestUrl(context, "getRandomSongs", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -572,16 +548,14 @@ public class RESTMusicService implements MusicService {
return bitmap;
}
String url = getRestUrl(context, "getCoverArt");
Map<String, String> parameters = new HashMap<>();
Builder builder = new FormBody.Builder();
builder.add("id", entry.getCoverArt());
parameters.put("id", entry.getCoverArt());
RequestBody formBody = builder.build();
String url = getRestUrl(context, "getCoverArt", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -616,16 +590,15 @@ public class RESTMusicService implements MusicService {
public Response getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
OkHttpClient eagerClient = client.newBuilder()
.readTimeout(30, TimeUnit.SECONDS)
.readTimeout(Util.getNetworkTimeoutMs(context), TimeUnit.MILLISECONDS)
.build();
String url = getRestUrl(context, "stream");
Map<String, String> parameters = new HashMap<>();
Builder builder = new FormBody.Builder();
builder.add("id", song.getId());
builder.add("maxBitRate", Integer.toString(maxBitrate));
parameters.put("id", song.getId());
parameters.put("maxBitRate", Integer.toString(maxBitrate));
RequestBody formBody = builder.build();
String url = getRestUrl(context, "stream", parameters);
Request.Builder requestBuilder = new Request.Builder();
if (offset > 0) {
@ -633,7 +606,6 @@ public class RESTMusicService implements MusicService {
}
requestBuilder.url(url);
requestBuilder.post(formBody);
Request request = requestBuilder.build();
@ -643,7 +615,7 @@ public class RESTMusicService implements MusicService {
@Override
public List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "getGenres");
String url = getRestUrl(context, "getGenres", null);
Request request = new Request.Builder()
.url(url)
@ -656,27 +628,25 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception {
Builder builder = new FormBody.Builder();
builder.add("genre", genre);
builder.add("count", Integer.toString(count));
builder.add("offset", Integer.toString(offset));
Map<String, String> parameters = new HashMap<>();
parameters.put("genre", genre);
parameters.put("count", Integer.toString(count));
parameters.put("offset", Integer.toString(offset));
// Add folder if it was set and is non null
int instance = getInstance(context);
if (Util.getAlbumListsPerFolder(context, instance)) {
String folderId = Util.getSelectedMusicFolderId(context, instance);
if (folderId != null) {
builder.add("musicFolderId", folderId);
parameters.put("musicFolderId", folderId);
}
}
String url = getRestUrl(context, "getSongsByGenre");
RequestBody formBody = builder.build();
String url = getRestUrl(context, "getSongsByGenre", parameters);
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
@ -685,25 +655,17 @@ public class RESTMusicService implements MusicService {
}
@Override
public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception {
String url = getRestUrl(context, "getUser");
public void startScan(Context context) throws Exception {
String url = getRestUrl(context, "startScan", null);
RequestBody formBody = new FormBody.Builder()
.add("username", username)
.build();
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
Request request = new Request.Builder().url(url).build();
try (Response response = client.newCall(request).execute()) {
List<User> users = new UserParser(context, getInstance(context)).parse(response.body().byteStream());
if (users.size() > 0) {
// Should only have returned one anyways
return users.get(0);
if (response.isSuccessful()) {
Log.d(TAG, "Media scan started" + response.toString());
} else {
return null;
Log.w(TAG, "media scan start failed" + response.toString());
Util.toast(context, R.string.settings_media_scan_start_failed);
}
}
}
@ -729,7 +691,7 @@ public class RESTMusicService implements MusicService {
}
@Override
public void setInstance(Integer instance) throws Exception {
public void setInstance(Integer instance) {
this.instance = instance;
}
@ -742,15 +704,15 @@ public class RESTMusicService implements MusicService {
}
public String getRestUrl(Context context, String method) {
return getRestUrl(context, method, true);
public String getRestUrl(Context context, String method, @Nullable Map<String, String> parameters) {
return getRestUrl(context, method, true, parameters);
}
public String getRestUrl(Context context, String method, boolean allowAltAddress) {
public String getRestUrl(Context context, String method, boolean allowAltAddress, @Nullable Map<String, String> parameters) {
if (instance == null) {
return Util.getRestUrl(context, method, allowAltAddress);
return Util.getRestUrl(context, method, allowAltAddress, parameters);
} else {
return Util.getRestUrl(context, method, instance, allowAltAddress);
return Util.getRestUrl(context, method, instance, allowAltAddress, parameters);
}
}
}

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

@ -18,12 +18,13 @@
*/
package net.nullsum.audinaut.util;
import android.app.Activity;
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;
@ -79,8 +80,8 @@ public abstract class BackgroundTask<T> implements ProgressListener {
}
}
private Activity getActivity() {
return (context instanceof Activity) ? ((Activity) context) : null;
private AppCompatActivity getActivity() {
return (context instanceof AppCompatActivity) ? ((AppCompatActivity) context) : null;
}
Handler getHandler() {
@ -95,7 +96,7 @@ public abstract class BackgroundTask<T> implements ProgressListener {
protected void error(Throwable error) {
Log.w(TAG, "Got exception: " + error, error);
Activity activity = getActivity();
AppCompatActivity activity = getActivity();
if (activity != null) {
new ErrorDialog(activity, getErrorMessage(error), true);
}

View File

@ -2,6 +2,10 @@ 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;
@ -12,6 +16,15 @@ class BlurBuilder {
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);
@ -29,4 +42,25 @@ class BlurBuilder {
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

@ -67,10 +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_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";
@ -121,7 +123,6 @@ public final class Constants {
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_BATCH_MODE = "batchMode";

View File

@ -1,7 +1,7 @@
package net.nullsum.audinaut.util;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import net.nullsum.audinaut.adapter.SectionAdapter;
import net.nullsum.audinaut.fragments.SubsonicFragment;
@ -68,7 +68,7 @@ public class DownloadFileItemHelperCallback extends ItemTouchHelper.SimpleCallba
pendingTask = new SilentBackgroundTask<Void>(downloadService) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
boolean running = true;
while (running) {
Object nextOperation = null;

View File

@ -20,11 +20,12 @@ 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.DrawableRes;
import android.util.SparseArray;
import android.util.TypedValue;
import androidx.annotation.AttrRes;
import androidx.annotation.DrawableRes;
import net.nullsum.audinaut.R;
import java.util.WeakHashMap;

View File

@ -23,9 +23,10 @@ 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 androidx.core.content.ContextCompat;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
@ -65,7 +66,7 @@ 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> 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 final Kryo kryo = new Kryo();
@ -292,7 +293,7 @@ public class FileUtil {
}
// 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 s = Util.getRestUrl(context, null, false, null) + entry.getId();
String cacheName = "album-" + s.hashCode() + ".ser";
MusicDirectory entryDir = FileUtil.deserialize(context, cacheName, MusicDirectory.class);

View File

@ -30,13 +30,14 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.util.LruCache;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.collection.LruCache;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.Playlist;
@ -99,7 +100,7 @@ public class ImageLoader {
nowPlayingSmall = null;
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
protected Void doInBackground() {
clearingCache = true;
cache.evictAll();
clearingCache = false;

View File

@ -1,8 +1,9 @@
package net.nullsum.audinaut.util;
import android.app.Activity;
import android.app.ProgressDialog;
import androidx.appcompat.app.AppCompatActivity;
import net.nullsum.audinaut.activity.SubsonicActivity;
/**
@ -11,17 +12,17 @@ import net.nullsum.audinaut.activity.SubsonicActivity;
*/
public abstract class LoadingTask<T> extends BackgroundTask<T> {
private final Activity tabActivity;
private final AppCompatActivity tabActivity;
private final boolean cancellable;
private ProgressDialog loading;
public LoadingTask(Activity activity) {
public LoadingTask(AppCompatActivity activity) {
super(activity);
tabActivity = activity;
this.cancellable = true;
}
public LoadingTask(Activity activity, final boolean cancellable) {
public LoadingTask(AppCompatActivity activity, final boolean cancellable) {
super(activity);
tabActivity = activity;
this.cancellable = cancellable;

View File

@ -23,13 +23,16 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Handler;
import android.support.v4.app.NotificationCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.RemoteViews;
import androidx.core.app.NotificationCompat;
import androidx.media.app.NotificationCompat.MediaStyle;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.activity.SubsonicActivity;
@ -66,28 +69,52 @@ public final class Notifications {
}
final boolean playing = downloadService.getPlayerState() == PlayerState.STARTED;
RemoteViews expandedContentView = new RemoteViews(context.getPackageName(), R.layout.notification_expanded);
setupViews(expandedContentView, context, song, true, playing);
RemoteViews smallContentView = new RemoteViews(context.getPackageName(), R.layout.notification);
setupViews(smallContentView, context, song, false, playing);
Intent notificationIntent = new Intent(context, SubsonicFragmentActivity.class);
notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, true);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
final Notification notification = new NotificationCompat.Builder(context, CHANNEL_PLAYING_ID)
Intent cancelIntent = new Intent("KEYCODE_MEDIA_STOP")
.setComponent(new ComponentName(context, DownloadService.class))
.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_STOP));
int[] compactActions = new int[]{0, 1, 2};
MediaSessionCompat mediaSession = new MediaSessionCompat(context, "Audinaut");
MediaSessionCompat.Token mediaToken = mediaSession.getSessionToken();
MediaMetadataCompat.Builder metadataBuilder = new MediaMetadataCompat.Builder();
mediaSession.setMetadata(metadataBuilder
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, -1)
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, getAlbumArt(context, song))
//.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, R.drawable.notification_logo)
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.getTitle())
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.getArtist())
.build() );
MediaStyle mediaStyle = new MediaStyle()
.setShowActionsInCompactView(compactActions)
.setShowCancelButton(true)
.setCancelButtonIntent(PendingIntent.getService(context, 0, cancelIntent, 0))
.setMediaSession(mediaToken);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_PLAYING_ID)
.setChannelId(CHANNEL_PLAYING_ID)
.setSmallIcon(R.drawable.stat_notify_playing)
.setContentTitle(song.getTitle())
.setContentText(song.getTitle())
.setContentText(song.getArtist())
.setSubText(song.getAlbum())
.setTicker(song.getTitle())
.setOngoing(playing)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setCustomContentView(smallContentView)
.setCustomBigContentView(expandedContentView)
.setContentIntent(PendingIntent.getActivity(context, 0, notificationIntent, 0))
.setPriority(NotificationCompat.PRIORITY_LOW).build();
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setShowWhen(false)
.setLargeIcon(getAlbumArt(context, song))
.setSmallIcon(R.drawable.notification_logo)
.setStyle(mediaStyle)
.setContentIntent(PendingIntent.getActivity(context, 0, notificationIntent, 0));
addActions(context, builder, playing);
final Notification notification = builder.build();
if(playing) {
notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
}
playShowing = true;
if (downloadForeground && downloadShowing) {
@ -115,13 +142,7 @@ public final class Notifications {
AudinautWidgetProvider.notifyInstances(context, downloadService, playing);
}
private static void setupViews(RemoteViews rv, Context context, MusicDirectory.Entry song, boolean expanded, boolean playing) {
// Use the same text for the ticker and the expanded notification
String title = song.getTitle();
String arist = song.getArtist();
String album = song.getAlbum();
// Set the album art.
private static Bitmap getAlbumArt(Context context, MusicDirectory.Entry song) {
try {
ImageLoader imageLoader = SubsonicActivity.getStaticImageLoader(context);
Bitmap bitmap = null;
@ -130,89 +151,42 @@ public final class Notifications {
}
if (bitmap == null) {
// set default album art
rv.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
return BitmapFactory.decodeResource(context.getResources(), R.drawable.unknown_album);
} else {
imageLoader.setNowPlayingSmall(bitmap);
rv.setImageViewBitmap(R.id.notification_image, bitmap);
return bitmap;
}
} catch (Exception x) {
Log.w(TAG, "Failed to get notification cover art", x);
rv.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
return BitmapFactory.decodeResource(context.getResources(), R.drawable.unknown_album);
}
}
// set the text for the notifications
rv.setTextViewText(R.id.notification_title, title);
rv.setTextViewText(R.id.notification_artist, arist);
rv.setTextViewText(R.id.notification_album, album);
boolean persistent = Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PERSISTENT_NOTIFICATION, false);
if (persistent) {
if (expanded) {
rv.setImageViewResource(R.id.control_pause, playing ? R.drawable.notification_pause : R.drawable.notification_start);
rv.setImageViewResource(R.id.control_previous, R.drawable.notification_backward);
rv.setImageViewResource(R.id.control_next, R.drawable.notification_forward);
} else {
rv.setImageViewResource(R.id.control_previous, playing ? R.drawable.notification_pause : R.drawable.notification_start);
rv.setImageViewResource(R.id.control_pause, R.drawable.notification_forward);
rv.setImageViewResource(R.id.control_next, R.drawable.notification_close);
}
} else {
// Necessary for switching back since it appears to re-use the same layout
rv.setImageViewResource(R.id.control_previous, R.drawable.notification_backward);
rv.setImageViewResource(R.id.control_next, R.drawable.notification_forward);
}
// Create actions for media buttons
private static void addActions(final Context context, final NotificationCompat.Builder builder, final boolean playing) {
PendingIntent pendingIntent;
int previous = 0, pause, next, close = 0;
if (persistent && !expanded) {
pause = R.id.control_previous;
next = R.id.control_pause;
close = R.id.control_next;
} else {
previous = R.id.control_previous;
pause = R.id.control_pause;
next = R.id.control_next;
}
if (persistent && close == 0 && expanded) {
close = R.id.notification_close;
rv.setViewVisibility(close, View.VISIBLE);
}
if (previous > 0) {
Intent prevIntent = new Intent("KEYCODE_MEDIA_PREVIOUS");
prevIntent.setComponent(new ComponentName(context, DownloadService.class));
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
rv.setOnClickPendingIntent(previous, pendingIntent);
}
Intent prevIntent = new Intent("KEYCODE_MEDIA_PREVIOUS");
prevIntent.setComponent(new ComponentName(context, DownloadService.class));
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
builder.addAction(R.drawable.notification_media_backward, "Previous", pendingIntent);
if (playing) {
Intent pauseIntent = new Intent("KEYCODE_MEDIA_PLAY_PAUSE");
pauseIntent.setComponent(new ComponentName(context, DownloadService.class));
pauseIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
pendingIntent = PendingIntent.getService(context, 0, pauseIntent, 0);
rv.setOnClickPendingIntent(pause, pendingIntent);
builder.addAction(R.drawable.notification_media_pause, "Pause", pendingIntent);
} else {
Intent prevIntent = new Intent("KEYCODE_MEDIA_START");
prevIntent.setComponent(new ComponentName(context, DownloadService.class));
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY));
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
rv.setOnClickPendingIntent(pause, pendingIntent);
Intent playIntent = new Intent("KEYCODE_MEDIA_PLAY");
playIntent.setComponent(new ComponentName(context, DownloadService.class));
playIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY));
pendingIntent = PendingIntent.getService(context, 0, playIntent, 0);
builder.addAction(R.drawable.notification_media_start, "Play", pendingIntent);
}
Intent nextIntent = new Intent("KEYCODE_MEDIA_NEXT");
nextIntent.setComponent(new ComponentName(context, DownloadService.class));
nextIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT));
pendingIntent = PendingIntent.getService(context, 0, nextIntent, 0);
rv.setOnClickPendingIntent(next, pendingIntent);
if (close > 0) {
Intent prevIntent = new Intent("KEYCODE_MEDIA_STOP");
prevIntent.setComponent(new ComponentName(context, DownloadService.class));
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_STOP));
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
rv.setOnClickPendingIntent(close, pendingIntent);
}
builder.addAction(R.drawable.notification_media_forward, "Next", pendingIntent);
}
public static void hidePlayingNotification(final Context context, final DownloadService downloadService, Handler handler) {

View File

@ -15,7 +15,7 @@ public final class SyncUtil {
private static void checkRestURL(Context context) {
int instance = Util.getActiveServer(context);
String newURL = Util.getRestUrl(context, null, instance, false);
String newURL = Util.getRestUrl(context, null, instance, false, null);
if (url == null || !url.equals(newURL)) {
syncedPlaylists = null;
url = newURL;
@ -72,7 +72,7 @@ public final class SyncUtil {
}
private static String getPlaylistSyncFile(Context context, int instance) {
return "sync-playlist-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
return "sync-playlist-" + (Util.getRestUrl(context, null, instance, false, null)).hashCode() + ".ser";
}
public static void removeMostRecentSyncFiles(Context context) {
@ -84,7 +84,7 @@ public final class SyncUtil {
}
private static String getMostRecentSyncFile(Context context, int instance) {
return "sync-most_recent-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
return "sync-most_recent-" + (Util.getRestUrl(context, null, instance, false, null)).hashCode() + ".ser";
}
public static class SyncSet implements Serializable {

View File

@ -18,10 +18,9 @@ package net.nullsum.audinaut.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Build;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.activity.SettingsActivity;
import net.nullsum.audinaut.activity.SubsonicFragmentActivity;
public final class ThemeUtil {
private static final String THEME_DARK = "dark";
@ -32,7 +31,15 @@ public final class ThemeUtil {
public static String getTheme(Context context) {
SharedPreferences prefs = Util.getPreferences(context);
String theme = prefs.getString(Constants.PREFERENCES_KEY_THEME, null);
String theme;
if (Build.VERSION.SDK_INT<29) {
// If Android Pie or older, default to null (handled below as light)
theme = prefs.getString(Constants.PREFERENCES_KEY_THEME, null);
} else {
// Else, for Android 10+, default to follow system dark mode setting
theme = prefs.getString(Constants.PREFERENCES_KEY_THEME, THEME_DAY_NIGHT);
}
if (THEME_DAY_NIGHT.equals(theme)) {
int currentNightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
@ -58,35 +65,16 @@ public final class ThemeUtil {
}
private static int getThemeRes(Context context, String theme) {
if (context instanceof SubsonicFragmentActivity || context instanceof SettingsActivity) {
if (Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)) {
switch (theme) {
case THEME_DARK:
return R.style.Theme_Audinaut_Dark_No_Actionbar;
case THEME_BLACK:
return R.style.Theme_Audinaut_Black_No_Actionbar;
default:
return R.style.Theme_Audinaut_Light_No_Actionbar;
}
} else {
switch (theme) {
case THEME_DARK:
return R.style.Theme_Audinaut_Dark_No_Color;
case THEME_BLACK:
return R.style.Theme_Audinaut_Black_No_Color;
default:
return R.style.Theme_Audinaut_Light_No_Color;
}
}
} else {
switch (theme) {
case THEME_DARK:
return R.style.Theme_Audinaut_Dark;
case THEME_BLACK:
return R.style.Theme_Audinaut_Black;
default:
return R.style.Theme_Audinaut_Light;
}
if(theme == null)
return R.style.Theme_Audinaut_Light;
switch (theme) {
case THEME_DARK:
return R.style.Theme_Audinaut_Dark;
case THEME_BLACK:
return R.style.Theme_Audinaut_Black;
default:
return R.style.Theme_Audinaut_Light;
}
}

View File

@ -17,62 +17,8 @@ package net.nullsum.audinaut.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import net.nullsum.audinaut.domain.User;
import net.nullsum.audinaut.service.MusicServiceFactory;
public final class UserUtil {
private static final String TAG = UserUtil.class.getSimpleName();
private static int instance = -1;
private static int instanceHash = -1;
private static User currentUser;
public static void refreshCurrentUser(Context context) {
currentUser = null;
seedCurrentUser(context);
}
public static void seedCurrentUser(Context context) {
// Only try to seed if online
if (Util.isOffline(context)) {
currentUser = null;
return;
}
final int instance = Util.getActiveServer(context);
final int instanceHash = (instance == 0) ? 0 : Util.getRestUrl(context).hashCode();
if (UserUtil.instance == instance && UserUtil.instanceHash == instanceHash && currentUser != null) {
return;
} else {
UserUtil.instance = instance;
UserUtil.instanceHash = instanceHash;
}
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
currentUser = MusicServiceFactory.getMusicService(context).getUser(false, getCurrentUsername(context, instance), context, null);
return null;
}
@Override
protected void done(Void result) {
if (context instanceof AppCompatActivity) {
((AppCompatActivity) context).supportInvalidateOptionsMenu();
}
}
@Override
protected void error(Throwable error) {
// Don't do anything, supposed to be background pull
Log.e(TAG, "Failed to seed user information");
}
}.execute();
}
private static String getCurrentUsername(Context context, int instance) {
SharedPreferences prefs = Util.getPreferences(context);
return prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null);

View File

@ -17,7 +17,6 @@
*/
package net.nullsum.audinaut.util;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ComponentName;
@ -36,18 +35,23 @@ import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.Log;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.adapter.DetailsAdapter;
import net.nullsum.audinaut.domain.MusicDirectory;
@ -63,12 +67,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import okhttp3.HttpUrl;
@ -115,6 +121,11 @@ public final class Util {
editor.apply();
}
public static int getNetworkTimeoutMs(Context context) {
SharedPreferences prefs = getPreferences(context);
return Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT, "30000"));
}
public static boolean isScreenLitOnDownload(Context context) {
SharedPreferences prefs = getPreferences(context);
return prefs.getBoolean(Constants.PREFERENCES_KEY_SCREEN_LIT_ON_DOWNLOAD, false);
@ -276,23 +287,26 @@ public final class Util {
}
public static String getRestUrl(Context context) {
return getRestUrl(context, null, true);
return getRestUrl(context, null, true, null);
}
public static String getRestUrl(Context context, String method, boolean allowAltAddress) {
// used
public static String getRestUrl(Context context, String method, boolean allowAltAddress, @Nullable Map<String, String> parameters) {
SharedPreferences prefs = getPreferences(context);
int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
return getRestUrl(context, method, prefs, instance, allowAltAddress);
return getRestUrl(context, method, prefs, instance, allowAltAddress, parameters);
}
public static String getRestUrl(Context context, String method, int instance, boolean allowAltAddress) {
public static String getRestUrl(Context context, String method, int instance, boolean allowAltAddress, @Nullable Map<String, String> parameters) {
SharedPreferences prefs = getPreferences(context);
return getRestUrl(context, method, prefs, instance, allowAltAddress);
return getRestUrl(context, method, prefs, instance, allowAltAddress, parameters);
}
private static String getRestUrl(Context context, String method, SharedPreferences prefs, int instance, boolean allowAltAddress) {
private static String getRestUrl(Context context, String method, SharedPreferences prefs, int instance, boolean allowAltAddress, @Nullable Map<String, String> parameters) {
String serverUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null);
if (serverUrl == null) return "OFFLINE";
HttpUrl.Builder builder;
builder = HttpUrl.parse(serverUrl).newBuilder();
@ -318,21 +332,33 @@ public final class Util {
builder.addPathSegment("rest");
builder.addPathSegment(method + ".view");
int hash = (username + password).hashCode();
Pair<String, String> values = tokens.get(hash);
if (values == null) {
String salt = new BigInteger(130, getRandom()).toString(32);
String token = md5Hex(password + salt);
values = new Pair<>(salt, token);
tokens.put(hash, values);
builder.addQueryParameter("u", username);
if (prefs.getBoolean(Constants.PREFERENCES_KEY_AUTH_METHOD + instance, true)) {
int hash = (username + password).hashCode();
Pair<String, String> values = tokens.get(hash);
if (values == null) {
String salt = new BigInteger(130, getRandom()).toString(32);
String token = md5Hex(password + salt);
values = new Pair<>(salt, token);
tokens.put(hash, values);
}
builder.addQueryParameter("s", values.getFirst());
builder.addQueryParameter("t", values.getSecond());
} else {
builder.addQueryParameter("p", password);
}
builder.addQueryParameter("u", username);
builder.addQueryParameter("s", values.getFirst());
builder.addQueryParameter("t", values.getSecond());
builder.addQueryParameter("v", Constants.REST_PROTOCOL_VERSION_SUBSONIC);
builder.addQueryParameter("c", Constants.REST_CLIENT_ID);
if (parameters != null) {
for (Map.Entry<String, String> parameter : parameters.entrySet()) {
builder.addQueryParameter(parameter.getKey(), parameter.getValue());
}
}
return builder.build().toString();
}
@ -351,7 +377,7 @@ public final class Util {
}
private static String getBlockTokenUsePref(Context context, int instance) {
return Constants.CACHE_BLOCK_TOKEN_USE + Util.getRestUrl(context, null, instance, false);
return Constants.CACHE_BLOCK_TOKEN_USE + Util.getRestUrl(context, null, instance, false, null);
}
public static void setBlockTokenUse(Context context, int instance) {
@ -365,7 +391,7 @@ public final class Util {
}
public static String getCacheName(Context context, String name, String id) {
String s = getRestUrl(context, null, getActiveServer(context), false) + id;
String s = getRestUrl(context, null, getActiveServer(context), false, null) + id;
return name + "-" + s.hashCode() + ".ser";
}
@ -714,7 +740,7 @@ public final class Util {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
return hexEncode(md5.digest(s.getBytes(Constants.UTF_8)));
return hexEncode(md5.digest(s.getBytes(StandardCharsets.UTF_8)));
} catch (Exception x) {
throw new RuntimeException(x.getMessage(), x);
}
@ -782,9 +808,9 @@ public final class Util {
m = t.length();
}
int p[] = new int[n + 1];
int d[] = new int[n + 1];
int _d[];
int[] p = new int[n + 1];
int[] d = new int[n + 1];
int[] _d;
int i;
int j;
@ -935,7 +961,7 @@ public final class Util {
}
}
public static void startActivityWithoutTransition(Activity currentActivity, Intent intent) {
public static void startActivityWithoutTransition(AppCompatActivity currentActivity, Intent intent) {
currentActivity.startActivity(intent);
}
@ -1124,4 +1150,10 @@ public final class Util {
return random;
}
public static void hideKeyboard(View view) {
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}

View File

@ -17,7 +17,7 @@
package net.nullsum.audinaut.util.tags;
import android.support.v4.util.LruCache;
import androidx.collection.LruCache;
import java.util.HashMap;
import java.util.Vector;

View File

@ -31,8 +31,8 @@ class Common {
}
/*
** Returns a 32bit int from given byte offset in LE
*/
** Returns a 32bit int from given byte offset in LE
*/
private int b2le32(byte[] b, int off) {
int r = 0;
for (int i = 0; i < 4; i++) {
@ -50,8 +50,8 @@ class Common {
}
/*
** convert 'byte' value into unsigned int
*/
** convert 'byte' value into unsigned int
*/
int b2u(byte x) {
return (x & 0xFF);
}

View File

@ -31,7 +31,7 @@ class FlacFile extends Common {
public HashMap getTags(RandomAccessFile s) throws IOException {
int xoff = 4; // skip file magic
int retry = 64;
int r[];
int[] r;
HashMap tags = new HashMap();
for (; retry > 0; retry--) {
@ -52,8 +52,8 @@ class FlacFile extends Common {
}
/* Parses the metadata block at 'offset' and returns
** [header_size, payload_size, type, stop_after]
*/
** [header_size, payload_size, type, stop_after]
*/
private int[] parse_metadata_block(RandomAccessFile s, long offset) throws IOException {
int[] result = new int[4];
byte[] mb_head = new byte[4];

View File

@ -19,6 +19,7 @@ package net.nullsum.audinaut.util.tags;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
@ -56,8 +57,8 @@ class ID3v2File extends Common {
}
/* Parses all ID3v2 frames at the current position up until payload_len
** bytes were read
*/
** bytes were read
*/
private HashMap parse_v3_frames(RandomAccessFile s, long payload_len) throws IOException {
HashMap tags = new HashMap();
byte[] frame = new byte[10]; // a frame header is always 10 bytes
@ -105,7 +106,7 @@ class ID3v2File extends Common {
rv[1] = getDecodedString(v);
} else if (k.equals("TXXX")) {
/* A freestyle field, ieks! */
String txData[] = getDecodedString(v).split(Character.toString('\0'), 2);
String[] txData = getDecodedString(v).split(Character.toString('\0'), 2);
/* Check if we got replaygain info in key\0value style */
if (txData.length == 2) {
if (txData[0].matches("^(?i)REPLAYGAIN_(ALBUM|TRACK)_GAIN$")) {
@ -156,13 +157,13 @@ class ID3v2File extends Common {
int ID3_ENC_UTF16BE = 0x02;
int ID3_ENC_UTF16LE = 0x01;
if (encid == ID3_ENC_LATIN) {
v = new String(raw, 1, len - 1, "ISO-8859-1");
v = new String(raw, 1, len - 1, StandardCharsets.ISO_8859_1);
} else if (encid == ID3_ENC_UTF8) {
v = new String(raw, 1, len - 1, "UTF-8");
v = new String(raw, 1, len - 1, StandardCharsets.UTF_8);
} else if (encid == ID3_ENC_UTF16LE) {
v = new String(raw, 3, len - 3, "UTF-16LE");
v = new String(raw, 3, len - 3, StandardCharsets.UTF_16LE);
} else if (encid == ID3_ENC_UTF16BE) {
v = new String(raw, 3, len - 3, "UTF-16BE");
v = new String(raw, 3, len - 3, StandardCharsets.UTF_16BE);
}
} catch (Exception ignored) {
}

View File

@ -19,6 +19,7 @@ package net.nullsum.audinaut.util.tags;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
@ -38,7 +39,7 @@ class LameHeader extends Common {
s.seek(offset + 0x24);
s.read(chunk);
String lameMark = new String(chunk, 0, chunk.length, "ISO-8859-1");
String lameMark = new String(chunk, 0, chunk.length, StandardCharsets.ISO_8859_1);
if (lameMark.equals("Info") || lameMark.equals("Xing")) {
s.seek(offset + 0xAB);

View File

@ -37,7 +37,7 @@ class OggFile extends Common {
HashMap tags = new HashMap();
for (; retry > 0; retry--) {
long res[] = parse_ogg_page(s, offset);
long[] res = parse_ogg_page(s, offset);
if (res[2] == OGG_TYPE_COMMENT) {
tags = parse_ogg_vorbis_comment(s, offset + res[0], res[1]);
break;
@ -49,8 +49,8 @@ class OggFile extends Common {
/* Parses the ogg page at offset 'offset' and returns
** [header_size, payload_size, type]
*/
** [header_size, payload_size, type]
*/
private long[] parse_ogg_page(RandomAccessFile s, long offset) throws IOException {
long[] result = new long[3]; // [header_size, payload_size]
byte[] p_header = new byte[OGG_PAGE_SIZE]; // buffer for the page header
@ -93,8 +93,8 @@ class OggFile extends Common {
}
/* In 'vorbiscomment' field is prefixed with \3vorbis in OGG files
** we check that this marker is present and call the generic comment
** parset with the correct offset (+7) */
** we check that this marker is present and call the generic comment
** parset with the correct offset (+7) */
private HashMap parse_ogg_vorbis_comment(RandomAccessFile s, long offset, long pl_len) throws IOException {
final int pfx_len = 7;
byte[] pfx = new byte[pfx_len];

View File

@ -1,10 +1,11 @@
package net.nullsum.audinaut.view;
import android.content.Context;
import android.support.v7.widget.AppCompatImageButton;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.appcompat.widget.AppCompatImageButton;
public class AutoRepeatButton extends AppCompatImageButton {
private static final long initialRepeatDelay = 1000;

View File

@ -18,7 +18,6 @@ import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.preference.EditTextPreference;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
@ -28,6 +27,8 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import androidx.core.content.ContextCompat;
import net.nullsum.audinaut.R;
import java.io.File;

View File

@ -18,9 +18,10 @@
*/
package net.nullsum.audinaut.view;
import android.app.Activity;
import android.content.Intent;
import android.support.v7.app.AlertDialog;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.activity.SubsonicFragmentActivity;
@ -31,11 +32,11 @@ import net.nullsum.audinaut.util.Util;
*/
public class ErrorDialog {
public ErrorDialog(Activity activity, int messageId) {
public ErrorDialog(AppCompatActivity activity, int messageId) {
this(activity, activity.getResources().getString(messageId), false);
}
public ErrorDialog(final Activity activity, String message, final boolean finishActivityOnClose) {
public ErrorDialog(final AppCompatActivity activity, String message, final boolean finishActivityOnClose) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setIcon(android.R.drawable.ic_dialog_alert);
@ -60,7 +61,7 @@ public class ErrorDialog {
}
}
private void restart(Activity activity) {
private void restart(AppCompatActivity activity) {
Intent intent = new Intent(activity, SubsonicFragmentActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Util.startActivityWithoutTransition(activity, intent);

View File

@ -19,10 +19,6 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.AdapterDataObserver;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
@ -32,9 +28,14 @@ import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
import net.nullsum.audinaut.R;
import static android.support.v7.widget.RecyclerView.OnScrollListener;
import static androidx.recyclerview.widget.RecyclerView.OnScrollListener;
public class FastScroller extends LinearLayout {
private static final String TAG = FastScroller.class.getSimpleName();

View File

@ -16,14 +16,15 @@
package net.nullsum.audinaut.view;
import android.graphics.Rect;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class GridSpacingDecoration extends RecyclerView.ItemDecoration {
public static final int SPACING = 10;

View File

@ -20,9 +20,10 @@ import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatImageView;
public class RecyclingImageView extends AppCompatImageView {
private boolean invalidated = false;
@ -45,7 +46,7 @@ public class RecyclingImageView extends AppCompatImageView {
if (drawable instanceof BitmapDrawable) {
if (isBitmapRecycled(drawable)) {
this.setImageDrawable(null);
setInvalidated(true);
this.invalidated = true;
}
} else if (drawable instanceof TransitionDrawable) {
TransitionDrawable transitionDrawable = (TransitionDrawable) drawable;

View File

@ -177,9 +177,9 @@ public class SongView extends UpdateView2<MusicDirectory.Entry, Boolean> {
moreButton.setImageResource(moreImage);
this.moreImage = moreImage;
}
} else if (this.moreImage != R.drawable.download_none_light) {
} else if (this.moreImage != R.drawable.download_none) {
moreButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.download_none));
this.moreImage = R.drawable.download_none_light;
this.moreImage = R.drawable.download_none;
}
if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFileExists) {

View File

@ -22,7 +22,6 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@ -30,6 +29,8 @@ import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.recyclerview.widget.RecyclerView;
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.util.DrawableTint;

View File

@ -1,11 +1,15 @@
package net.nullsum.audinaut.domain
import android.content.Context
import java.io.Serializable
import java.util.*
class Indexes(var shortcuts: MutableList<Artist> = mutableListOf(),
var artists: MutableList<Artist> = mutableListOf(),
var entries: MutableList<MusicDirectory.Entry> = mutableListOf()) : Serializable {
fun sortChildren(context: Context) {
fun sortChildren() {
shortcuts.sortBy { s -> s.id.toLowerCase(Locale.ROOT) }
artists.sortBy { a -> a.name.toLowerCase(Locale.ROOT) }
entries.sortBy { e -> e.artist.toLowerCase(Locale.ROOT) }
}
}

View File

@ -0,0 +1,5 @@
<adaptive-icon
xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 B

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 907 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 555 B

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