Compare commits

...

1157 Commits

Author SHA1 Message Date
Óscar García Amor 90205fe0fb
Update README.md 2022-07-08 18:49:07 +02:00
Óscar García Amor 2165ce75b3
Merge branch 'develop' of github.com:ultrasonic/ultrasonic into develop 2022-07-08 12:42:04 +02:00
Óscar García Amor 8b3ee0a8d6
Migrate to GitLab 2022-07-08 12:41:53 +02:00
birdbird 695b2df63f
Merge pull request #750 from ultrasonic/offline/trackDao
Finish offline ID3 support
2022-07-08 09:20:40 +02:00
tzugen 798d795e81
Add Album list support in Offline 2022-07-07 19:20:40 +02:00
tzugen ecfce59e0f
Add clearer warning to ID3 offline setting 2022-07-07 18:53:36 +02:00
tzugen de0cb7713b
Improve offline support for Compilations 2022-07-06 15:35:34 +02:00
tzugen 78bfab3753
Conditionally hide offline Id3 Setting 2022-07-06 12:26:48 +02:00
tzugen b955d77152
Make Id3 offline dependent on Id3 2022-07-06 11:16:53 +02:00
tzugen b11694d6a2
Fix logic whether to showArtistPicture 2022-07-06 11:16:52 +02:00
tzugen 31a1fdace1
Formatting 2022-07-06 11:16:52 +02:00
tzugen 5b03b632fd
Fix three tests. 2022-07-06 11:16:52 +02:00
tzugen 152b1d261a
Fix two tests. 2022-07-06 11:16:52 +02:00
tzugen 53a1a5545a
Formatting.
Disable line length check in detekt. It's being guarded by KtLint already.
2022-07-06 11:16:52 +02:00
tzugen ad54db5bcb
Make Ids composite of Item Id + Server Id 2022-07-06 11:16:52 +02:00
tzugen 177329abcf
Add Migration 2022-07-06 11:16:52 +02:00
tzugen 241e51015f
Clean & formatting
Update room 2.4.0 -> 2.4.2
2022-07-06 11:16:52 +02:00
tzugen 60dbe70ca5
Add code to Downloader 2022-07-06 11:16:52 +02:00
tzugen 8490f7115d
Add Offline support for tracks 2022-07-06 11:16:52 +02:00
tzugen ee67f4c744
Add track Dao 2022-07-06 11:16:52 +02:00
tzugen 3a3bd10fdb
Add AlbumDao, rename getArtist to getAlbumsOfArtist 2022-07-06 11:16:52 +02:00
birdbird 3445576dc9
Merge pull request #775 from ultrasonic/seekBar
Add more values to preload count
2022-07-06 10:46:56 +02:00
tzugen 8c40f662a1
Add more values to preload count 2022-07-06 08:49:29 +02:00
birdbird 6c6227ce41
Merge pull request #770 from Maxmystere/notification/add-rating
Add song rating to notification
2022-07-05 19:31:18 +02:00
tzugen 240a2fa8f6
Fix a bug that the musicService was incorrectly cached... 2022-07-05 19:21:07 +02:00
tzugen 7de775dc26
Formatting 2022-07-05 18:38:26 +02:00
birdbird d034fc9c71
Update AutoMediaBrowserCallback.kt 2022-07-05 18:34:24 +02:00
birdbird 05ada9297d
Update MediaNotificationProvider.kt 2022-07-05 18:18:02 +02:00
Maxence G aa6c037b20
Remove useless request 2022-07-04 18:55:07 +02:00
Maxence G b8c924be27
MainThreadExecutor to Kotlin 2022-07-04 18:42:14 +02:00
Maxence G 0929a6a1bd
Lint 2022-07-03 18:40:58 +02:00
Maxence G fefee74a66
Use MainThreadExecutor 2022-07-03 18:38:29 +02:00
Maxence G 37e3ce09c1
Update notification on Rating change 2022-07-03 18:23:22 +02:00
Maxence G 16b3fcad32
Final lint 2022-07-02 21:58:45 +02:00
Maxence G d6aebd9989
Lint 2022-07-02 21:51:28 +02:00
Maxence G 3f408600cb
Add const for custom SessionCommands
Improve rating call
2022-07-02 21:47:32 +02:00
Maxence G 9014b47b74
Add song rating to notification 2022-07-02 01:27:12 +02:00
tzugen ac489ae8b9
Merge pull request #764 from ultrasonic/hmpr
Merge update build tools #755 by Holger Müller
2022-06-22 13:40:15 +02:00
tzugen e7f8fa21cb
Merge update build tools #755 by Holger Müller
Squashed commit of the following:

commit 4491c65b1b
Merge: 51ff716f 77865a14
Author: tzugen <67737443+tzugen@users.noreply.github.com>
Date:   Tue Jun 21 20:50:05 2022 +0200

    Merge branch 'develop' into gradle-update

commit 51ff716ff5
Author: Holger Müller <github@euhm.de>
Date:   Tue Jun 21 20:38:52 2022 +0200

    fixed lint warning

commit 18c31a5704
Author: Holger Müller <github@euhm.de>
Date:   Tue Jun 21 20:38:35 2022 +0200

    fixed lint warning

commit 603654c262
Author: Holger Müller <github@euhm.de>
Date:   Tue Jun 21 20:37:51 2022 +0200

    API is > lollipop ... target removed

commit b38a7211de
Author: Holger Müller <github@euhm.de>
Date:   Tue Jun 21 20:37:07 2022 +0200

    new created after fixes

commit 4929a526f7
Author: tzugen <tzugen@riseup.net>
Date:   Tue Jun 21 10:43:16 2022 +0200

    Disable ObsoleteLintCustomCheck

commit d0c30f0b6b
Author: tzugen <tzugen@riseup.net>
Date:   Tue Jun 21 10:14:06 2022 +0200

    Update more libs

commit e2fa447bbf
Merge: d4ead495 ff9c7b24
Author: tzugen <67737443+tzugen@users.noreply.github.com>
Date:   Tue Jun 21 09:47:03 2022 +0200

    Merge branch 'develop' into gradle-update

commit d4ead49548
Merge: 2dac6a7e 9a73d72f
Author: Holger Müller <github@euhm.de>
Date:   Tue Jun 21 08:50:42 2022 +0200

    merged with develop branch

commit 2dac6a7e01
Author: Holger Müller <github@euhm.de>
Date:   Mon Jun 20 21:45:22 2022 +0200

    update to android image tag 2022.06.1

commit f3dc259c39
Author: Holger Müller <github@euhm.de>
Date:   Mon Jun 20 20:56:37 2022 +0200

    rebuild lint-baseline.xml

commit c71bc1212a
Author: Holger Müller <github@euhm.de>
Date:   Mon Jun 20 20:55:00 2022 +0200

    removed unneeded cast

commit eca136dabe
Author: Holger Müller <github@euhm.de>
Date:   Fri Jun 17 23:58:37 2022 +0200

    commit signed

commit 540f476334
Author: Holger Müller <github@euhm.de>
Date:   Fri Jun 17 23:40:59 2022 +0200

    commit signed

    Signed-off-by: Holger Müller <github@euhm.de>

commit 986bd013a4
Author: Holger Müller <github@euhm.de>
Date:   Fri Jun 17 23:27:20 2022 +0200

    push to latest gradle version, set targetSdk to 33

Signed-off-by: tzugen <tzugen@riseup.net>
2022-06-22 13:35:19 +02:00
tzugen b1c3cabfef
Update README.md (#766) 2022-06-21 22:23:10 +02:00
tzugen 77865a143d
Enable schema export (needed for AutoMigration generator) (#757)
* Enable schema export (needed for AutoMigration generator)
* Add Schema folder to GIT
2022-06-21 10:07:03 +02:00
Óscar García Amor ff9c7b2435
Merge pull request #761 from ultrasonic/fix_#759
Fix #759 , a crash when the artist name was empty.
2022-06-21 08:52:54 +02:00
Óscar García Amor 737563bf6b
Merge branch 'develop' into fix_#759 2022-06-21 08:24:52 +02:00
tzugen 9a73d72fa4
Merge pull request #762 from ultrasonic/tzugen-patch-1
Update CONTRIBUTING.md
2022-06-21 06:38:35 +02:00
tzugen 98ce519014
Merge branch 'develop' into tzugen-patch-1 2022-06-20 23:05:21 +02:00
tzugen 83fc54d332
Merge pull request #758 from Maxmystere/media3-beta01
Upgrade to media3-beta01
2022-06-20 23:04:02 +02:00
Maxence G a2b9c6b9a3
Final final version good v3.2 2022-06-20 19:54:26 +02:00
Maxence G 5ae56d26c5
Merge branch 'media3-beta01' of github.com:Maxmystere/ultrasonic into media3-beta01 2022-06-20 19:50:15 +02:00
Maxence G 4efb6dcb58
Up lint 2022-06-20 19:49:17 +02:00
tzugen 8a90e98989
Update CONTRIBUTING.md 2022-06-20 19:35:46 +02:00
tzugen 46a8f4640d
Fix #759 , a crash when the artist name was empty. 2022-06-20 10:12:45 +02:00
tzugen ab41966943
Readd comment 2022-06-20 09:49:49 +02:00
tzugen 00d7ce326c
Rm comment 2022-06-20 09:48:28 +02:00
Maxence G bc4b0aa832
final fix checkstyle v2 2022-06-19 23:51:18 +02:00
Maxence G 23fd336ffd
Fix lint 2022-06-19 23:46:01 +02:00
Maxence G b57a973510
Fix static analysis 2022-06-19 23:29:07 +02:00
Maxence G 8796006ced
Fix checkstyle 2022-06-19 23:24:23 +02:00
Maxence G 545b65921e
Put Previous/Play/Next in compact notification 2022-06-19 19:43:55 +02:00
Maxence G cf367ead92
Add back notification icon 2022-06-19 18:40:04 +02:00
Maxence G 9961213f09
Upgrade to media3-beta01 2022-06-19 18:21:33 +02:00
tzugen 5deb7d4d58
Merge pull request #749 from ultrasonic/ready/removeJacoco
Remove Jacoco, closes #751
2022-06-18 22:37:12 +02:00
tzugen 5f31eaaffe
Merge branch 'develop' into ready/removeJacoco 2022-06-18 15:36:46 +02:00
tzugen cad6477cd9
Merge pull request #754 from hmueller01/target-sdk-31
Target sdk 31 into develop
2022-06-17 18:14:22 +02:00
tzugen b440821ea8
Tweak backup rules 2022-06-17 13:36:41 +02:00
Holger Müller 8663b9d50e
Target SDK 31 2022-06-17 13:27:30 +02:00
Óscar García Amor 2bae243be0
Merge branch 'develop' into ready/removeJacoco 2022-06-17 10:29:46 +02:00
Óscar García Amor 139e810186
Include beta tags on builds 2022-06-17 09:46:37 +02:00
tzugen 66443ba018
Remove Jacoco 2022-06-16 16:40:59 +02:00
tzugen f8b78a47d2
Merge pull request #748 from ultrasonic/tzugen-patch-1
Typo
2022-06-16 16:31:04 +02:00
tzugen 4cda114f4c
Typo 2022-06-16 16:22:37 +02:00
tzugen d8b5b774ee
Merge pull request #726 from ultrasonic/media3-flat
Implement Media3
2022-06-16 13:47:36 +02:00
tzugen b6730f5a93
Reset widget on manual exit as well. 2022-06-08 10:02:04 +02:00
tzugen 87c160610f
Clear widget when service is being shutdown 2022-06-08 09:17:14 +02:00
tzugen 70f8b75019
Completely stop and release the player on dismissing the app. 2022-06-07 16:18:26 +02:00
tzugen 147d7cd46e
Fix reappearing notification 2022-06-07 16:00:51 +02:00
tzugen 59e37e62a6
Call stopForeground(yes) 2022-06-07 13:30:33 +02:00
tzugen 1e571e165c
Don't ecit the process, and reinit Koin on resume 2022-06-06 22:12:16 +02:00
tzugen 5e0dd14c4f
Shutdown service on exit button press 2022-05-31 20:53:58 +02:00
tzugen 53ae0cd232
Update copyright 2022-05-31 10:18:23 +02:00
tzugen 608f86ac5f
Always call prepare before starting playback,
otherwise resuming playback is difficult when the session was dismissed...
2022-05-30 16:24:24 +02:00
tzugen 669b51c0d2
Merge remote-tracking branch 'origin/develop' into media3-flat 2022-05-30 16:11:59 +02:00
tzugen 6e1478d896
Pause playback when swiping away... 2022-05-30 16:10:54 +02:00
Nite d9e4b8b3d3
Merge pull request #737 from i-do-cpp/develop
Support HTTP basic authentication
2022-05-17 17:49:07 +02:00
Nite f790e29add
Fixed AndroidManifest to receive Bluetooth events
Set parallel downloads to 2 so the third thread can stream for playback
2022-05-10 18:15:37 +02:00
Nite faf07f2887
Fixed not closed connection in CachedDataSource 2022-05-06 18:51:12 +02:00
i-do-cpp 057644f592
Support HTTP basic authentication 2022-05-05 20:41:04 +02:00
tzugen 926081f84c
Add exported attributes, fix lint 2022-05-04 11:59:48 +02:00
tzugen 4a00494647
Readd Headphone plug and Bluetooth listeners 2022-05-04 11:52:45 +02:00
Nite 34e0178db3
Fixed Lint errors 2022-05-02 20:47:38 +02:00
Nite cbe3992b01
Fixed Notification Ids to be different for Downloader and Player
Fixed multiple start of periodic CheckDownloads
2022-05-02 20:46:01 +02:00
Nite 46846bd5c9
Incremented max playlist size
Fixed DownloadService start
Minor fixes
2022-04-24 08:44:36 +02:00
tzugen 707339b88b
Try to fix the mess :) 2022-04-22 21:03:57 +02:00
tzugen 827654c0c1
Create lock before launching the coroutine,
return always a copy of the list and not the real one.
2022-04-21 20:24:43 +02:00
tzugen 1d236aa6e3
RM unused PlayerState.kt 2022-04-21 14:52:11 +02:00
tzugen 9cdba9a27a
Ensure main thread usage in Rx subscription. 2022-04-21 14:42:24 +02:00
tzugen 7ba599f58c
Remove throttle again, causes calls from wrong thread... 2022-04-21 14:39:16 +02:00
tzugen 2e1e627b7a
- Set Wake mode flag according to Offline status
- Use Rx to trigger CheckDownloads()
- Fix #680 by listening to PositionDiscontinuity
- Throttle RxEvents
2022-04-21 10:45:37 +02:00
tzugen d550eabf88
Always call prepare on adding items 2022-04-21 10:12:05 +02:00
tzugen dda86b42c7
Fix prefs 2022-04-20 22:54:59 +02:00
tzugen b6e890b26c
Cleanup 2022-04-20 22:07:50 +02:00
tzugen c2ac1d436f
Use previous values for channel id 2022-04-20 21:56:30 +02:00
tzugen 2aaa3c2119
Fix de(serilization) 2022-04-20 21:40:22 +02:00
tzugen 5d4aff1f21
Cleanup restore 2022-04-20 20:58:10 +02:00
tzugen 6115ac995f
Remove legacyPlayerState 2022-04-20 20:58:10 +02:00
tzugen 647435fe55
launch restore on Main Thread 2022-04-20 20:57:51 +02:00
tzugen 81d24f6cbb
Cleanup MediaPlayerLifecycleSupport.kt 2022-04-20 17:06:30 +02:00
tzugen 69c78f4c37
Enable StrictMode logging, rm unused prefs 2022-04-20 13:15:22 +02:00
tzugen 3691428a68
Squashed commit of the following:
commit 7a8f20ade7
Author: Holger Müller <github@euhm.de>
Date:   Fri Apr 8 23:21:15 2022 +0200

    make build working with target sdk 31

commit 5890c32b7e
Author: Holger Müller <github@euhm.de>
Date:   Fri Apr 8 22:57:23 2022 +0200

    make build working with target sdk 31

commit 1c7c4839b3
Merge: 48b0cdea 92ef78a3
Author: Holger Müller <github@euhm.de>
Date:   Fri Apr 8 21:55:14 2022 +0200

    Merge remote-tracking branch 'upstream/media3-flat' into media3-flat

commit 48b0cdea83
Merge: 9101980c 3ca25ed1
Author: Holger Müller <github@euhm.de>
Date:   Fri Apr 8 19:58:55 2022 +0200

    Merge remote-tracking branch 'upstream/media3-flat' into media3-flat

commit 9101980cb6
Merge: 515690ab 97798446
Author: Holger Müller <github@euhm.de>
Date:   Fri Apr 8 17:07:54 2022 +0200

    Merge remote-tracking branch 'upstream/media3-flat' into media3-flat

commit 515690abac
Author: Holger Müller <github@euhm.de>
Date:   Thu Apr 7 08:55:42 2022 +0200

    made button off mode better visible
2022-04-17 06:58:39 +02:00
tzugen 788538ee6a
Squashed commit of the following:
commit c7d9af4529
Author: Holger Müller <github@euhm.de>
Date:   Sat Apr 16 17:56:41 2022 +0200

    fixed CI failure

commit 59e18bc5c0
Author: Holger Müller <github@euhm.de>
Date:   Sat Apr 16 17:42:49 2022 +0200

    removed unused dataSourceFactory, commented debug output
2022-04-17 06:55:24 +02:00
tzugen 762aeec5d3
Cleanup 2022-04-16 21:20:10 +02:00
tzugen a3a0c7f41d
Minor 2022-04-16 11:23:05 +02:00
tzugen 1564379bd1
Fix a bunch of problems with the DataSources 2022-04-16 00:04:52 +02:00
tzugen 7d33770fd6
Add some more logging calls 2022-04-12 17:12:17 +02:00
tzugen 728afad00c
Merge remote-tracking branch 'origin/develop' into media3-flat 2022-04-12 17:04:35 +02:00
Óscar García Amor f121e297df
Fix publish github signed apk CI step (again) 2022-04-11 11:58:57 +02:00
Óscar García Amor 3f2cfb131a
Fix publish github signed apk CI step 2022-04-11 11:40:01 +02:00
Óscar García Amor b8b4b81726
Fix sign release apk CI step 2022-04-11 11:27:02 +02:00
Óscar García Amor 383089a409
Improve CI pipeline 2022-04-11 11:07:29 +02:00
Óscar García Amor 8d8a5f05ea
Bump version to 3.2.0 2022-04-11 10:12:52 +02:00
Óscar García Amor 46a2e5d67b
Update translations 2022-04-11 09:50:17 +02:00
tzugen 92ef78a36a
Move alpha value to const val. 2022-04-08 21:28:14 +02:00
tzugen e5021959c3
Fix a small bug in togglePlayPause() 2022-04-08 21:24:04 +02:00
tzugen 3ca25ed1c6
Rework ActiveServer handling.
Remove blocking call on setting the server.
Implement offline server display more cleanly.
Reconfigure the SourceFactory when the active server has changed
2022-04-08 18:08:56 +02:00
tzugen 6da83db9df
Set memory to 3.2g 2022-04-08 18:07:13 +02:00
tzugen 9779844620
Solve more simple... 2022-04-07 15:29:24 +02:00
tzugen f936ad690c
Remove GC logging 2022-04-07 15:27:52 +02:00
tzugen 5230ce011d
Make full body logging an optional debug setting. 2022-04-07 15:15:08 +02:00
Holger Müller a98c9e2ffd
fixed copy paste error with repeat button 2022-04-06 21:05:44 +02:00
tzugen 0128a8b29d
Fix memory settings 2022-04-06 21:00:27 +02:00
tzugen 41f5520f1f
Remove unused resources & update lint baseline 2022-04-05 22:52:02 +02:00
tzugen fd34199c27
Remove unused resources 2022-04-05 22:45:51 +02:00
tzugen bb77216eff
Format 2022-04-05 22:33:04 +02:00
tzugen e1f4ee15d5
Fix Widget 2022-04-05 22:24:06 +02:00
tzugen d0959ffcb5
AppWidget to Kotlin 2022-04-05 21:56:25 +02:00
tzugen 4c22c8b41b
Formating / detekt 2022-04-05 21:56:13 +02:00
tzugen ba1a1c5538
Use grayed out shuffle and repeat buttons.
Fix removing and resorting playlist.
2022-04-05 21:41:27 +02:00
tzugen 7742f67796
Add pref switch for HW offload 2022-04-05 20:58:11 +02:00
tzugen 1a69507e34
Remove a bunch of now unused prefs 2022-04-05 10:21:46 +02:00
tzugen 46fb7664c3
Fix insertAfterCurrent, Fix getUri 2022-04-05 10:10:24 +02:00
tzugen dd65a12b53
Migrate AutoMediaBrowser 2022-04-05 00:05:52 +02:00
tzugen 2f7f47783a
Enable HW playback for better performance 2022-04-05 00:05:52 +02:00
tzugen b1c2d020b5
Fix a bug in MetadataParser (unrelated to media3) 2022-04-04 18:27:59 +02:00
tzugen 5dc9fda7a4
Detekt fixes 2022-04-04 18:18:52 +02:00
tzugen 1313fb6c0c
Split Cache into dedicated datasource 2022-04-04 17:59:12 +02:00
tzugen 5966dd7299
Remove gapless setting 2022-04-04 17:43:02 +02:00
tzugen 1703f02aad
Remove unused file 2022-04-04 00:01:26 +02:00
tzugen 922022ab03
Switch to Media3 2022-04-03 23:59:18 +02:00
tzugen bfc11f9924
Don't manage ID manually, but use autoGenerate to ensure uniqueness
(across the lifetime of the db)
2022-04-03 23:54:52 +02:00
tzugen e77b5abd3e
Merge pull request #719 from ultrasonic/AlbumTrackCachePrepare
Add migration path for coming DB change
2022-03-31 12:20:27 +02:00
tzugen 988bf62acf
try was missing a catch block. 2022-03-30 16:16:59 +02:00
tzugen 1a46f7e2c6
Make it clear which ID the OfflineServer has (-1)
Also provide migration path to future version
2022-03-28 23:57:37 +02:00
tzugen 1d88c585c4
Remove migration function from 2020.
All regular users will have run the migration by now.
2022-03-28 23:57:37 +02:00
tzugen 287169649a
Merge pull request #722 from ultrasonic/ready/OnlyRename
Cleanup terminology surrounding entry vs track vs song.
2022-03-28 23:56:22 +02:00
tzugen 020f67d5e6
Merge branch 'develop' into ready/OnlyRename 2022-03-28 23:06:21 +02:00
Óscar García Amor fcc57ae316
[skip ci] Replace Airsonic with Airsonic-Advanced 2022-03-28 15:39:52 +02:00
tzugen 0fd17bfe8c
Merge pull request #721 from ultrasonic/ready/cleanupPref
Cleanup an unused preference
2022-03-28 13:20:20 +02:00
tzugen 0c016bff41
Remove unused strings 2022-03-27 20:14:18 +02:00
tzugen 12435ed9ec
Cleanup an unused preference 2022-03-27 20:09:49 +02:00
tzugen c2226ba202
Fix older detekt issues in DownloadFile and regenerate Baselinefile 2022-03-27 16:40:09 +02:00
tzugen 892b441c0d
Move Track and Album to their own files 2022-03-27 16:39:35 +02:00
tzugen e53da92dac
Unify terminology also by renaming DownloadFile.song -> DownloadFile.track 2022-03-27 16:39:35 +02:00
tzugen 2de59b2206
Rename MusicDirectory.Entry to Track to make more clear what it is 2022-03-27 16:39:35 +02:00
tzugen 34c13d7908
Merge pull request #711 from ultrasonic/shareStyles
Don't exclude the whole .idea folder
2022-03-27 15:41:14 +02:00
tzugen 88918bd839
Keep the whole .idea directory ignored. 2022-03-27 15:24:33 +02:00
tzugen f49063664b
Add weak warning for code which is not formatted according to the official style guide.
This only run in newly added/modified files.
2022-03-27 15:22:43 +02:00
tzugen 126efd35c6
Include official kotlin code style 2022-03-27 15:22:43 +02:00
tzugen a6a052781d
Don't exclude the whole .idea folder
https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
2022-03-27 15:22:43 +02:00
tzugen 107b01fd91
Merge pull request #710 from hmueller01/detailed-media-info
Add detailed song information to Now Playing
2022-03-23 13:06:22 +01:00
tzugen d05ac1489e
Merge branch 'develop' into detailed-media-info 2022-03-23 11:48:04 +01:00
Holger Müller fc94d28862
satisfy CI checker
Signed-off-by: Holger Müller <github@euhm.de>
2022-03-22 08:50:35 +01:00
Holger Müller 8bec74e66a
replaced visibility by isVisible thanks to @tzugen
Signed-off-by: Holger Müller <github@euhm.de>
2022-03-22 08:26:24 +01:00
Holger Müller acf6c5a681
removed untranslatable string from all languages
Signed-off-by: Holger Müller <github@euhm.de>
2022-03-22 08:09:53 +01:00
tzugen 81a21ce8b7
Merge pull request #708 from hmueller01/config-text-update
updated settings.download_transition
2022-03-21 23:40:43 +01:00
Holger Müller cf86101de2
updated settings.download_transition 2022-03-21 22:54:41 +01:00
Nite 5a44fcfe29
Merge pull request #713 from ultrasonic/update_circleci_img
Update CircleCi Image, it started to throw a warning:
2022-03-21 19:27:40 +01:00
Nite 2aa5174fbd
Merge branch 'develop' into update_circleci_img 2022-03-21 19:09:25 +01:00
Nite cf7cef9831
Merge pull request #653 from ultrasonic/draft/okhttp
Update OkHttp to 4.9.1
2022-03-21 19:07:50 +01:00
Nite 423957d954
Merge branch 'develop' into draft/okhttp 2022-03-21 18:51:00 +01:00
Holger Müller 0944bd2217
removed unused import to satisfy CI checker
Signed-off-by: Holger Müller <github@euhm.de>
2022-03-21 18:43:10 +01:00
Holger Müller 7b750c692c
shorten onCurrentChanged() to 60 lines to not fail CI LongMethod
Signed-off-by: Holger Müller <github@euhm.de>
2022-03-21 18:38:27 +01:00
Holger Müller c247e930c4
reworks requested by @ogarcia
Signed-off-by: Holger Müller <github@euhm.de>
2022-03-21 18:04:55 +01:00
tzugen 44d68a71da
Merge pull request #712 from ultrasonic/addLocaleChooser
Add locale chooser
2022-03-21 11:23:10 +01:00
tzugen 7a51c271ba
Merge branch 'develop' into addLocaleChooser 2022-03-21 10:00:28 +01:00
Óscar García Amor 4d91068535
Merge pull request #714 from ultrasonic/wordingAlert
Reword alert for better help
2022-03-21 09:56:25 +01:00
tzugen fe3b713241
Tweak wording 2022-03-21 09:47:46 +01:00
Holger Müller 2f5704548c
implemented @tzugen way to show the song details
Signed-off-by: Holger Müller <github@euhm.de>
2022-03-20 22:05:59 +01:00
tzugen 17850980e1
Remove traditional german variant 2022-03-20 13:35:11 +01:00
tzugen 34d2b45d71
Add Res Config 2022-03-20 13:12:47 +01:00
tzugen 555ef5b7ff
Differentiate chinese variants 2022-03-20 13:10:40 +01:00
tzugen c269243a0d
Complete traditional strings 2022-03-20 09:14:47 +01:00
tzugen 2d8b93301f
Add traditional german variant 2022-03-20 09:09:14 +01:00
tzugen e4a41de3ef
Reword alert for better help 2022-03-20 07:57:50 +01:00
tzugen f0447105d2
Fix methods in integration tests 2022-03-19 18:39:13 +01:00
tzugen 65c4f2b100
Update CircleCi Image, it started to throw a warning:
https://discuss.circleci.com/t/legacy-convenience-image-deprecation/41034
2022-03-19 17:44:12 +01:00
tzugen f6f9683a9c
Migrate some old calls 2022-03-19 17:39:59 +01:00
tzugen c2d62e8688
Disable language split in bundle 2022-03-19 17:36:46 +01:00
tzugen e153565086
Update Okhttp, Jackson and retrofit 2022-03-19 17:07:16 +01:00
tzugen f30a582c7b
Add locale chooser 2022-03-19 17:00:16 +01:00
Holger Müller cc5f29ca98
fixed failed ultrasonic:ktlintMainSourceSetCheck
Signed-off-by: Holger Müller <github@euhm.de>
2022-03-19 14:51:39 +01:00
Holger Müller 8f18192c36
fixed failed ultrasonic:ktlintMainSourceSetCheck
Signed-off-by: Holger Müller <github@euhm.de>
2022-03-19 14:38:10 +01:00
Holger Müller ae2055e324
add detailed song information to Now Playing
Signed-off-by: Holger Müller <github@euhm.de>
2022-03-18 20:46:38 +01:00
Nite dee4675715
Merge pull request #706 from hmueller01/fix-debug-file-size2
fixed output of debug file size (in MB)
2022-03-15 12:26:46 +01:00
Nite e8c31db90f
Merge branch 'develop' into fix-debug-file-size2 2022-03-15 11:33:44 +01:00
Nite 34fb63c783
Merge pull request #707 from nitehu/fix/search2-result-artist
Fixed Search result to return Artist correctly when ID3 tags are turned off
2022-03-15 11:32:55 +01:00
Nite 9ee03aae2f
Merge branch 'develop' into fix/search2-result-artist 2022-03-15 11:08:24 +01:00
Nite ebfc06c423
Fixed tests 2022-03-15 10:52:22 +01:00
Nite 0587f4d837
Merge pull request #705 from hmueller01/strings-update
updated english language
2022-03-15 10:39:41 +01:00
Nite 6bfd06c6a0
Updated Search2 Artist result to use Index instead of Artist
(As needed for listing the Artist's albums afterwards)
2022-03-15 10:31:38 +01:00
Óscar García Amor 6442bae882
Merge branch 'develop' into strings-update 2022-03-15 09:15:40 +01:00
Nite c81c685800
Merge pull request #699 from omgcem/fix-context-menu-track
Updated references to context_menu_track to correctly identify events
2022-03-15 09:11:27 +01:00
Holger Müller 5941e5ab87
fixed output of debug file size (in MB) 2022-03-12 15:40:21 +01:00
Holger Müller cf52d76698
updated english language 2022-03-12 15:35:45 +01:00
Cem Eren 273ac8f9b8
Reused existing functions for track actions
Updated existing functions with additional parameter so they can be used
by context menu but added default value function so that existing calls
on buttons can remain without parameters being passed in.
2022-03-11 19:35:18 +01:00
Nite 465c211017
Merge branch 'develop' into fix-context-menu-track 2022-03-09 16:57:31 +01:00
Nite 4fbedc3d2b
Merge pull request #695 from ultrasonic/improve-template
Improve issue template adding server info
2022-03-09 16:56:55 +01:00
Cem Eren 0961f56a7d
Corrected style violation 2022-03-06 00:19:25 +01:00
Cem Eren eb0fa67431
Updated references to context_menu_track to correctly identify events
Additionally updated event handler to correctly play, queue, pin and
download songs now that events are triggering correctly.
2022-03-05 00:10:20 +01:00
Óscar García Amor 3d65c0a90c
Improve issue template adding server info 2022-02-25 08:44:05 +01:00
Nite 7dd479c0d2
Merge pull request #691 from blastrock/fix-play-all
TrackCollectionFragment: fix play all button
2022-02-20 13:46:32 +01:00
Nite b5bfd87fcc
Merge branch 'develop' into fix-play-all 2022-02-19 21:30:36 +01:00
Philippe Daouadi d03b633eeb
TrackCollectionFragment: fix play all button
The button would not work because the entries are actually of the Album
type instead of Entry.

Signed-off-by: Philippe Daouadi <philippe@ud2.org>
2022-02-19 18:30:59 +01:00
Nite 4ee4b70e09
Merge pull request #692 from nitehu/hide-keyboard
Fixed Search and Edit Server to hide the keyboard when navigating away
2022-02-19 14:32:47 +01:00
Nite 8675f25668
Fixed Search and Edit Server to hide the keyboard when navigating away 2022-02-19 13:54:25 +01:00
Nite 00dc87d5df
Merge pull request #690 from nitehu/fix-about
Made About text scrollable, fixed title
2022-02-19 11:45:43 +01:00
Nite b7d1e4acf6
Made About text scrollable, fixed title 2022-02-19 11:26:13 +01:00
Óscar García Amor baa0c92c7a
Bump version to 3.1.1 2022-02-17 08:34:15 +01:00
Nite 7c9d51f758
Merge pull request #689 from nitehu/update-gradle
Updated Gradle to fix R8 bug with Kotlin 1.6
2022-02-16 19:20:11 +01:00
Nite 9bf7e99abd
Fixed Lint 2022-02-16 19:04:27 +01:00
Nite b5e606455e
Updated Gradle to fix R8 bug with Kotlin 1.6 2022-02-16 18:33:07 +01:00
Óscar García Amor e2ddb35ce3
Bump version to 3.1.0 2022-02-15 09:50:25 +01:00
Óscar García Amor 9581a17bd9
Update translations 2022-02-15 09:17:08 +01:00
Óscar García Amor 3fbf47dedf
Mark URL strings from about screen as not translatable 2022-02-15 08:42:39 +01:00
Óscar García Amor 87aea4847c
Merge pull request #684 from nitehu/redesign-about
Redesigned the About fragment - removed WebView
2022-02-14 09:11:30 +01:00
Nite f210a6e363
Redesigned the About fragment - removed WebView 2022-02-13 19:42:34 +01:00
Nite a9ee09bc2f
Merge pull request #665 from ultrasonic/dependabot/gradle/org.jetbrains.kotlinx-kotlinx-coroutines-android-1.6.0-native-mt
Bump kotlinx-coroutines-android from 1.5.2-native-mt to 1.6.0-native-mt
2022-02-08 20:19:17 +01:00
Nite bf96f36cb4
Merge branch 'develop' into dependabot/gradle/org.jetbrains.kotlinx-kotlinx-coroutines-android-1.6.0-native-mt 2022-02-08 19:57:01 +01:00
Nite da69fb9f1f
Merge pull request #648 from ultrasonic/dependabot/gradle/versions.kotlin-1.6.10
Bump versions.kotlin from 1.5.31 to 1.6.10
2022-02-08 19:30:18 +01:00
Nite 5962cc2add
Merge branch 'develop' into dependabot/gradle/versions.kotlin-1.6.10
Added temporary fix for Koin bug
2022-02-08 18:18:58 +01:00
Nite 0d4b400105
Merge pull request #630 from ultrasonic/ready/LRUCache
Convert LRUCache to Kotlin
2022-02-05 13:29:10 +01:00
Nite ca9ba68a5e
Merge branch 'develop' into ready/LRUCache 2022-02-05 11:54:38 +01:00
Nite d00a30940e
Merge pull request #678 from nitehu/revert-to-server-color
Add back server coloring feature
2022-02-05 11:24:37 +01:00
Nite 586bc51a9c
Revert "Merge pull request #667 from ultrasonic/patchBranch"
This reverts commit 41a462708d, reversing
changes made to fd239e8e72.
2022-02-05 11:06:20 +01:00
Óscar García Amor a53d5378bf
Bump version to 3.0.1 2022-02-04 09:30:53 +01:00
Óscar García Amor 4778d18fb9
Update translations 2022-02-04 09:00:35 +01:00
Óscar García Amor 2ff944d77d
Merge branch 'develop' into ready/LRUCache 2022-02-04 08:47:42 +01:00
Nite 462719ccd1
Merge pull request #671 from ultrasonic/dependabot/gradle/versions.mockito-4.3.1
Bump versions.mockito from 4.1.0 to 4.3.1
2022-02-01 17:50:12 +01:00
Nite e39f232732
Merge branch 'develop' into dependabot/gradle/org.jetbrains.kotlinx-kotlinx-coroutines-android-1.6.0-native-mt 2022-02-01 17:21:54 +01:00
dependabot[bot] ca89d4b55e
Bump versions.mockito from 4.1.0 to 4.3.1
Bumps `versions.mockito` from 4.1.0 to 4.3.1.

Updates `mockito-core` from 4.1.0 to 4.3.1
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.1.0...v4.3.1)

Updates `mockito-inline` from 4.1.0 to 4.3.1
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.1.0...v4.3.1)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.mockito:mockito-inline
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-01 11:01:45 +00:00
Óscar García Amor 3558479278
Improve generation of signed apk in CI 2022-01-19 09:39:26 +01:00
dependabot[bot] c66ea1c437
Bump kotlinx-coroutines-android from 1.5.2-native-mt to 1.6.0-native-mt
Bumps [kotlinx-coroutines-android](https://github.com/Kotlin/kotlinx.coroutines) from 1.5.2-native-mt to 1.6.0-native-mt.
- [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases)
- [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md)
- [Commits](https://github.com/Kotlin/kotlinx.coroutines/commits)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-android
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-18 10:37:26 +00:00
Óscar García Amor 23d0783164
Bump version to 3.0.0 2022-01-18 11:36:39 +01:00
Óscar García Amor 22c9db59c3
Ignore Unused Quantity 2022-01-18 11:23:19 +01:00
Óscar García Amor 121bcde18a
Update translations 2022-01-18 10:55:58 +01:00
Óscar García Amor 41a462708d
Merge pull request #667 from ultrasonic/patchBranch
Deactivate custom color and set DB to version 3
2022-01-18 10:53:17 +01:00
Nite 5e24c3ab40
Fixed text visibility 2022-01-14 19:36:05 +01:00
tzugen f68b46678a
Deactivate custom color and set DB to version 3 2022-01-13 19:22:44 +01:00
tzugen fd239e8e72
Merge pull request #660 from ultrasonic/develop_migrations
Fix database migrations
2022-01-12 07:17:40 +01:00
tzugen 10892f7527
Merge branch 'develop' into develop_migrations 2022-01-11 09:12:36 +01:00
Óscar García Amor e2a6a32b76
Merge pull request #659 from ultrasonic/ic_empty
More tiny and pretty ic_empty icon
2021-12-27 12:36:21 +01:00
Óscar García Amor 0dd666415d
Merge branch 'develop' into ic_empty 2021-12-27 12:18:13 +01:00
Óscar García Amor 8f43f69a66
Don't push translations from master 2021-12-27 11:16:14 +01:00
Óscar García Amor 193fe5ac3a
Add missing database migrations 2021-12-24 20:00:10 +01:00
Óscar García Amor 3588bb9380
Merge pull request #657 from ultrasonic/mainScroll
Scrolling / Database
2021-12-24 18:18:51 +01:00
Óscar García Amor 95b78a3c11
More tiny and pretty ic_empty icon 2021-12-24 17:18:57 +01:00
tzugen 9f5bc85cf2
Fallback to destructive migration on downgrade 2021-12-23 10:26:04 +01:00
tzugen 5a02467ee8
Use binding 2021-12-22 13:36:47 +01:00
tzugen 4a996f8edc
Fix Scroll in MainFragment 2021-12-22 13:26:58 +01:00
tzugen 8d5675f461
Merge pull request #656 from ultrasonic/randomReload
Don't clear random list when appending
2021-12-22 12:42:55 +01:00
tzugen a5e8daa912
Don't clear random list when appending 2021-12-21 23:44:18 +01:00
tzugen c2b23c4001
Merge pull request #655 from ultrasonic/fixIndex1
Create a correctly typed list from getMusicDirectory()
2021-12-21 23:20:44 +01:00
tzugen 10b83805a9
Create a correctly typed list from getMusicDirectory() 2021-12-21 13:31:58 +01:00
Óscar García Amor fbf5a63396
Avoid lint errors if a language has an unused quantity 2021-12-21 10:42:54 +01:00
Óscar García Amor bb370bfc44
Fix lint errors 2021-12-21 10:27:11 +01:00
Óscar García Amor 2153650d8b
Update translations 2021-12-21 09:26:42 +01:00
tzugen 987fbbe02a
Bump cache version 2021-12-20 22:39:20 +01:00
tzugen b958dcabe4
Move versions file to gradle dir 2021-12-20 22:15:07 +01:00
tzugen d66de5955b
Merge pull request #652 from nitehu/fix/isUri
Updated custom cache location handling to remove isUri
2021-12-20 22:02:36 +01:00
tzugen 1eca5a756e
Use new toml file for version declaration 2021-12-20 21:59:57 +01:00
tzugen dd92d00be5
Update Gradle and Gradle Plugin 2021-12-20 20:44:24 +01:00
tzugen 7531a4d4aa
Suppress some warnings 2021-12-20 20:20:08 +01:00
tzugen 82f45bd5dd
Set compileSdk to 31 2021-12-20 19:49:44 +01:00
tzugen c0ef964a3e
Run KtlintFormat 2021-12-20 19:41:55 +01:00
Nite 34e232a43a
Fixed default for cache directory selector 2021-12-20 19:39:44 +01:00
tzugen 65347a20fa
Update room 2021-12-20 19:39:20 +01:00
tzugen 7f42ed6a37
Update ktlint as well 2021-12-20 19:37:04 +01:00
dependabot[bot] 95f37ba2e2
Bump versions.kotlin from 1.5.31 to 1.6.10
Bumps `versions.kotlin` from 1.5.31 to 1.6.10.

Updates `kotlin-gradle-plugin` from 1.5.31 to 1.6.10
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.6.10/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.31...v1.6.10)

Updates `kotlin-stdlib` from 1.5.31 to 1.6.10
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.6.10/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.31...v1.6.10)

Updates `kotlin-reflect` from 1.5.31 to 1.6.10
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.6.10/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.31...v1.6.10)

Updates `kotlin-test-junit` from 1.5.31 to 1.6.10
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.6.10/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.31...v1.6.10)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.jetbrains.kotlin:kotlin-reflect
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.jetbrains.kotlin:kotlin-test-junit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-20 17:51:46 +00:00
tzugen fa434342d9
Covert LRUCache to Kotlin 2021-12-20 18:45:04 +01:00
Nite 17e49ff49e
Updated custom cache location handling to remove isUri
Fixed DownloadFileCache contained DownloadFiles with old cache path
2021-12-20 13:15:45 +01:00
Nite adf72d6460
Merge pull request #649 from ultrasonic/ready/dialogs
Unify error dialog handling
2021-12-20 09:52:51 +01:00
Nite 505405e87f
Fixed comment 2021-12-20 09:33:50 +01:00
Nite a3ad17692b
Merge branch 'develop' into ready/dialogs 2021-12-20 09:22:39 +01:00
Nite 5c3e2f6e37
Merge pull request #642 from ultrasonic/ready/removeMergeAdapter
Remove MergeAdapter and SackOfViewsAdapter
2021-12-20 09:22:04 +01:00
Nite 941fbd907f
Merge branch 'develop' into ready/removeMergeAdapter 2021-12-20 08:56:31 +01:00
Nite 095cf4ef4a
Merge pull request #641 from ultrasonic/ready/removeFeatureStorage
Remove feature storage
2021-12-20 08:56:00 +01:00
tzugen 3be480a9a2
Add error message to cache location picker
Fixes #643
2021-12-18 18:19:15 +01:00
tzugen 5bfa0044ab
Introduce prepopulated Info and Error Dialog.Builders,
and thereby unify dialog handling across the code base
2021-12-18 18:13:43 +01:00
tzugen 6b1fc7575a
Use material icons for info and error dialogs 2021-12-18 16:43:51 +01:00
tzugen 6fcea86097
Remove feature storage 2021-12-18 15:02:53 +01:00
tzugen 28d5e5043f
Remove MergeAdapter and SackOfViewsAdapter 2021-12-18 14:55:18 +01:00
tzugen eb2aeabd5d
Merge pull request #615 from ultrasonic/api30
Update the File code to use Storage Access Framework in order to support API 30
2021-12-18 14:53:44 +01:00
tzugen a0da791b28
Minor fixes 2021-12-18 14:33:04 +01:00
tzugen f4554ff29e
Fix messed up indentation in StreamProxy 2021-12-16 19:02:33 +01:00
tzugen 9acc5121a4
Regenerate lint baseline 2021-12-16 18:55:35 +01:00
Nite 107146c8d9
Minor cleanup, added comments 2021-12-16 14:18:54 +01:00
Nite d51544f927
Added lazy initialization of DownloadFile status for better SAF performance
Minor fixes
2021-12-15 17:44:19 +01:00
Nite 66e7732ec2
Removed remaining DocumentFile uses
Fixed lint issues
2021-12-12 17:06:11 +01:00
Nite fa4214a0ac
Moved from DocumentFile to DocumentsContract
Added separate handling for the old java File paths
2021-12-12 13:00:53 +01:00
Nite 34c5ced32e
Refactored to use DocumentFile instead of FSAF 2021-12-10 21:28:46 +01:00
Nite 90638e5fd7
Merge branch 'develop' into api30 2021-12-10 11:00:59 +01:00
tzugen 6730713763
Merge pull request #640 from ultrasonic/dependabot/gradle/io.gitlab.arturbosch.detekt-detekt-gradle-plugin-1.19.0
Bump detekt-gradle-plugin from 1.18.1 to 1.19.0
2021-12-09 20:02:23 +01:00
dependabot[bot] 38fa4964f8
Bump detekt-gradle-plugin from 1.18.1 to 1.19.0
Bumps [detekt-gradle-plugin](https://github.com/detekt/detekt) from 1.18.1 to 1.19.0.
- [Release notes](https://github.com/detekt/detekt/releases)
- [Commits](https://github.com/detekt/detekt/compare/v1.18.1...v1.19.0)

---
updated-dependencies:
- dependency-name: io.gitlab.arturbosch.detekt:detekt-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-09 18:53:14 +00:00
tzugen b85230c056
Merge pull request #638 from ultrasonic/dependabot/gradle/versions.mockito-4.1.0
Bump versions.mockito from 4.0.0 to 4.1.0
2021-12-09 19:50:32 +01:00
dependabot[bot] fb85fb4e82
Bump versions.mockito from 4.0.0 to 4.1.0
Bumps `versions.mockito` from 4.0.0 to 4.1.0.

Updates `mockito-core` from 4.0.0 to 4.1.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.0.0...v4.1.0)

Updates `mockito-inline` from 4.0.0 to 4.1.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.0.0...v4.1.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.mockito:mockito-inline
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-09 18:07:14 +00:00
Nite 8c716da213
Merge pull request #635 from ultrasonic/draft/refactor-lists
Completely overhaul list display
2021-12-09 19:06:26 +01:00
tzugen 80e587c1aa
Add scrollbar to playlist view,
implement SectionedAdapter for Artists
2021-12-08 17:52:31 +01:00
tzugen e337177715
Style fixes 2021-12-07 00:06:41 +01:00
tzugen d8cdc81424
Increase touch area of drag handler.
Also use new tintable color for drag drawable
2021-12-07 00:04:53 +01:00
tzugen 351ad914e7
Fix constraints in track details 2021-12-06 23:49:09 +01:00
Nite 0d24c87eef
- Fixed track item layout when track number is missing
- Fixed Rx unsubscribing
- Fixed drag handle usage in playlist
2021-12-06 19:23:22 +01:00
tzugen de04f4cbe6
Fix the alignment of the status text,
add transparency when dragging a song,
remove wrong context menu
2021-12-05 21:29:32 +01:00
tzugen 026aa79572
Remove special casing of SongsForGenre and thereby fix it.
Also prevent jumping in the random albums list and
don't refresh the album list on back navigation
2021-12-05 21:07:08 +01:00
tzugen 6daa17efd5
Show folder header in Artist list 2021-12-05 14:05:42 +01:00
tzugen f2948cd3db
Various fixes & cleanups 2021-12-02 13:20:08 +01:00
tzugen 2ac1ea3f89
Shorten INTENT Constants names 2021-11-30 21:23:54 +01:00
tzugen f1e789ea9b
Fixed search, put compareTo method into Interface 2021-11-30 21:21:04 +01:00
tzugen bdac092eff
Make SearchResults expandable,
finish music folder support,
change Service interface of AlbumList to return listOf(Album)
2021-11-30 00:47:03 +01:00
tzugen aa33d7c882
Cleanup nested functions on OfflineMusicService and make it return the correct MusicDirectory type 2021-11-29 20:14:11 +01:00
tzugen 775f56c6fa
Fix header glitch 2021-11-29 19:00:28 +01:00
tzugen 2f0ff384d0
Various fixes
* Work on folder selector,
* Make current play queue drag&droppable
* Fix album view in offline mode
2021-11-29 15:09:51 +01:00
tzugen 82d90a6aee
Fix context menus.
Also cleanup files, rename layouts
2021-11-28 19:13:25 +01:00
tzugen b33fe2d451
Add nice looking empty list view
Also fix shouldRetry() in the Downloader
2021-11-28 19:13:25 +01:00
tzugen 4e37a2483c
Add an MusicDirectory.Album model to represent the APIAlbum model
It became necessary in order to have different types for Tracks vs Albums,
instead of just differentiating by isDirectory: Boolean.

Also:
* Fix Album display in SearchFragment.kt
* Use same ids in all lists
2021-11-28 19:13:24 +01:00
tzugen 5dfb66eec2
Implement singular selection for Bookmarks 2021-11-28 19:13:24 +01:00
tzugen ad793e27a5
Remove viewRefresh setting 2021-11-28 19:13:24 +01:00
tzugen 2086a6cac5
Unify play next and play last icons,
style fixes
2021-11-28 19:13:24 +01:00
tzugen eeb2d13d96
Delete a bunch of now-unused classes
Also run KtLint
2021-11-28 19:13:24 +01:00
tzugen f8a87f7c85
BookmarksFragment is now based on TrackCollectionFragment
Also start SearchFragment.kt
2021-11-28 19:13:24 +01:00
tzugen 7640f4c4aa
Start migration of Album and Artist 2021-11-25 18:43:50 +01:00
tzugen d243ae1b44
Use RecycleView inside PlayerFragment 2021-11-25 18:43:46 +01:00
tzugen 6277ee73c0
Simplify and fix download status display 2021-11-25 18:43:46 +01:00
tzugen 7a2dbf65d9
Fix jumping downloads in Download view 2021-11-25 18:43:31 +01:00
tzugen 19d014709f
Don't create DownloadFile instances unnecessarily 2021-11-25 16:53:25 +01:00
tzugen d0e39efc50
Migrate DownloadsFragment to new system 2021-11-25 16:53:25 +01:00
tzugen e81b1ef8c2
Add a HeaderView binder 2021-11-25 16:53:25 +01:00
tzugen 5f716f5008
Use MultiTypeAdapter as a backend for RecyclerView stuff 2021-11-25 16:53:22 +01:00
Nite 5cf914f555
Added file caches to speed up SAF
Updated settings page with default music cache path reset
Fixed a bunch of bugs
2021-11-23 20:22:31 +01:00
Nite 5c7cde2349
Merged develop into api30 2021-11-19 20:34:03 +01:00
Nite 3f570636dd
Merge changes 2021-11-19 19:09:27 +01:00
Nite 1d0bb944e1
Implemented Storage Access Framework as Music Cache 2021-11-19 18:43:52 +01:00
Nite 5fac1b74a3
Merge pull request #629 from ultrasonic/ready/rm-viewflipper
Remove MyViewFlipper.java
2021-11-18 21:13:12 +01:00
Nite d84a0a3929
Merge branch 'develop' into ready/rm-viewflipper 2021-11-18 20:30:20 +01:00
Nite f3ac843a9c
Merge pull request #628 from ultrasonic/ready/rm-silentbackground
Remove Silentbackground Task
2021-11-18 20:27:41 +01:00
tzugen cddbe72752
Remove network communication from main thread 2021-11-18 19:54:49 +01:00
tzugen 744282f10a
Prevent overlap in very long song titles 2021-11-18 19:07:35 +01:00
tzugen 51d6a23208
Provide Scope differently 2021-11-18 19:07:35 +01:00
tzugen a327a5b390
Update detekt-baseline 2021-11-18 19:07:35 +01:00
tzugen 36b581e3c1
Rename UncaughtExceptionHandler 2021-11-18 19:07:35 +01:00
tzugen 1ed9360bc7
CommunicationError to Object with static methods 2021-11-18 19:07:34 +01:00
tzugen ed152fa52a
Remove SilentBackgroundTask.kt 2021-11-18 19:07:34 +01:00
Nite 45e9728e0f
Merge pull request #626 from ultrasonic/cachecleaner
Migrate CacheCleaner to Coroutine
2021-11-18 18:17:30 +01:00
tzugen b1cb70764c
Remove MyViewFlipper.java
It was a workaround for a bug in Android 2, which is fixed in Android 3 and later.
2021-11-16 20:00:27 +01:00
tzugen 00cd4fce44
Migrate CacheCleaner to Coroutine 2021-11-16 18:01:04 +01:00
tzugen 97eb753413
Merge pull request #622 from nitehu/refactor-events
Refactor events
2021-11-16 15:23:13 +01:00
tzugen 00781ba7de
Add a comment 2021-11-16 14:56:39 +01:00
Nite d6f908b80c
Merge branch 'develop' into refactor-events 2021-11-15 18:20:56 +01:00
Nite e019ec788d
Fixed subscription 2021-11-15 18:20:26 +01:00
tzugen f73457298d
Move file methods to FileUtil.kt 2021-11-13 18:16:05 +01:00
tzugen c9e276dc76
API 30 2021-11-13 18:16:03 +01:00
Nite 28ef67a210
Merge pull request #625 from ultrasonic/style
Small style fixes
2021-11-10 18:27:36 +01:00
tzugen bb36116d70
Add .ifNotNull extension function as a more readable alternative to ?.let 2021-11-09 22:20:41 +01:00
tzugen 8830d76497
Make public playlist immutable (only Downloader can touch it)
Remove external usage of playlist_revision
2021-11-09 20:46:44 +01:00
tzugen 61f23fa948
Update lint 2021-11-09 18:45:00 +01:00
tzugen 92adcf47bd
Tweak drop shadow 2021-11-09 18:09:50 +01:00
tzugen 7e3cb19bac
Visually align song details.
* Also cleanup unused layout and color files
2021-11-09 18:09:50 +01:00
tzugen dc312d4592
Update dependencies.gradle 2021-11-09 18:09:50 +01:00
dependabot[bot] 435376b48b
Bump lifecycle-viewmodel-ktx from 2.2.0 to 2.4.0
Bumps lifecycle-viewmodel-ktx from 2.2.0 to 2.4.0.

---
updated-dependencies:
- dependency-name: androidx.lifecycle:lifecycle-viewmodel-ktx
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-09 18:09:50 +01:00
Nite 69825b28bb
Merged player state and track changes into a single event 2021-11-08 20:38:05 +01:00
Nite 66df5b1daf
Merge branch 'develop' into refactor-events 2021-11-05 19:26:45 +01:00
Nite 38979bf9d4
Merge pull request #617 from ultrasonic/dependabot/gradle/androidx.lifecycle-lifecycle-viewmodel-ktx-2.4.0
Bump lifecycle-viewmodel-ktx from 2.2.0 to 2.3.0
2021-11-05 19:23:55 +01:00
Nite 68549992f4
Merge branch 'develop' into dependabot/gradle/androidx.lifecycle-lifecycle-viewmodel-ktx-2.4.0 2021-11-05 19:09:35 +01:00
Nite 2e482e02a2
Merge pull request #618 from ultrasonic/dependabot/gradle/androidx.core-core-ktx-1.7.0
Bump core-ktx from 1.5.0 to 1.6.0
2021-11-05 19:09:11 +01:00
Nite bf18c43d73
Merge branch 'develop' into dependabot/gradle/androidx.core-core-ktx-1.7.0 2021-11-05 18:43:24 +01:00
Nite 7fe4845305
Merge pull request #619 from ultrasonic/dependabot/gradle/org.mockito.kotlin-mockito-kotlin-4.0.0
Bump mockito-kotlin from 3.2.0 to 4.0.0
2021-11-05 18:31:36 +01:00
dependabot[bot] c29b8ebe0e
Bump mockito-kotlin from 3.2.0 to 4.0.0
Bumps [mockito-kotlin](https://github.com/mockito/mockito-kotlin) from 3.2.0 to 4.0.0.
- [Release notes](https://github.com/mockito/mockito-kotlin/releases)
- [Commits](https://github.com/mockito/mockito-kotlin/compare/3.2.0...4.0.0)

---
updated-dependencies:
- dependency-name: org.mockito.kotlin:mockito-kotlin
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-05 17:16:07 +00:00
Nite 3bacabe480
Merge pull request #623 from ultrasonic/network
Use modern network APIs
2021-11-05 18:15:16 +01:00
tzugen aac73cd6d7
Further cleanup maxBitrate function 2021-11-03 12:59:06 +01:00
tzugen a66d07ae84
Use modern network APIs 2021-11-03 12:23:53 +01:00
Nite 210ae35ee0
Merge pull request #620 from ultrasonic/cc
CacheCleaner to Kotlin
2021-11-02 22:29:57 +01:00
Nite 4e3102f131
Fixed condition for directory listing
Minor cleanup
2021-11-02 22:19:09 +01:00
Nite eba42b82dc
Merge branch 'develop' into cc 2021-11-02 21:33:53 +01:00
Nite a34fc809d9
Merge pull request #616 from ultrasonic/rm-globalscope
Remove global scope use
2021-11-02 21:32:21 +01:00
Nite ffb2d59886
Updated Events to ReactiveX
Minor fixes
2021-11-02 17:45:01 +01:00
tzugen a6e76e9d53
Ktlint 2021-11-02 09:07:38 +01:00
tzugen c4e2c786d1
Fix flags 2021-11-02 09:05:51 +01:00
tzugen 050161bbb0
API 30 2021-11-01 17:10:46 +01:00
tzugen f085a8ab65
Transform CacheCleaner to Kotlin 2021-11-01 14:26:32 +01:00
tzugen dfb3561965
Remove custom Pair implementation 2021-11-01 14:20:57 +01:00
tzugen 8c99c84a90
Default arguments 2021-11-01 14:14:12 +01:00
tzugen 4fb4ab18da
Unused argument 2021-11-01 14:13:25 +01:00
tzugen e6624ada9a
Unnecesary null-check 2021-11-01 14:12:35 +01:00
tzugen a58e541ccc
Update dependencies.gradle 2021-11-01 13:05:26 +01:00
tzugen 416bc57eea
1.6.0 2021-11-01 12:28:04 +01:00
dependabot[bot] 34c3936513
Bump core-ktx from 1.5.0 to 1.7.0
Bumps core-ktx from 1.5.0 to 1.7.0.

---
updated-dependencies:
- dependency-name: androidx.core:core-ktx
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-01 11:03:00 +00:00
dependabot[bot] aece29559e
Bump lifecycle-viewmodel-ktx from 2.2.0 to 2.4.0
Bumps lifecycle-viewmodel-ktx from 2.2.0 to 2.4.0.

---
updated-dependencies:
- dependency-name: androidx.lifecycle:lifecycle-viewmodel-ktx
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-01 11:02:54 +00:00
tzugen e32b3461c9
Remove global scope use 2021-10-31 19:18:20 +01:00
Nite fec2d78d30
Merge branch 'develop' into refactor-events 2021-10-31 17:20:03 +01:00
Nite 7ed91db250
Merge pull request #612 from ultrasonic/prefs
Cleanup a bunch of preference setters and getter by using the new utility class
2021-10-31 17:10:38 +01:00
Nite 7c43d01f8e
Fixed nullable parameters 2021-10-31 16:54:37 +01:00
Nite 5eaf9cccb1
Started refactoring events to ReactiveX 2021-10-31 15:22:15 +01:00
tzugen bd23f54783
Remove Build switches for Lollipop 2021-10-30 16:07:47 +02:00
tzugen 5fe1921ca5
Cleanup issues 2021-10-30 15:59:51 +02:00
tzugen e5f7ca6310
Convert SettingsFragment to Kotlin 2021-10-30 15:40:43 +02:00
tzugen 4d42c0d9d2
Use lambdas in SettingsFragment, remove Consumer class. 2021-10-30 15:28:53 +02:00
tzugen e19d43d6b3
Better wording 2021-10-30 15:16:17 +02:00
tzugen 2d9a212b5c
Cleanup a bunch of preference setters and getters by using the new utility class 2021-10-30 15:14:15 +02:00
Nite f0c02f5551
Merge pull request #611 from ultrasonic/downloader-ld
Rename DownloadQueueSerializer to PlaybackstateSerializer
2021-10-28 17:16:24 +02:00
tzugen 7c956566a0
Merge branch 'develop' into downloader-ld 2021-10-28 11:44:22 +02:00
Nite ada780ab25
Merge pull request #599 from nitehu/navigation-servers
Moved server selector and settings to the Navigation menu
2021-10-28 10:38:35 +02:00
Nite 70e42fb443
Fixed default foreground color 2021-10-28 10:00:23 +02:00
tzugen c83a9778fc
Tweak layout 2021-10-28 09:36:46 +02:00
Nite 6636d6a558
Minor fixes 2021-10-28 07:38:43 +02:00
Nite 7ccb9d055c
Fixed lint 2021-10-28 07:30:31 +02:00
Nite 9782e18b6e
Updated looks of navigation header 2021-10-28 07:28:31 +02:00
tzugen eb2e6ada0a
Tweaked selected color, added circle to offline icon 2021-10-25 20:16:19 +02:00
Nite cef1153f89
Merge branch 'develop' into navigation-servers 2021-10-24 22:34:56 +02:00
tzugen fea515a526
Replace consumer use with a lambda 2021-10-23 16:54:00 +02:00
tzugen c44257f569
Fix a minor warning 2021-10-23 16:49:56 +02:00
tzugen 939cd8583c
Semantically rename DownloadQueueSerializer to PlaybackStateSerializer 2021-10-23 16:39:58 +02:00
tzugen 7d2923230c
Merge pull request #604 from ultrasonic/downloader-ld
Implement a Download view
2021-10-23 16:31:42 +02:00
tzugen ece53f7687
Add a comment 2021-10-23 16:13:05 +02:00
Nite 2847a51674
Fixed autoplay
Fixed uncaught exception
Fixed playlist loading from file
2021-10-23 16:09:49 +02:00
tzugen 24092ce465
Remove unneccessary call to checkDownloads() which were interferring with AutoPlay somehow. 2021-10-23 15:58:33 +02:00
tzugen 6f676d20b0
Update baseline to remove closed issues 2021-10-23 15:58:33 +02:00
tzugen 1d5b335f97
Add Download Fragment which show active downloads 2021-10-23 15:58:32 +02:00
tzugen 9bc19ec044
Semantic rename 2021-10-23 15:58:32 +02:00
tzugen 0bcf51a409
Implement LiveData in Downloader.kt
Minor: Also remove a suppress comment
2021-10-23 15:58:32 +02:00
Nite f58c361e4e
Fixed navigation on welcome screen
Fixed navigation after selecting new server
Fixed color for new server
Fixed typo
2021-10-21 17:55:19 +02:00
Nite 949e7e58ba
Merge branch 'develop' into navigation-servers 2021-10-20 19:48:41 +02:00
Nite 77a2dcf935
Merge pull request #595 from ultrasonic/dependabot/gradle/versions.mockito-4.0.0
Bump versions.mockito from 3.12.4 to 4.0.0
2021-10-20 19:45:51 +02:00
dependabot[bot] 974099630a
Bump versions.mockito from 3.12.4 to 4.0.0
Bumps `versions.mockito` from 3.12.4 to 4.0.0.

Updates `mockito-core` from 3.12.4 to 4.0.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.12.4...v4.0.0)

Updates `mockito-inline` from 3.12.4 to 4.0.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.12.4...v4.0.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:production
  update-type: version-update:semver-major
- dependency-name: org.mockito:mockito-inline
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-20 17:09:12 +00:00
Nite 2197959507
Merge pull request #607 from ultrasonic/pref_delegate
Use delegate for Settings, reduces Boilerplate code
2021-10-20 19:07:39 +02:00
tzugen 62bade916f
Use delegate for Settings, reduces Boilerplate code 2021-10-18 17:32:12 +02:00
Nite ccf39661ab
Cleaned up warnings in FilePickerAdapter 2021-10-15 16:35:27 +02:00
Nite 7c66bc7ec8
Implemented navigation header coloring based on selected server 2021-10-15 16:26:06 +02:00
Nite 477f6f5d7c
Merge remote-tracking branch 'remotes/upstream/develop' into navigation-servers 2021-10-15 13:52:50 +02:00
Nite 784c65f96d
Merge pull request #600 from ultrasonic/permission_failed
Migrate Permission utitlity to Kotlin, increase min SDK to 17
2021-10-14 21:35:51 +02:00
Nite 16b2a99631
Fixed lint errors 2021-10-14 20:26:21 +02:00
Nite 427034053c
Merge branch 'develop' into permission_failed 2021-10-14 19:45:44 +02:00
Nite 6905c68898
Merge pull request #603 from ultrasonic/gradle72
Update Gradle and ktlint
2021-10-14 18:05:01 +02:00
Nite 2b40577d4b
Merge branch 'develop' into gradle72 2021-10-14 17:43:13 +02:00
Nite 23cca33d5a
Started implementing server colors 2021-10-14 17:17:32 +02:00
Óscar García Amor 62986ca79d
Fix lint errors 2021-10-14 10:02:22 +02:00
Óscar García Amor e2cff64e4e
Bump version to 2.24.0 2021-10-14 09:35:57 +02:00
Óscar García Amor 704f78366a
Merge branch 'master' into develop 2021-10-14 09:19:39 +02:00
Óscar García Amor e426081d1d
Update translations 2021-10-14 09:04:08 +02:00
tzugen bc43cc6874
Update Gradle config to remove deprecated attrs.
Deactivate xml reporting (not used currently)
2021-10-13 19:06:42 +02:00
tzugen 83f4ecb15a
Prepare a number of layouts for RTL and thereby fix a lot of lint messages 2021-10-13 18:51:11 +02:00
tzugen fbdf6d846b
Cleanup dead code paths 2021-10-13 18:51:11 +02:00
tzugen d1e636f553
Fix some new lint issues 2021-10-13 18:51:11 +02:00
tzugen d7cd68c39e
Makes more sense to update to 17 instead of 16. 2021-10-13 18:51:11 +02:00
tzugen b892b7b8d3
Migrate PermissionUtil to Kotlin
- Set min SDK to 16 (was 14), because READ_EXTERNAL_STORAGE is minimum 16
- Add new shortcut to Settings.kt class
- Use showDialog util function
2021-10-13 18:51:04 +02:00
tzugen 5c9b149bec
Update Gradle and ktlint 2021-10-13 18:48:56 +02:00
tzugen ec4f57b5b6
Merge pull request #602 from ultrasonic/fix_npe
Fix NPE
2021-10-13 18:45:41 +02:00
tzugen 87c2e44ab8
Fix NPE 2021-10-13 15:10:06 +02:00
tzugen 910a05d49d
Merge pull request #596 from nitehu/feature/share-on-server
Added option whether to create a share on the server when sharing songs
2021-10-13 15:05:16 +02:00
Nite b44e0517ce
Updated Settings for sharing 2021-10-12 21:09:19 +02:00
Nite 5a3eb6482a
Added option whether to create a share on the server when sharing songs 2021-10-12 21:09:19 +02:00
Nite 7a6f993602
Merge pull request #594 from nitehu/playback_complete
Added PlaybackComplete intent when a song playback is completed
2021-10-12 17:10:58 +02:00
Nite 2f98bf9ae0
Merge branch 'develop' into playback_complete 2021-10-12 16:47:59 +02:00
tzugen 855f111cb2
Merge pull request #593 from nitehu/fix/album_list
Fixed album lists
2021-10-12 15:36:52 +02:00
tzugen aea2e6baef
Style fix 2021-10-12 15:16:40 +02:00
Nite eff1a714e2
Fixed album lists in landscape mode
Fixed starring on album lists
Fixed reading starred state on the Subsonic API
2021-10-12 15:16:40 +02:00
Nite f752307a38
Changed navigation header colors 2021-10-11 19:43:01 +02:00
Nite 86f2aa1656
Fixed lint 2021-10-11 17:43:17 +02:00
Nite 256f785d39
Updated navigation header style 2021-10-11 17:31:32 +02:00
Nite 62150b77d3
Removed strings 2021-10-10 15:03:57 +02:00
Nite 652f85b070
Removed unused strings 2021-10-10 14:41:16 +02:00
Nite 381e29b2d1
Fixed lint issues 2021-10-10 14:15:03 +02:00
Nite 4b4853374c
Moved server selector to the navigation menu 2021-10-10 14:03:53 +02:00
Óscar García Amor 7091967fa5
Merge pull request #592 from ultrasonic/dependabot-q
Run dependabot monthly
2021-10-08 08:39:47 +02:00
Óscar García Amor c3df5ead37
Merge branch 'develop' into dependabot-q 2021-10-08 08:27:35 +02:00
Nite a3128c5f7f
Fixed lint 2021-10-07 18:02:23 +02:00
Nite 221f218856
Added PlaybackComplete intent when a song playback is completed 2021-10-07 17:51:19 +02:00
Nite ed59136fa6
Merge pull request #588 from ultrasonic/dependabot/gradle/androidx.constraintlayout-constraintlayout-2.1.1
Bump constraintlayout from 2.1.0 to 2.1.1
2021-10-04 18:55:43 +02:00
dependabot[bot] 0da22ef75b
Bump constraintlayout from 2.1.0 to 2.1.1
Bumps [constraintlayout](https://github.com/androidx/constraintlayout) from 2.1.0 to 2.1.1.
- [Release notes](https://github.com/androidx/constraintlayout/releases)
- [Commits](https://github.com/androidx/constraintlayout/commits)

---
updated-dependencies:
- dependency-name: androidx.constraintlayout:constraintlayout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-04 16:45:36 +00:00
tzugen 6b24e0ae4b
Run dependabot monthly
Now that our dependencies are up-to-date,
run dependabot only once per month, and also ignore patch releases,
in order to avoid frequent PRs that all require a review by us.
2021-10-04 18:45:33 +02:00
Nite 90e269525e
Merge pull request #587 from ultrasonic/dependabot/gradle/org.junit.vintage-junit-vintage-engine-5.8.1
Bump junit-vintage-engine from 5.8.0 to 5.8.1
2021-10-04 18:45:02 +02:00
dependabot[bot] 1d26d9ba3a
Bump junit-vintage-engine from 5.8.0 to 5.8.1
Bumps [junit-vintage-engine](https://github.com/junit-team/junit5) from 5.8.0 to 5.8.1.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.8.0...r5.8.1)

---
updated-dependencies:
- dependency-name: org.junit.vintage:junit-vintage-engine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-04 15:45:21 +00:00
Nite d89ae50ef4
Merge pull request #586 from ultrasonic/dependabot/gradle/versions.kotlin-1.5.31
Bump versions.kotlin from 1.5.30 to 1.5.31
2021-10-04 17:44:47 +02:00
dependabot[bot] e77a8e67a8
Bump versions.kotlin from 1.5.30 to 1.5.31
Bumps `versions.kotlin` from 1.5.30 to 1.5.31.

Updates `kotlin-gradle-plugin` from 1.5.30 to 1.5.31
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.5.31/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.30...v1.5.31)

Updates `kotlin-stdlib` from 1.5.30 to 1.5.31
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.5.31/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.30...v1.5.31)

Updates `kotlin-reflect` from 1.5.30 to 1.5.31
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.5.31/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.30...v1.5.31)

Updates `kotlin-test-junit` from 1.5.30 to 1.5.31
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.5.31/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.30...v1.5.31)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-reflect
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-test-junit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-04 15:34:50 +00:00
tzugen a7a895af96
Merge pull request #583 from ultrasonic/download-metadata
Download metadata
2021-10-04 15:06:21 +02:00
tzugen 35a0dd761d
Fix a bug where newly added Artists were not cached correctly 2021-10-04 11:20:52 +02:00
Nite fda746905d
Merged changes from develop 2021-09-24 21:12:02 +02:00
Nite 2237b476dc
Merge pull request #585 from nitehu/fix/avrcp_compatibility
Added setting to disable Now Playing List sending for incompatible bluetooth devices
2021-09-24 20:29:43 +02:00
Nite b640805559
Moved settings related properties from Util to their own class 2021-09-24 18:20:53 +02:00
Nite d4ce10ebfa
fixed lint error 2021-09-23 16:00:20 +02:00
Nite be49145aa8
Created different notification when Ultrasonic is only downloading files
Fixed null check
2021-09-23 15:51:53 +02:00
Nite 01aa1fe887
Moved kotlin files to their place 2021-09-21 15:20:56 +02:00
Nite 77f857b1c6
Added setting to disable Now Playing List sending for incompatible bluetooth devices 2021-09-17 16:45:33 +02:00
tzugen 02129a8fd0
Remove unused strings 2021-09-15 21:34:48 +02:00
tzugen ecc7e870f1
Save Artists in Offline database 2021-09-15 20:51:42 +02:00
tzugen 28097bf325
Optimize Imageloader to not create empty MusicDirectory.Entries 2021-09-15 20:46:05 +02:00
tzugen 611539be55
Handle WifiLock in Download manager class, instead of creating an individual lock per task.
Also only stop the executor when done.
2021-09-15 20:46:05 +02:00
tzugen 5ff4d21abc
Merge FileUtil functions into a single class. 2021-09-15 20:46:05 +02:00
tzugen ec49775d7e
Convert FileUtil to Kotlin 2021-09-15 20:46:05 +02:00
tzugen c48c41284e
Migrate Constants to Kotlin, update a copyright notice. 2021-09-15 20:46:05 +02:00
tzugen ba745a5f9a
Run download task inside service 2021-09-15 20:46:05 +02:00
Nite 8ab903c7d2
Merge pull request #582 from ultrasonic/dependabot/gradle/org.junit.vintage-junit-vintage-engine-5.8.0
Bump junit-vintage-engine from 5.7.1 to 5.8.0
2021-09-15 19:09:48 +02:00
dependabot[bot] ac3635a8f4
Bump junit-vintage-engine from 5.7.1 to 5.8.0
Bumps [junit-vintage-engine](https://github.com/junit-team/junit5) from 5.7.1 to 5.8.0.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.7.1...r5.8.0)

---
updated-dependencies:
- dependency-name: org.junit.vintage:junit-vintage-engine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-15 14:32:46 +00:00
Nite 546039fd5e
Merge pull request #574 from ultrasonic/dependabot/gradle/org.jetbrains.kotlinx-kotlinx-coroutines-android-1.5.2-native-mt
Bump kotlinx-coroutines-android from 1.5.1-native-mt to 1.5.2-native-mt
2021-09-15 16:31:15 +02:00
dependabot[bot] 86a772f115
Bump kotlinx-coroutines-android from 1.5.1-native-mt to 1.5.2-native-mt
Bumps [kotlinx-coroutines-android](https://github.com/Kotlin/kotlinx.coroutines) from 1.5.1-native-mt to 1.5.2-native-mt.
- [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases)
- [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md)
- [Commits](https://github.com/Kotlin/kotlinx.coroutines/commits)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-android
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-15 14:12:46 +00:00
Nite d3c7fcf7e7
Merge pull request #573 from ultrasonic/dependabot/gradle/io.gitlab.arturbosch.detekt-detekt-gradle-plugin-1.18.1
Bump detekt-gradle-plugin from 1.18.0 to 1.18.1
2021-09-15 16:03:40 +02:00
Nite 1f5de82004
Merge branch 'develop' into dependabot/gradle/io.gitlab.arturbosch.detekt-detekt-gradle-plugin-1.18.1 2021-09-15 15:35:59 +02:00
Óscar García Amor a64fe47353
Bump version to 2.23.1 2021-09-13 11:09:50 +02:00
Óscar García Amor 8928980bae
Update translations 2021-09-13 11:04:29 +02:00
tzugen 950691daab
Merge pull request #572 from nitehu/changeDragHandle
Moved drag handle to the left in the Now Playing list
2021-09-12 09:27:32 +02:00
Nite ee1e4548e3
Fixed lint errors 2021-09-12 09:18:49 +02:00
Nite 07f334a7fd
Moved drag handle to the left in the Now Playing list
Updated drag handle to the dotted version
2021-09-12 09:18:49 +02:00
Nite 5ef3ced111
Fixed media session null checks 2021-09-12 08:54:08 +02:00
Óscar García Amor 4ce5cfcb8e
Merge pull request #578 from nitehu/readme
Added link to the wiki for enabling Ultrasonic with Android Auto
2021-09-10 12:40:47 +02:00
Nite 25fd2f4772
Added link to the wiki for enabling Ultrasonic with Android Auto 2021-09-10 12:04:37 +02:00
dependabot[bot] 008520fbd0
Bump detekt-gradle-plugin from 1.18.0 to 1.18.1
Bumps [detekt-gradle-plugin](https://github.com/detekt/detekt) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/detekt/detekt/releases)
- [Commits](https://github.com/detekt/detekt/compare/v1.18.0...v1.18.1)

---
updated-dependencies:
- dependency-name: io.gitlab.arturbosch.detekt:detekt-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-06 11:05:46 +00:00
Nite 225bb78263
Merge pull request #571 from nitehu/fix/MediaSessionNull
Fixed media session null checks
2021-09-05 09:59:37 +02:00
Nite 84d28178bf
Fixed media session null checks 2021-09-05 09:34:38 +02:00
Nite f29db522d4
Merge pull request #567 from ultrasonic/semanticDownloadView
Use semantically correct API endpoint when streaming/downloading.
2021-09-03 20:26:58 +02:00
Nite 9f1315b6dc
Merge branch 'develop' into semanticDownloadView 2021-09-01 18:19:55 +02:00
Nite 9f02ee95a4
Merge pull request #568 from ultrasonic/downloader
Rework Downloader
2021-09-01 18:14:58 +02:00
tzugen d08a38ea1c
Small fixes 2021-08-28 11:43:33 +02:00
tzugen ad81f3bf6d
Use semantically correct API endpoint when streaming/downloading.
Fixes #257
2021-08-28 11:39:32 +02:00
tzugen 594e94eea7
Finish 2021-08-28 11:29:47 +02:00
tzugen f9aac1ca43
Add parallel downloading, better priority handling 2021-08-28 11:29:47 +02:00
tzugen b8eddb2d24
Rename .java to .kt 2021-08-28 11:29:47 +02:00
tzugen e0df24182e
Add nullability annotation 2021-08-28 11:29:47 +02:00
tzugen 029f0fa4da
Merge pull request #559 from Maxmystere/Improve-Offline-Support
Add better offline Support
2021-08-27 12:08:14 +02:00
tzugen 662cb1728b
Fix a number of minor warnings in Util.kt 2021-08-27 11:50:31 +02:00
tzugen e8baea6195
Convert MainFragment to Kotlin 2021-08-26 22:58:40 +02:00
tzugen e00137a635
Rename .java to .kt 2021-08-26 22:58:39 +02:00
tzugen d83cf0917c
Fix compiler warning 2021-08-26 22:58:39 +02:00
Maxence G (Hello-Fluffy) 0a2e7358fa
Fix CI 2021-08-26 22:58:39 +02:00
Maxence G (Hello-Fluffy) 7eb3d01ae8
Limit GetSongs if length is too short 2021-08-26 22:58:39 +02:00
Maxence G (Hello-Fluffy) a23e5ff794
Add better offline Support 2021-08-26 22:58:39 +02:00
tzugen 6aa3124ee9
Merge pull request #565 from ultrasonic/dependabot/gradle/versions.kotlin-1.5.30
Bump versions.kotlin from 1.5.21 to 1.5.30
2021-08-26 20:59:07 +02:00
dependabot[bot] 413707e482
Bump versions.kotlin from 1.5.21 to 1.5.30
Bumps `versions.kotlin` from 1.5.21 to 1.5.30.

Updates `kotlin-gradle-plugin` from 1.5.21 to 1.5.30
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.21...v1.5.30)

Updates `kotlin-stdlib` from 1.5.21 to 1.5.30
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.21...v1.5.30)

Updates `kotlin-reflect` from 1.5.21 to 1.5.30
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.21...v1.5.30)

Updates `kotlin-test-junit` from 1.5.21 to 1.5.30
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.21...v1.5.30)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-reflect
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-test-junit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-26 17:51:07 +00:00
tzugen e753d95ed3
Merge pull request #564 from ultrasonic/translations
Translations
2021-08-26 19:04:40 +02:00
tzugen 0579bd9754
Merge remote-tracking branch 'origin/master' into develop 2021-08-26 18:54:33 +02:00
Nite bb1451b800
Merge pull request #563 from ultrasonic/dependabot/gradle/versions.mockito-3.12.4
Bump versions.mockito from 3.11.2 to 3.12.4
2021-08-26 16:50:15 +02:00
dependabot[bot] 01be4c7148
Bump versions.mockito from 3.11.2 to 3.12.4
Bumps `versions.mockito` from 3.11.2 to 3.12.4.

Updates `mockito-core` from 3.11.2 to 3.12.4
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.11.2...v3.12.4)

Updates `mockito-inline` from 3.11.2 to 3.12.4
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.11.2...v3.12.4)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.mockito:mockito-inline
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-26 14:15:03 +00:00
Nite cb51c0b723
Merge pull request #551 from ultrasonic/dependabot/gradle/androidx.constraintlayout-constraintlayout-2.1.0
Bump constraintlayout from 2.0.4 to 2.1.0
2021-08-26 16:13:25 +02:00
tzugen 0a39891b93
Break up CI steps and only build release (and not debug) in last steps 2021-08-26 12:07:00 +02:00
tzugen 7e82efddad
Break up CI steps and only build release (and not debug) in last steps 2021-08-26 11:58:20 +02:00
Óscar García Amor 6202484df7
Bump version to 2.23.0 2021-08-26 11:04:21 +02:00
Óscar García Amor 4a10b76067
Update translations 2021-08-26 10:29:20 +02:00
dependabot[bot] 9ea0246032
Bump constraintlayout from 2.0.4 to 2.1.0
Bumps [constraintlayout](https://github.com/androidx/constraintlayout) from 2.0.4 to 2.1.0.
- [Release notes](https://github.com/androidx/constraintlayout/releases)
- [Commits](https://github.com/androidx/constraintlayout/compare/2.0.4...2.1.0)

---
updated-dependencies:
- dependency-name: androidx.constraintlayout:constraintlayout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-25 14:29:08 +00:00
Nite bd34345c12
Merge pull request #558 from ultrasonic/video-cleanup
Video call can be static
2021-08-25 16:28:22 +02:00
Nite 2655a4a606
Merge branch 'develop' into video-cleanup 2021-08-25 14:27:05 +02:00
tzugen 059f6d8f32
Merge pull request #537 from SaintDubious/AndroidAuto2
Added Android Auto support
2021-08-20 21:07:45 +02:00
tzugen 27971cb426
Merge remote-tracking branch 'origin/develop' into AndroidAuto2 2021-08-20 20:57:00 +02:00
tzugen a051d4d040
Clean up Util.kt after conversion
(currentSong was assigned but never actually used)
2021-08-20 20:53:17 +02:00
tzugen 8b15c9a57f
Merge pull request #556 from ultrasonic/dependabot/gradle/io.gitlab.arturbosch.detekt-detekt-gradle-plugin-1.18.0
Bump detekt-gradle-plugin from 1.17.1 to 1.18.0
2021-08-18 11:23:21 +02:00
tzugen 54f39be7ca
Video call can be static 2021-08-18 09:09:08 +02:00
tzugen 3afb86c22c
Video call can be static 2021-08-18 09:07:57 +02:00
dependabot[bot] d01030b61d
Bump detekt-gradle-plugin from 1.17.1 to 1.18.0
Bumps [detekt-gradle-plugin](https://github.com/detekt/detekt) from 1.17.1 to 1.18.0.
- [Release notes](https://github.com/detekt/detekt/releases)
- [Commits](https://github.com/detekt/detekt/compare/v1.17.1...v1.18.0)

---
updated-dependencies:
- dependency-name: io.gitlab.arturbosch.detekt:detekt-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-16 11:04:34 +00:00
Nite fc2e4b7ae4
Merge branch 'develop' into AndroidAuto2 2021-08-02 10:41:38 +02:00
Nite 6e6cf3a86a
Merge pull request #546 from ultrasonic/dependabot/gradle/versions.kotlin-1.5.21
Bump versions.kotlin from 1.5.20 to 1.5.21
2021-08-02 10:01:34 +02:00
dependabot[bot] 43a806a699
Bump versions.kotlin from 1.5.20 to 1.5.21
Bumps `versions.kotlin` from 1.5.20 to 1.5.21.

Updates `kotlin-gradle-plugin` from 1.5.20 to 1.5.21
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.20...v1.5.21)

Updates `kotlin-stdlib` from 1.5.20 to 1.5.21
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.20...v1.5.21)

Updates `kotlin-reflect` from 1.5.20 to 1.5.21
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.20...v1.5.21)

Updates `kotlin-test-junit` from 1.5.20 to 1.5.21
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.20...v1.5.21)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-reflect
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-test-junit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-02 07:03:36 +00:00
Nite c64420c11f
Merge pull request #545 from ultrasonic/dependabot/gradle/org.jetbrains.kotlinx-kotlinx-coroutines-android-1.5.1-native-mt
Bump kotlinx-coroutines-android from 1.5.0-native-mt to 1.5.1-native-mt
2021-08-02 09:02:45 +02:00
dependabot[bot] adca273d3f
Bump kotlinx-coroutines-android from 1.5.0-native-mt to 1.5.1-native-mt
Bumps [kotlinx-coroutines-android](https://github.com/Kotlin/kotlinx.coroutines) from 1.5.0-native-mt to 1.5.1-native-mt.
- [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases)
- [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md)
- [Commits](https://github.com/Kotlin/kotlinx.coroutines/commits)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-android
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-02 06:32:43 +00:00
Nite 2703a4f35b
Merge pull request #543 from ultrasonic/dependabot/gradle/versions.kluent-1.68
Bump versions.kluent from 1.64 to 1.68
2021-08-02 08:31:49 +02:00
Nite 9546bdeab5
Fixed playing single track in empty playlist 2021-07-19 15:31:52 +02:00
James Wells 961c726da8
restore previous code 2021-07-18 16:14:43 -04:00
Nite c9f2050c46
Suppress detekt LongMethod 2021-07-18 13:23:20 +02:00
Nite 982639d2c7
Fixed lint errors
Added comments
2021-07-18 13:17:29 +02:00
Nite cf05d3c781
Implemented Media Browsing 2021-07-18 11:33:39 +02:00
Nite f50d6f13f4
Started implementing Media Browser
Added root menus, playlists and artists
2021-07-16 17:29:21 +02:00
Nite 635ea2f55e
Merge branch 'develop' into AndroidAuto2 2021-07-16 07:39:33 +02:00
Nite 51dbdfb39a
Implemented track detail display in the playlist
Fixed playback position disappearing when paused
2021-07-14 16:09:52 +02:00
Nite 56af9e4bf2
Moved MediaSession handling to its own class
Fixed queue and position handling on Now Playing screen
2021-07-13 19:25:37 +02:00
Nite 83c6b76d0a
Updated Android Auto to use MediaPlayerService separately
Added some missing features found in the docs
2021-07-12 16:13:34 +02:00
dependabot[bot] ba323fa9ac
Bump versions.kluent from 1.64 to 1.68
Bumps `versions.kluent` from 1.64 to 1.68.

Updates `kluent` from 1.64 to 1.68
- [Release notes](https://github.com/MarkusAmshove/Kluent/releases)
- [Changelog](https://github.com/MarkusAmshove/Kluent/blob/master/CHANGELOG.md)
- [Commits](https://github.com/MarkusAmshove/Kluent/commits)

Updates `kluent-android` from 1.64 to 1.68
- [Release notes](https://github.com/MarkusAmshove/Kluent/releases)
- [Changelog](https://github.com/MarkusAmshove/Kluent/blob/master/CHANGELOG.md)
- [Commits](https://github.com/MarkusAmshove/Kluent/commits)

---
updated-dependencies:
- dependency-name: org.amshove.kluent:kluent
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.amshove.kluent:kluent-android
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-12 11:05:39 +00:00
Nite ea3b055288
Merge pull request #541 from ultrasonic/dependabot/gradle/com.karumi-dexter-6.2.3
Bump dexter from 6.2.2 to 6.2.3
2021-07-09 14:05:43 +02:00
dependabot[bot] 6558f871ca
Bump dexter from 6.2.2 to 6.2.3
Bumps [dexter](https://github.com/karumi/Dexter) from 6.2.2 to 6.2.3.
- [Release notes](https://github.com/karumi/Dexter/releases)
- [Commits](https://github.com/karumi/Dexter/compare/6.2.2...6.2.3)

---
updated-dependencies:
- dependency-name: com.karumi:dexter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-09 11:22:42 +00:00
Nite ee76c661a1
Merge pull request #540 from ultrasonic/dependabot/gradle/com.google.android.material-material-1.4.0
Bump material from 1.3.0 to 1.4.0
2021-07-09 13:21:48 +02:00
dependabot[bot] 808eafc85d
Bump material from 1.3.0 to 1.4.0
Bumps [material](https://github.com/material-components/material-components-android) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/material-components/material-components-android/releases)
- [Commits](https://github.com/material-components/material-components-android/compare/1.3.0...1.4.0)

---
updated-dependencies:
- dependency-name: com.google.android.material:material
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-09 09:56:48 +00:00
Nite 649a702ebb
Merge pull request #539 from ultrasonic/dependabot/gradle/org.robolectric-robolectric-4.6.1
Bump robolectric from 4.5.1 to 4.6.1
2021-07-09 11:56:04 +02:00
dependabot[bot] 090d7f8ab7
Bump robolectric from 4.5.1 to 4.6.1
Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.5.1 to 4.6.1.
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.5.1...robolectric-4.6.1)

---
updated-dependencies:
- dependency-name: org.robolectric:robolectric
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-09 09:23:02 +00:00
Nite 1938b3bb4b
Merge pull request #538 from ultrasonic/dependabot/gradle/com.android.tools.build-gradle-4.2.2
Bump gradle from 4.2.0 to 4.2.2
2021-07-09 11:22:25 +02:00
dependabot[bot] e60ac32c3f
Bump gradle from 4.2.0 to 4.2.2
Bumps gradle from 4.2.0 to 4.2.2.

---
updated-dependencies:
- dependency-name: com.android.tools.build:gradle
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-09 07:18:46 +00:00
Nite 6791f0631a
Merge pull request #534 from ultrasonic/dependabot/gradle/versions.kotlin-1.5.20
Bump versions.kotlin from 1.5.10 to 1.5.20
2021-07-09 09:17:52 +02:00
James Wells db0669098c
Another Attempt at Auto 2021-07-04 16:42:18 -04:00
James Wells 04de4544ee
merge develop 2021-07-04 15:41:56 -04:00
dependabot[bot] 62189dce5c
Bump versions.kotlin from 1.5.10 to 1.5.20
Bumps `versions.kotlin` from 1.5.10 to 1.5.20.

Updates `kotlin-gradle-plugin` from 1.5.10 to 1.5.20
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.5.20/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.10...v1.5.20)

Updates `kotlin-stdlib` from 1.5.10 to 1.5.20
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.5.20/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.10...v1.5.20)

Updates `kotlin-reflect` from 1.5.10 to 1.5.20
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.5.20/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.10...v1.5.20)

Updates `kotlin-test-junit` from 1.5.10 to 1.5.20
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.5.20/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.10...v1.5.20)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-reflect
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-test-junit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-29 16:55:34 +00:00
tzugen 69c9739db0
Merge pull request #445 from tzugen/database-4
Implement a room database for better offline support
2021-06-29 18:54:53 +02:00
tzugen b546f2c2fb
The Tests actually caught an error :)
If shortcuts were set, these were added as duplicates to the list.
2021-06-29 18:01:26 +02:00
tzugen fe9b2f9700
Update Baseline file 2021-06-29 18:01:26 +02:00
tzugen dbdb59bbff
Add a Room database for Artists, Indexes and MusicFolders.
* There is one database for each Server
* Index items are saved with a "musicFolderId" prop, which makes it possible to filter the database by musicFolder without necessarily having to query the server for it.
* Databases for new Servers are created on the fly
* If the user removes a server, the respective database is deleted.
2021-06-29 18:01:26 +02:00
tzugen fa94cd24da
Export schema 2021-06-29 18:01:26 +02:00
tzugen c99c4478f2
Remove file storage code from RESTAPI class 2021-06-29 18:01:26 +02:00
tzugen 67f2d1f9a8
Merge pull request #535 from ultrasonic/dependabot/gradle/versions.mockito-3.11.2
Bump versions.mockito from 3.11.0 to 3.11.2
2021-06-29 13:46:44 +02:00
dependabot[bot] f15891cbdd
Bump versions.mockito from 3.11.0 to 3.11.2
Bumps `versions.mockito` from 3.11.0 to 3.11.2.

Updates `mockito-core` from 3.11.0 to 3.11.2
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.11.0...v3.11.2)

Updates `mockito-inline` from 3.11.0 to 3.11.2
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.11.0...v3.11.2)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.mockito:mockito-inline
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-29 09:52:14 +00:00
Óscar García Amor bc8a78e772
Bump version to 2.22.0 2021-06-29 11:51:24 +02:00
Óscar García Amor 68acf3789c
Update translations 2021-06-29 11:28:26 +02:00
Óscar García Amor 2a6c9baa3d
Merge branch 'tzugen-leaks' into develop 2021-06-29 11:27:31 +02:00
tzugen e3e8d36f5c
Save the correct field to the server preferences 2021-06-25 17:47:11 +02:00
tzugen 2da4f79098
Increase memory cache size 2021-06-23 17:31:41 +02:00
James Wells 4ff167e497
merge develop 2021-06-22 20:08:49 -04:00
tzugen d916e937f9
Move a resource from a versioned folder
(v14 is the default now)
2021-06-22 20:42:05 +02:00
tzugen d4c0f62b1d
Don't keep a reference to context here,
it's a leak and not used anyway.
2021-06-21 19:11:30 +02:00
tzugen 6fca6d918c
Merge pull request #529 from tzugen/fix-save-playlist
Fix save playlist
2021-06-21 18:58:27 +02:00
tzugen 1132ec6c87
Merge branch 'develop' into fix-save-playlist 2021-06-21 18:49:25 +02:00
tzugen d6594b8ec4
Merge pull request #530 from tzugen/diff
Use DiffUtil for better performance when refreshing the data..
2021-06-21 18:49:17 +02:00
tzugen bc16b64be4
Avoid unnecessary reloads when navigating back,
this preserves the scroll position.
2021-06-21 13:41:39 +02:00
tzugen 16c6b6b03e
Fix a potential exception in shuffle()
If getCurrentPlayingIndex() returns -1,
then remove() fails
2021-06-20 23:38:13 +02:00
tzugen 36dccc845b
Use ListAdapter class to calculate Diff off the main thread 2021-06-20 08:31:12 +02:00
tzugen 1ecb577c50
Use DiffUtil for better performance when refreshing the data.. 2021-06-20 00:29:35 +02:00
tzugen 8c2896ea16
Remove static field leaks on SeekBar,
cleanup code and update baseline
2021-06-19 23:52:24 +02:00
tzugen d8b032e2e3
Cleanup code 2021-06-19 23:52:24 +02:00
tzugen 30bbeb7594
Rename .java to .kt 2021-06-19 23:52:24 +02:00
tzugen 28336c8bf7
Convert PlayerFragment to Kotlin 2021-06-19 23:52:24 +02:00
tzugen 5cfb49ac69
Rename .java to .kt 2021-06-19 23:52:24 +02:00
tzugen ffe67e6c50
Merge pull request #526 from Maxmystere/fix-notification-star
Fix no server call when starring song from notification
2021-06-19 23:48:29 +02:00
tzugen 38f1f5f5c6
Nicer catch all 2021-06-19 23:25:02 +02:00
tzugen 956d555b35
Update PlayerFragment.java 2021-06-19 23:23:39 +02:00
James Wells 793c4a6ca7
working version 2021-06-19 00:05:19 -04:00
James Wells 3853fce818
Merge branch 'develop' into AndroidAuto 2021-06-18 22:52:56 -04:00
Maxence G d440b8c76d
Fix no server call when starring song from notification 2021-06-16 20:33:21 +02:00
Nite 2d4c773344
Merge pull request #525 from tzugen/fix-video-url
Properly generate the Video stream url, without actually making a request
2021-06-16 10:05:50 +02:00
tzugen 57d740af12
Properly generate the Video stream url, without actually making a request! 2021-06-14 20:53:55 +02:00
tzugen 6a370696cd
Merge pull request #520 from tzugen/remove-flash
Remove flash
2021-06-14 19:20:50 +02:00
tzugen bc295a825b
Merge branch 'develop' into remove-flash 2021-06-14 19:10:56 +02:00
tzugen 914f84a4c8
Merge pull request #519 from tzugen/demo
Better experience for new users
2021-06-14 19:10:40 +02:00
Nite a02b620531
Fixed typos 2021-06-14 15:42:24 +02:00
tzugen 3b5b8feb56
Update baseline files 2021-06-12 20:00:44 +02:00
tzugen 58200660d3
Also delete unused strings related to licensing stuff 2021-06-12 19:56:22 +02:00
tzugen f7dfdcadad
Remove unused strings 2021-06-12 19:48:36 +02:00
tzugen a43a525bab
Remove two unrelated, but unused files. 2021-06-12 15:31:24 +02:00
tzugen db31fefe83
Remove flash support 2021-06-12 15:31:21 +02:00
tzugen 172cbd0476
Add an option to load the demo database for new users 2021-06-12 15:04:17 +02:00
tzugen a2324eabf2
Use ID3 tags by default for new users.
This is what most people are used to from iTunes or Spotify..
2021-06-12 15:01:03 +02:00
tzugen 6ea4ac5829
Merge pull request #517 from tzugen/cleanupStreamHandling
Cleaner separation of API result handling.
2021-06-11 10:42:03 +02:00
tzugen 5e4c6cc627
I made currentApiVersion nullable here two commits ago while testing,
but it doesn't need to be.
Also update some comments
2021-06-11 10:24:21 +02:00
James Wells 8855d19113
Merge branch 'develop' into AndroidAuto 2021-06-10 19:19:29 -04:00
tzugen 24ae0d9e81
Catch all exceptions in GenericListModel
This is how it used to be, before i changed it because
detekt was complaining.
2021-06-09 20:53:01 +02:00
tzugen 619f441790
Fix tests 2021-06-09 20:23:22 +02:00
tzugen 6ab0ff973a
Fix version checking for freshly added servers 2021-06-09 19:33:17 +02:00
tzugen 620239f859
Improve the extension function by making it call-chainable 2021-06-09 17:36:11 +02:00
tzugen a60a843edf
Fix tests 2021-06-09 17:00:25 +02:00
tzugen d9a7fa2413
Cleaner separation of API result handling.
* CallResponseChecker functionality are now Kotlin Extensions
* Removed unfitting shortcuts from SubsonicAPIClient
* Increase timeout to get a video url
* Fix a bug, that the Rest API version was checked twice on each call
2021-06-09 14:15:39 +02:00
tzugen dd1f55a927
Merge pull request #515 from ultrasonic/dependabot/gradle/versions.mockito-3.11.0
Bump versions.mockito from 3.8.0 to 3.11.0
2021-06-08 22:13:24 +02:00
tzugen 175f7cd6f6
Fix imports (ownership and thus package names have changed for Mockito-Kotlin) 2021-06-08 22:00:04 +02:00
tzugen 27f3d7c00c
Also update Mockito-Kotlin 2021-06-08 21:23:36 +02:00
dependabot[bot] 0c839a4cab
Bump versions.mockito from 3.8.0 to 3.11.0
Bumps `versions.mockito` from 3.8.0 to 3.11.0.

Updates `mockito-core` from 3.8.0 to 3.11.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.8.0...v3.11.0)

Updates `mockito-inline` from 3.8.0 to 3.11.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.8.0...v3.11.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.mockito:mockito-inline
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-08 19:16:35 +00:00
tzugen 580a170f4b
Merge pull request #514 from tzugen/coverart
Fix bugs in new image loader and make it default.
2021-06-08 21:15:46 +02:00
tzugen 4eeff191aa
Add two ToDos 2021-06-08 17:20:45 +02:00
tzugen be356d9c0a
Cleanup unused functions from RESTMusicService,
put the caching functionality into the ImageLoader
2021-06-08 17:12:55 +02:00
tzugen 3c554caf2e
On disk we are only caching the full-size images.
So when modify the key to query for the full size image,
because scaling down a larger size image on the device is quicker than
requesting the down-sized image from the network.
2021-06-08 14:49:49 +02:00
Nite 78cb4d09cf
Separated cache entries for large images 2021-06-07 21:38:39 +02:00
Nite fa6566e903
Fixed showing the unknown image when a source isn't specified 2021-06-07 20:32:44 +02:00
Nite 7182694c5d
Fixed currentDownloading to be nullable
(It can be null when nothing is downloading)
2021-06-07 20:06:40 +02:00
tzugen 6dff5c5a23
Add mandatory stableKey to tests 2021-06-07 14:37:01 +02:00
tzugen 566e429e4c
Use file cache in Picasso 2021-06-07 14:22:33 +02:00
tzugen 9161f9dc99
Move ImageLoader module into main module. 2021-06-07 13:26:08 +02:00
tzugen f8efb6d592
Update a test to reflect new arguments 2021-06-07 13:26:07 +02:00
tzugen 92fe7359ff
Finish removing unneeded preferences and fix import problem 2021-06-07 13:26:07 +02:00
tzugen 41eb217d8f
Finish removing unneeded preferences 2021-06-07 13:26:07 +02:00
tzugen 0989ee20a6
Remove LegacyImageLoader, use Picasso as default 2021-06-07 13:26:07 +02:00
tzugen 2eaa9a2091
Fix two bugs in the new image loader:
1. Id was checked for nullability, but it is actually an empty string in most cases where there is no Cover art.
This lead to queries without id set.
2. Size was not respected by the new image loader.
2021-06-07 13:26:07 +02:00
tzugen 90ecbe4b78
Make getCoverArt require a non-null entry 2021-06-07 13:26:07 +02:00
James Wells e95b2ce09c
refactored a bit, and some cleanup 2021-06-06 18:28:46 -04:00
James Wells be4ffc2c7e
Merge branch 'develop' into AndroidAuto 2021-06-06 16:36:33 -04:00
Óscar García Amor 1dc480b141
Bump version to 2.21.0 2021-06-03 10:33:05 +02:00
Óscar García Amor e0f9059de8
Update translations 2021-06-03 08:58:13 +02:00
Nite 0a886d7095
Merge pull request #505 from Maxmystere/check-server-features
Implement server feature checking
2021-06-01 15:37:56 +02:00
Maxence G 17b9b776fb
Add french translation 2021-05-31 17:02:27 +02:00
Nite 132ee3b563
Removed unused string 2021-05-31 14:26:48 +02:00
Nite fe5ee8b12a
Fixed ktlint 2021-05-31 14:04:46 +02:00
Nite 41023282e8
Fixed detekt
Fixed code duplication
Added more info to the Connection Test dialog for the user
2021-05-31 13:38:53 +02:00
Maxence G 67870efd67
Fix length 2021-05-29 15:46:09 +02:00
Maxence G 3e08735374
run ktlint 2021-05-29 15:42:15 +02:00
Maxence G 075b80c01f
Fix new active server getter 2021-05-29 15:30:01 +02:00
Maxence G d8e7b991cd
Merge remote-tracking branch 'base/develop' into check-server-features 2021-05-29 15:28:25 +02:00
Maxence G fd48367cab
Reduce function length 2021-05-29 15:00:46 +02:00
tzugen 7d15f67d2d
Merge pull request #504 from ultrasonic/dependabot/gradle/io.gitlab.arturbosch.detekt-detekt-gradle-plugin-1.17.1
Bump detekt-gradle-plugin from 1.17.0 to 1.17.1
2021-05-29 10:51:47 +02:00
dependabot[bot] 2050e21c9c
Bump detekt-gradle-plugin from 1.17.0 to 1.17.1
Bumps [detekt-gradle-plugin](https://github.com/detekt/detekt) from 1.17.0 to 1.17.1.
- [Release notes](https://github.com/detekt/detekt/releases)
- [Commits](https://github.com/detekt/detekt/compare/v1.17.0...v1.17.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-29 08:35:39 +00:00
tzugen ddc0d71538
Merge pull request #507 from ultrasonic/dependabot/gradle/versions.kotlin-1.5.10
Bump versions.kotlin from 1.4.32 to 1.5.10
2021-05-29 10:34:58 +02:00
James Wells e666498f13
Initial Test of Android Auto 2021-05-28 20:40:00 -04:00
tzugen 8aa1cf1e2c
Fix Kotlin 1.5 deprecated calls 2021-05-28 20:32:31 +02:00
tzugen 71068fe635
Upgrade Koin to 3.0.2 2021-05-28 19:39:27 +02:00
tzugen f9401ba1fa
Apply JVM Target to all Kotlin tasks 2021-05-28 19:21:50 +02:00
tzugen d8500e45e4
Kotlin and Room need to be updated at the same time. 2021-05-28 19:21:50 +02:00
dependabot[bot] e1d3e61c74
Bump kotlinx-coroutines-android from 1.4.3-native-mt to 1.5.0-native-mt
Bumps [kotlinx-coroutines-android](https://github.com/Kotlin/kotlinx.coroutines) from 1.4.3-native-mt to 1.5.0-native-mt.
- [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases)
- [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md)
- [Commits](https://github.com/Kotlin/kotlinx.coroutines/commits)
2021-05-28 19:21:50 +02:00
dependabot[bot] b5801f488f
Bump versions.kotlin from 1.4.32 to 1.5.10
Bumps `versions.kotlin` from 1.4.32 to 1.5.10.

Updates `kotlin-gradle-plugin` from 1.4.32 to 1.5.10
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.4.32...v1.5.10)

Updates `kotlin-stdlib` from 1.4.32 to 1.5.10
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.4.32...v1.5.10)

Updates `kotlin-reflect` from 1.4.32 to 1.5.10
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.4.32...v1.5.10)

Updates `kotlin-test-junit` from 1.4.32 to 1.5.10
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.4.32...v1.5.10)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-28 19:21:49 +02:00
Nite bf013c02d6
Merge pull request #506 from tzugen/KotlinServices
Kotlin services
2021-05-28 17:31:36 +02:00
tzugen ee9c478bfe
Make parentId nullable in TrackCollectionModel 2021-05-28 12:35:29 +02:00
tzugen e21ae1299b
Fix more detekt and lint issues and remove them from the baseline. 2021-05-27 15:30:20 +02:00
tzugen 6dc0eb7ecc
Move files to Koltin dir 2021-05-27 11:33:10 +02:00
tzugen cc7e273d50
Fix remaining warnings 2021-05-27 11:18:29 +02:00
tzugen ac77d9557c
Clarify time handling for the time limited cache
(Less magic numbers)
2021-05-27 11:15:28 +02:00
tzugen 154662bec5
Make Artist and Entry comparable (thus sortable) 2021-05-27 11:14:23 +02:00
tzugen aa1c0d8baa
Convert TimeLimitedCache to Kotlin 2021-05-27 11:13:23 +02:00
tzugen 38c1480f77
Whitespace and detekt 2021-05-26 23:32:25 +02:00
tzugen 8567fc0105
Fix warning in FileLoggerTree.kt 2021-05-26 23:22:13 +02:00
tzugen e059d737bc
Adopt changes from stricter nullabilities 2021-05-26 23:21:56 +02:00
tzugen b30584f99c
Make Entry always have an id 2021-05-26 23:20:19 +02:00
tzugen 3257fb9153
Make Entry always have an id 2021-05-26 23:19:16 +02:00
tzugen 5ac36b749b
Convert all Services to Kotlin 2021-05-26 23:17:52 +02:00
tzugen 3f2daaa7ec
Rename .java to .kt 2021-05-26 23:17:52 +02:00
tzugen e55d894f45
Consistently throw Exeception in OfflineMusicService 2021-05-26 21:32:52 +02:00
tzugen f510638571
Fix KtLint issue 2021-05-26 08:49:23 +02:00
tzugen 31850bf270
Cleanup AlertDialog method
The other method was unused, so we can get rid of it.
Also use a lambda for the callback
2021-05-26 08:49:12 +02:00
Maxence G 3c4a5064b0
change exception type 2021-05-25 19:30:02 +02:00
Maxence G 070e0ac792
Fix lintFormat + remove synchronized 2021-05-25 18:51:00 +02:00
Maxence G 1b7f48c53a
Implement server feature checking 2021-05-24 23:25:12 +02:00
tzugen cebbb39caf
Merge pull request #502 from ultrasonic/dependabot/gradle/androidx.core-core-ktx-1.5.0
Bump core-ktx from 1.5.0-rc02 to 1.5.0
2021-05-24 10:37:25 +02:00
dependabot[bot] faee926c35
Bump core-ktx from 1.5.0-rc02 to 1.5.0
Bumps core-ktx from 1.5.0-rc02 to 1.5.0.

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-24 08:14:46 +00:00
Nite 04b4b154e4
Merge pull request #501 from tzugen/impl
Migrate MediaPlayerController to Kotlin
2021-05-24 09:23:53 +02:00
tzugen 2db7d2daf1
Hide cluttering koin warning, which was added in 2.2.0 and will be removed again in 2.3.0
See https://github.com/InsertKoinIO/koin/issues/939 for more details
2021-05-22 11:01:12 +02:00
tzugen 79fd1ac84f
Remove resolved issues from baselines 2021-05-21 23:34:57 +02:00
tzugen 0500bd5e88
Handle also UnknownHostException in GenericListModel 2021-05-21 23:34:57 +02:00
tzugen 005c1cb8f7
Fix an String resource int that wasn't converted to a String 2021-05-21 23:34:57 +02:00
tzugen 3d99269c41
Remove context from ShufflePlayBuffer 2021-05-21 23:34:57 +02:00
tzugen ed6c6c9057
Remove two warnings in Downloader 2021-05-21 23:34:57 +02:00
tzugen b8fbbd8c49
Remove Context from Downloader & DownloadFile 2021-05-21 23:34:52 +02:00
tzugen 9b2e54b94e
Remove context from JukeboxMediaPlayer and Scrobbler 2021-05-21 22:52:16 +02:00
tzugen 00e64cdddc
Use lambdas in OfflineMusicService 2021-05-21 22:39:21 +02:00
tzugen 1b5db9da1f
Make ExternalStorageMonitor context-free 2021-05-21 22:37:43 +02:00
tzugen df047dd463
Refactor MediaPlayerController to Kotlin
and also make MediaPlayerService context-free
2021-05-21 22:35:22 +02:00
tzugen a801e276ee
Rename .java to .kt 2021-05-21 22:35:21 +02:00
tzugen 05cb82921d
Fix remaining warnings in MediaPlayerLifecycleSupport 2021-05-21 19:43:37 +02:00
tzugen f596dc77c1
Make MediaPlayerLifecycleSupport context-free 2021-05-21 19:43:32 +02:00
tzugen 7922d6caad
Prevent leaks by making cleanup tasks static 2021-05-21 19:11:26 +02:00
tzugen b25a118148
Remove unused context from CacheCleaner 2021-05-21 18:50:57 +02:00
tzugen 402fe961ba
Merge MediaPlayerController interface & implementation 2021-05-21 18:50:33 +02:00
Nite 6943dc5895
Merge pull request #500 from tzugen/detekt-3
Fix a lot of warnings, update baseline files, improve CI run times
2021-05-21 09:33:23 +02:00
tzugen f1713fdbd3
Use a recently maintained version of DSLV, which compiles correctly with newer Gradles. 2021-05-21 09:16:06 +02:00
tzugen b0b03c0fe0
Be more selective with the configuration of gradle.properties in CI 2021-05-20 18:16:55 +02:00
tzugen ab4c2c1bbe
Add staggered caching, to increase the times we have a warm cache 2021-05-20 18:16:55 +02:00
tzugen 29a98c1263
The AGP build cache was removed in AGP 4.1. Previously introduced in AGP 2.3 to complement the Gradle build cache, the AGP build cache was superseded entirely by the Gradle build cache in AGP 4.1. This change does not impact build time. 2021-05-20 18:16:55 +02:00
tzugen 78e4a00476
Our target sdk is 29 2021-05-20 18:16:55 +02:00
tzugen 387119a90c
Make the CI fail if any new lint issues (warnings or errors) are added. 2021-05-20 18:16:55 +02:00
tzugen d26f8595ab
Manually disable IconMissingDensityFolder 2021-05-20 18:16:55 +02:00
tzugen dff054847a
Regenerate lint baseline
Removes 238 fixed issues
Adds 177 previously unlisted issues
2021-05-20 18:16:55 +02:00
tzugen 10edce9f56
Disable daemon 2021-05-20 18:16:55 +02:00
tzugen 3a05f30907
Fix more warnings 2021-05-20 18:16:55 +02:00
tzugen b559f6420e
Move certain ignores from the baseline to the specified files. This makes refactoring much less fragile. 2021-05-20 18:16:55 +02:00
tzugen 689b704bc5
Enable parallel AST generation 2021-05-20 18:16:55 +02:00
tzugen a25a2ff337
Go back to default detekt checks.
Type resolution (which I had enabled some weeks ago) is really not stable enough for production and also takes much longer time to compile.
2021-05-20 18:16:55 +02:00
tzugen 8b01118233
Remove local copy of com.mobeta.android.dslv in favour of importing it from maven 2021-05-20 18:16:42 +02:00
Nite 950cb6254f
Merge pull request #493 from tzugen/generic-views
Introduce new Generic Fragments, ViewModels, and Adapters for the display of API data.
2021-05-18 11:44:51 +02:00
tzugen 5f08e7e7cd
Create a new SelectMusicFolderViewHolder instance when the Adapter is recreated,
instead of reusing an old one. This lead to a crash.

Also make the AlbumList use the correct placeholder,
remove an unused resource file, and update the placeholder to look less clumsy.
2021-05-18 08:50:04 +02:00
tzugen 2cf80707f7
Correctly display the folder header also in album view
Also fix a number of smaller issues
2021-05-16 21:06:04 +02:00
tzugen 72c03cc500
Introduce new Generic Fragments, ViewModels, and Adapters for the display of API data.
* Splits former SelectAlbumFragment into separate fragments for Albums and general collections of tracks
* Renames and refactors SelectArtist view to extend the new Generic classes
* Adds error handling (Fixes #484)
* Adds EndlessScrolling capabilities to all Album Lists
* Uses RecyclerViews and LiveData for performance and in-memory caching
* Refreshes the UI to be aligned with the ArtistList UI
* Add a new GenericEntry to the domain data classes, and make other types extend it
2021-05-16 17:42:31 +02:00
tzugen c6a744cc14
Rename classes in preparation of split. 2021-05-16 17:42:31 +02:00
tzugen 1fc7a954e9
Regenerate baseline files 2021-05-16 17:42:31 +02:00
tzugen 13b00f75f0
Use default values for LongMethod, LongParameterList, LargeClass and ComplexMethod.
The algrorithms in detekt had changed when I updated detekt to v1.16, but our values not,
resulting in a very long baseline file with many non-sensical warnings.
By removing our config we follow the detekt config when they modify their algorithms again.
2021-05-16 17:42:31 +02:00
Nite 1599c96085
Merge pull request #476 from tzugen/j-center
Try to remove jcenter()
2021-05-16 17:08:48 +02:00
tzugen fb1d305545
Update detekt to 1.17.0 2021-05-15 14:50:04 +02:00
tzugen 8fbefe7066
Koin has renamed their id 2021-05-15 14:50:04 +02:00
tzugen b136235ccf
Try to remove jcenter() 2021-05-15 14:50:04 +02:00
tzugen 74ce93f37b
Merge pull request #490 from ultrasonic/dependabot/gradle/junit-junit-4.13.2
Bump junit from 4.12 to 4.13.2
2021-05-12 15:15:46 +02:00
dependabot[bot] 8fd66b2a1c
Bump junit from 4.12 to 4.13.2
Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.2.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-12 11:36:22 +00:00
tzugen 642c90974c
Merge pull request #487 from ultrasonic/dependabot/gradle/com.android.support.test-runner-1.0.2
Bump runner from 1.0.1 to 1.0.2
2021-05-12 13:35:35 +02:00
dependabot[bot] a5ad39caba
Removing unused testing dependencies 2021-05-12 12:58:48 +02:00
tzugen 403fead776
Merge pull request #492 from ultrasonic/dependabot/gradle/androidx.core-core-ktx-1.5.0-rc02
Bump core-ktx from 1.5.0-rc01 to 1.5.0-rc02
2021-05-12 11:35:16 +02:00
dependabot[bot] 7a012d3bce
Bump core-ktx from 1.5.0-rc01 to 1.5.0-rc02
Bumps core-ktx from 1.5.0-rc01 to 1.5.0-rc02.

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-12 09:13:31 +00:00
Óscar García Amor 426249f370
Merge pull request #491 from tzugen/weekly
Run dependabot only weekly!
2021-05-12 11:13:06 +02:00
Nite 1df6181cc3
Merge branch 'develop' into weekly 2021-05-12 10:38:32 +02:00
tzugen f897a1b459
Merge pull request #486 from ultrasonic/dependabot/gradle/commons-codec-commons-codec-1.15
Bump commons-codec from 1.10 to 1.15
2021-05-12 10:19:33 +02:00
tzugen b78dadc170
Run dependabot only weekly! 2021-05-12 10:07:43 +02:00
dependabot[bot] 330dade288
Bump commons-codec from 1.10 to 1.15
Bumps [commons-codec](https://github.com/apache/commons-codec) from 1.10 to 1.15.
- [Release notes](https://github.com/apache/commons-codec/releases)
- [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt)
- [Commits](https://github.com/apache/commons-codec/compare/1.10...rel/commons-codec-1.15)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-12 06:26:57 +00:00
Nite 14941486a5
Merge pull request #485 from tzugen/context-free-2
Removing remaining context from MusicService signatures.
2021-05-11 15:32:18 +02:00
tzugen a60be2157b
Removing remaining context from MusicService signatures. 2021-05-11 14:26:29 +02:00
Nite aed5d9b629
Merge pull request #480 from ultrasonic/dependabot/gradle/com.google.android.material-material-1.3.0
Bump material from 1.2.1 to 1.3.0
2021-05-11 14:09:32 +02:00
Nite c7904fad84
Merge branch 'develop' into dependabot/gradle/com.google.android.material-material-1.3.0 2021-05-11 13:41:34 +02:00
tzugen de44d28257
Merge pull request #482 from ultrasonic/dependabot/gradle/androidx.media-media-1.3.1
Bump media from 1.3.0 to 1.3.1
2021-05-11 13:13:06 +02:00
dependabot[bot] 4617a210fa
Bump media from 1.3.0 to 1.3.1
Bumps media from 1.3.0 to 1.3.1.

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-11 11:00:16 +00:00
dependabot[bot] cb5cdbe878
Bump material from 1.2.1 to 1.3.0
Bumps [material](https://github.com/material-components/material-components-android) from 1.2.1 to 1.3.0.
- [Release notes](https://github.com/material-components/material-components-android/releases)
- [Commits](https://github.com/material-components/material-components-android/compare/1.2.1...1.3.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-11 10:50:12 +00:00
Nite 09b5654f5a
Merge pull request #479 from ultrasonic/dependabot/gradle/androidx.constraintlayout-constraintlayout-2.0.4
Bump constraintlayout from 2.0.1 to 2.0.4
2021-05-11 12:48:42 +02:00
dependabot[bot] d399780d23
Bump constraintlayout from 2.0.1 to 2.0.4
Bumps constraintlayout from 2.0.1 to 2.0.4.

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-11 10:38:46 +00:00
Nite e9cc2880f2
Merge pull request #477 from tzugen/context-free
Make all preference operations context-free
2021-05-11 12:34:51 +02:00
tzugen c66118172c
Revert accidental changes in the method signature. 2021-05-11 12:04:29 +02:00
tzugen 5b6fa20050
This was literally the result of removing one(!) dependency on context inside FileUtil
17 Classes with x function  which don't need to pass around context anymore..
2021-05-11 12:04:29 +02:00
tzugen f2b1ba0e94
Remove unused context references in MusicServiceFactory 2021-05-11 12:04:29 +02:00
tzugen 1fa9e604b5
Remove a large number of baselined detekt issues :) 2021-05-11 12:04:29 +02:00
tzugen 76ba4c8ac9
Remove more context dependencies 2021-05-11 12:04:29 +02:00
tzugen 2c5f8d9dd0
Now we can stop passing around the context for a lot of API calls 2021-05-11 12:04:29 +02:00
tzugen 9ec92c6cf3
Make all preference operations context-free
(use the application context for them)
2021-05-11 12:04:26 +02:00
Nite 21282b4c17
Merge pull request #478 from tzugen/nav-crash
Nav crash
2021-05-10 16:30:02 +02:00
tzugen 6c122b9e80
Fix #415 2021-05-09 18:31:54 +02:00
tzugen e0eb2aaf61
Fix a number of warnings 2021-05-09 18:30:38 +02:00
tzugen e8fe006b1a
Merge pull request #473 from ultrasonic/dependabot/gradle/versions.navigation-2.3.5
Bump versions.navigation from 2.3.2 to 2.3.5
2021-05-09 09:28:59 +02:00
dependabot[bot] d46c556e6f
Bump versions.navigation from 2.3.2 to 2.3.5
Bumps `versions.navigation` from 2.3.2 to 2.3.5.

Updates `navigation-fragment` from 2.3.2 to 2.3.5

Updates `navigation-ui` from 2.3.2 to 2.3.5

Updates `navigation-fragment-ktx` from 2.3.2 to 2.3.5

Updates `navigation-ui-ktx` from 2.3.2 to 2.3.5

Updates `navigation-dynamic-features-fragment` from 2.3.2 to 2.3.5

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-08 16:11:09 +00:00
tzugen e06400550b
Merge pull request #475 from ultrasonic/dependabot/gradle/org.jetbrains.kotlinx-kotlinx-coroutines-android-1.4.3-native-mt
Bump kotlinx-coroutines-android from 1.3.9 to 1.4.3-native-mt
2021-05-08 18:09:57 +02:00
dependabot[bot] 19614e79c1
Bump kotlinx-coroutines-android from 1.3.9 to 1.4.3-native-mt
Bumps [kotlinx-coroutines-android](https://github.com/Kotlin/kotlinx.coroutines) from 1.3.9 to 1.4.3-native-mt.
- [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases)
- [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md)
- [Commits](https://github.com/Kotlin/kotlinx.coroutines/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-07 15:06:28 +00:00
Nite 8d2f4b5bf1
Merge pull request #467 from tzugen/lockscreen-controls
Lockscreen controls
2021-05-07 16:53:14 +02:00
Nite ec21e14e33
Merge branch 'develop' into lockscreen-controls 2021-05-07 16:15:20 +02:00
Nite 48ebe8939b
Merge pull request #471 from ultrasonic/dependabot/gradle/org.junit.vintage-junit-vintage-engine-5.7.1
Bump junit-vintage-engine from 5.3.1 to 5.7.1
2021-05-07 16:10:23 +02:00
Nite 0b2a62f391
Fixed some more warnings 2021-05-07 15:55:03 +02:00
Nite e37791e37c
Fixed deprecation warnings for kluent 2021-05-07 15:36:11 +02:00
dependabot[bot] 1deefb6a22
Bump junit-vintage-engine from 5.3.1 to 5.7.1
Bumps [junit-vintage-engine](https://github.com/junit-team/junit5) from 5.3.1 to 5.7.1.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.3.1...r5.7.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-07 09:16:04 +00:00
Nite f0e285cc67
Merge pull request #466 from tzugen/remove-version
Remove unused versions.kt & semver
2021-05-07 11:12:50 +02:00
Nite 23c430e923
Merge branch 'develop' into remove-version 2021-05-07 10:35:56 +02:00
Nite e1b2ec29cf
Merge pull request #459 from ultrasonic/dependabot/gradle/com.android.tools.build-gradle-4.2.0
Bump gradle from 6.5 to 7.0 and gradle plugin from 4.1.3 to 4.2.0
2021-05-07 10:35:17 +02:00
Nite dcd5cc8feb
Merge branch 'develop' into dependabot/gradle/com.android.tools.build-gradle-4.2.0 2021-05-07 09:37:29 +02:00
Nite 7dcc5bbf13
Merge pull request #455 from tzugen/patch-further
Followup #437
2021-05-07 09:36:30 +02:00
Nite afccbf3f65
Merge branch 'develop' into patch-further 2021-05-07 09:14:27 +02:00
Óscar García Amor 44c4b2eaf3
Merge pull request #469 from nitehu/develop
Add Ampache as supported server
2021-05-07 08:54:54 +02:00
Óscar García Amor 3dc82ed2ee
Merge branch 'develop' into develop 2021-05-07 08:39:00 +02:00
Óscar García Amor ed2de31fc7
Merge pull request #468 from tzugen/remove-codecov
Remove codecov
2021-05-07 08:33:38 +02:00
Nite 70dbfff1e1
Add Ampache as supported server 2021-05-07 07:56:36 +02:00
tzugen e4c13a305c
Remove codecov 2021-05-06 22:54:17 +02:00
tzugen 5187ac1f54
Merge pull request #462 from ultrasonic/dependabot/gradle/versions.retrofit-2.9.0
Bump versions.retrofit from 2.4.0 to 2.6.4
2021-05-06 20:46:42 +02:00
tzugen a6c914cf7f
Make the server setting model a local val 2021-05-06 20:45:45 +02:00
tzugen 9ca15deef2
Fix a copy & paste mistake
(Wrongly inserted if clause)
2021-05-06 20:45:45 +02:00
tzugen 094c0d2757
Cache requireArguments() in a local val 2021-05-06 20:45:45 +02:00
tzugen 8a2d100e96
Migrate ciTest to "Task Configuration Avoidance" 2021-05-06 20:33:38 +02:00
tzugen af304cd10f
Fix Jacoco under Gradle 7.0,
by migrating the task to use "Task Configuration Avoidance"
see https://docs.gradle.org/current/userguide/task_configuration_avoidance.html
2021-05-06 20:33:38 +02:00
tzugen 75bcbd97b1
Update Gradle to 7.0 2021-05-06 20:33:38 +02:00
dependabot[bot] e6b77847b6
Bump gradle from 4.1.3 to 4.2.0
Bumps gradle from 4.1.3 to 4.2.0.

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-06 20:33:38 +02:00
tzugen ddd8a54592
Merge branch 'develop' into dependabot/gradle/versions.retrofit-2.9.0 2021-05-06 20:25:27 +02:00
tzugen c8ecf4c123
Merge pull request #465 from ultrasonic/dependabot/gradle/versions.kluent-1.65
Bump versions.kluent from 1.35 to 1.64
2021-05-06 20:25:01 +02:00
tzugen bc2c591ff2
Set isActive correctly when pausing. 2021-05-06 13:23:38 +02:00
tzugen a910818b70
Add skip functions to lockscreen 2021-05-06 13:23:35 +02:00
tzugen 85e2e473e5
Fix a typo, make two functions private 2021-05-06 12:53:25 +02:00
tzugen 8ebd53a8eb
Remove no longer needed safe call. 2021-05-06 12:40:49 +02:00
dependabot[bot] 040dba6a77
Bump versions.kluent from 1.35 to 1.64 2021-05-06 12:36:27 +02:00
tzugen 7b7f470667
Merge branch 'develop' into dependabot/gradle/versions.retrofit-2.9.0 2021-05-06 12:25:40 +02:00
dependabot[bot] 2659634bda
Bump versions.retrofit from 2.4.0 to 2.6.4 2021-05-06 12:24:21 +02:00
tzugen bedbad658f
Merge branch 'develop' into remove-version 2021-05-06 12:12:50 +02:00
tzugen 4d27c35cf4
Remove unused versions.kt & semver 2021-05-06 12:09:23 +02:00
Nite 162bb18078
Merge pull request #451 from tzugen/remove-mediastore
Remove MediaStoreService, reinstate Util.scanMedia
2021-05-06 11:52:19 +02:00
Nite 3a2c41b879
Merge branch 'develop' into remove-mediastore 2021-05-06 09:49:46 +02:00
Nite 036a7a3a8e
Merge pull request #460 from ultrasonic/dependabot/gradle/org.robolectric-robolectric-4.5.1
Bump robolectric from 4.4 to 4.5.1
2021-05-06 09:49:24 +02:00
Nite 457a908d72
Merge branch 'develop' into dependabot/gradle/org.robolectric-robolectric-4.5.1 2021-05-06 09:23:40 +02:00
Nite 8701facd03
Merge pull request #458 from ultrasonic/dependabot/gradle/org.jacoco-org.jacoco.core-0.8.7
Bump org.jacoco.core from 0.8.5 to 0.8.7
2021-05-06 09:20:54 +02:00
Nite 115b8c3183
Merge branch 'develop' into dependabot/gradle/org.jacoco-org.jacoco.core-0.8.7 2021-05-06 09:02:07 +02:00
Nite b06384a15d
Merge pull request #457 from ultrasonic/dependabot/gradle/versions.mockito-3.8.0
Bump versions.mockito from 3.5.5 to 3.8.0
2021-05-06 09:01:34 +02:00
Nite ce7e95f59e
Merge branch 'develop' into dependabot/gradle/versions.mockito-3.8.0 2021-05-06 08:39:28 +02:00
Nite 341412a9e8
Merge pull request #456 from ultrasonic/dependabot/gradle/com.karumi-dexter-6.2.2
Bump dexter from 6.1.2 to 6.2.2
2021-05-06 08:38:56 +02:00
Nite e9b22b827a
Fixed updating CacheLocation setting after change 2021-05-06 08:23:08 +02:00
dependabot[bot] 17533bded1
Bump robolectric from 4.4 to 4.5.1
Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.4 to 4.5.1.
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.4...robolectric-4.5.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-05 08:57:27 +00:00
dependabot[bot] 2432ca7d1e
Bump org.jacoco.core from 0.8.5 to 0.8.7
Bumps [org.jacoco.core](https://github.com/jacoco/jacoco) from 0.8.5 to 0.8.7.
- [Release notes](https://github.com/jacoco/jacoco/releases)
- [Commits](https://github.com/jacoco/jacoco/compare/v0.8.5...v0.8.7)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-05 08:57:18 +00:00
dependabot[bot] 0ec94d456f
Bump versions.mockito from 3.5.5 to 3.8.0
Bumps `versions.mockito` from 3.5.5 to 3.8.0.

Updates `mockito-core` from 3.5.5 to 3.8.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.5.5...v3.8.0)

Updates `mockito-inline` from 3.5.5 to 3.8.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.5.5...v3.8.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-05 08:57:11 +00:00
dependabot[bot] e4af2b603f
Bump dexter from 6.1.2 to 6.2.2
Bumps [dexter](https://github.com/karumi/Dexter) from 6.1.2 to 6.2.2.
- [Release notes](https://github.com/karumi/Dexter/releases)
- [Commits](https://github.com/karumi/Dexter/compare/6.1.2...6.2.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-05 08:56:56 +00:00
Nite 88db9be878
Merge pull request #452 from ultrasonic/Dependabot-1
Create dependabot.yml
2021-05-05 10:44:59 +02:00
Nite 5c95d9c45a
Merge branch 'develop' into Dependabot-1 2021-05-05 09:42:21 +02:00
Nite 83b186b9b5
Merge branch 'develop' into remove-mediastore 2021-05-05 09:41:09 +02:00
Nite e75137372b
Merge pull request #447 from tzugen/rogue-session
Get rid of rogue session
2021-05-05 09:40:09 +02:00
tzugen a3b97fef47
Delegate all handling of the Intent receiver registering to our Service Class. 2021-05-04 16:32:17 +02:00
tzugen 80036034e4
Rename method for clarity 2021-05-04 16:16:05 +02:00
tzugen 06c076470a
Merge branch 'develop' into Dependabot-1 2021-05-04 09:59:51 +02:00
tzugen dc6cfd141b
Create dependabot.yml 2021-05-04 09:57:08 +02:00
Nite fb74d6465e
Merge branch 'develop' into rogue-session 2021-05-02 12:04:23 +02:00
Nite fe332eed91
Merge pull request #437 from tzugen/select-further
Refactor SelectAlbumView using ViewModel pattern
2021-05-02 11:50:25 +02:00
tzugen 66a306152d
Remove MediaStoreService, reinstate Util.scanMedia
This partially reverts commit 8fbc2a9fa3.
2021-05-01 13:31:48 +02:00
tzugen f6a8ee7241
Remove @Deprecated tag (keep it in JDoc because it doesn't generate a warning) 2021-05-01 10:50:54 +02:00
tzugen adc350b5c4
Fix wrong injection method after rebase 2021-05-01 10:50:54 +02:00
tzugen 98ba9ecd3e
Migrate from deprecated registerMediaButtonReceiver to MediaSession equivalent 2021-05-01 10:49:23 +02:00
tzugen 4af0d1db25
executeOnStartedMediaPlayerService takes a lambda now 2021-05-01 10:49:23 +02:00
tzugen d385cf271e
Add comments and deprecation notices 2021-04-27 20:45:25 +02:00
tzugen ce2f5a95d9
Remove unused parameter 2021-04-27 20:41:14 +02:00
tzugen 237f7ab181
Update baseline files. I have moved several methods, which now get triggered again.
To keep this PR manageable, I don't intent to split up the classes now.
2021-04-27 20:41:14 +02:00
tzugen fe69141e19
Don't leak context in model 2021-04-27 20:41:14 +02:00
tzugen 25f072a5d5
Cleanup some methods
+ Ktlint WS fixes
2021-04-27 20:40:57 +02:00
tzugen dca26f14eb
Fix more compiler warnings 2021-04-27 20:39:52 +02:00
tzugen 1802e91fa7
Use lambdas in Listeners, fix various warnings 2021-04-27 20:39:52 +02:00
tzugen fa4f4d6c9c
Move loading functions into ViewModel 2021-04-27 20:39:48 +02:00
tzugen 1f57fb334b
Refactor: LoadTask to Coroutines. 2021-04-27 19:52:13 +02:00
Óscar García Amor 1dd43d857d
Remove environment variables in generate_signed_apk CI step 2021-04-27 19:35:38 +02:00
Óscar García Amor 1c58062496
Ignore quantity errors in translations 2021-04-27 19:13:27 +02:00
Óscar García Amor f9dcc62ed7
Bump version to 2.20.0 2021-04-27 18:43:35 +02:00
tzugen 13f9dd9b33
Merge pull request #443 from ultrasonic/scrobbler
Better scrobble text and description, closes #442
2021-04-27 17:51:29 +02:00
Óscar García Amor 2d4b295f7f
Better scrobble text and description, closes #442 2021-04-27 17:33:15 +02:00
Nite 119cc4589e
Merge pull request #439 from tzugen/heapspace
Fix a typo in the circleci config.yml
2021-04-27 14:38:01 +02:00
tzugen 822b3551d9
Fix a typo in the circleci config.yml 2021-04-27 11:55:15 +02:00
Nite 817cc14ed9
Merge pull request #421 from tzugen/modern-notification-2
Modernize Service Notification
2021-04-27 10:58:50 +02:00
tzugen 4f70c61592
Fix Magic Number problem in DownloadFile (caused by #431). 2021-04-27 10:21:57 +02:00
tzugen 3e8cf63603
Merge branch 'develop' into modern-notification-2 2021-04-27 08:18:49 +02:00
Nite ae22a0c141
Merge pull request #431 from SaintDubious/retryCount
Add a retry count to stop infinite downloads
2021-04-27 07:14:02 +02:00
James Wells d084270c4b
Add a retry count to stop infinite downloads
Signed-off-by: James Wells <james@jameswells.net>
2021-04-25 23:08:12 -04:00
tzugen 0a170918c8
Adjust Timber debug level 2021-04-25 16:49:14 +02:00
tzugen cd27734c04
Emulate keycode for the Session callbacks 2021-04-23 15:04:01 +02:00
tzugen 8004bc31fe
Add PlaybackState actions
.. and map PlayerStates more precisely.
2021-04-23 11:26:15 +02:00
tzugen 410e1df980
Upgrade detekt to v1.16.0 to get Type Resolution support
In course I had to modify the rules (many were renamed or dropped),
and create a new baseline (many new rules added errors in old files)
2021-04-23 11:26:15 +02:00
tzugen 0ad6d0b691
Move MediaPlayerService to Kotlin dir 2021-04-23 11:26:15 +02:00
tzugen 3aae91bf13
Refactor after conversion 2021-04-23 11:26:15 +02:00
tzugen 35e89b47c6
Convert MediaPlayerService to Kotlin 2021-04-23 11:26:15 +02:00
tzugen 9472aa4c22
Rename .java to .kt 2021-04-23 11:26:14 +02:00
tzugen df54d3450b
Detekt: Remove "warningThreshold and failThreshold properties are deprecated. Please use the new 'maxIssues' config property." 2021-04-23 11:26:14 +02:00
tzugen e6135dc21a
Set Session to active 2021-04-23 11:26:14 +02:00
tzugen 205f477b43
Refactor all event listeners to lambdas 2021-04-23 11:26:10 +02:00
tzugen 2adb9ffc7e
Remove two warnings 2021-04-23 11:21:28 +02:00
tzugen f27212f263
Set AudioAttributes in LocalMediaPlayer 2021-04-23 11:21:24 +02:00
tzugen e63f24242b
Change detekt config: Allow TODO comments, but continue to block FIXME comments
Our code still has so many TODOs that sometimes they can't be fixed immediately
because the require a much larger refactor, and are also so specific that they
don't warrant an own bug.
2021-04-23 11:20:13 +02:00
tzugen 5f8e3ce851
Modernize AudioFocusHandler 2021-04-23 11:20:09 +02:00
tzugen 8640d39438
We need androidx.media at v1.3.0.
Also do the minor gradle update
2021-04-23 11:13:44 +02:00
tzugen 37632dd031
Convert AudioFocusHandler to Kotlin 2021-04-23 11:13:44 +02:00
tzugen 0550116b35
Rename .java to .kt 2021-04-23 11:13:44 +02:00
tzugen 8bfc5d04ef
Pass keyevents from the session to MediaPlayerLifecycleSupport 2021-04-23 11:13:44 +02:00
tzugen 88f6bdb3a9
setShowBadge = false 2021-04-23 11:13:44 +02:00
tzugen e4bf431cfe
Add deleteIntent 2021-04-23 11:13:43 +02:00
tzugen d3b94f3d4c
Add Star/Unstar and close Action
Also: Add album to subtitle :)
2021-04-23 11:13:43 +02:00
tzugen 1ee36322db
Remove unused views and util function 2021-04-23 11:13:43 +02:00
tzugen 116307df56
Fix a regression were we accidentally reset our current MediaPlayer
Fixes #418
2021-04-23 11:13:43 +02:00
tzugen 731447fda5
Improve icon sizes 2021-04-23 11:13:43 +02:00
tzugen 19580cda8b
We can now remove the deprecated Remote Control API 2021-04-23 11:13:43 +02:00
tzugen 26ba022003
Modernize Service Notification
Use the native Android way of generating playback notifications,
by using a "Media Style" and creating a Media Session.
2021-04-23 11:13:43 +02:00
tzugen 417496aacc
Remove unecessary null-check 2021-04-23 11:13:43 +02:00
Nite 2e4dde099d
Merge pull request #429 from tzugen/428
Fix #428
2021-04-22 08:33:12 +02:00
tzugen 6c681ddb52
Fix #428 2021-04-21 18:42:59 +02:00
Nite d41bd8bc07
Merge pull request #425 from tzugen/gradle-ok-revert
Revert okHTTP to 3.10.0
2021-04-19 19:08:35 +02:00
tzugen c59a38777f
Set okHTTP to version 3.12.x, it is the last branch with full API compatibility, which still receives important fixes 2021-04-19 18:35:50 +02:00
Nite 3aee0d5693
Merge pull request #424 from tzugen/gradle
Remove gradle warning
2021-04-19 15:54:31 +02:00
tzugen d0ad8cef7d
Remove deprecated kotlin-android-extension plugin 2021-04-19 12:21:42 +02:00
tzugen 9cb2ed3356
Update gradle plugin and androidxcore
We need core at 1.5.0-rc01 to be able to clearActions()
2021-04-19 12:21:42 +02:00
tzugen f1ce568b5e
Update kotlin, room, okhttp 2021-04-19 12:21:42 +02:00
tzugen b5806cbbbf
Set language level 1.8 correctly also to Kotlin compiler,
removed unused variables.
2021-04-19 12:21:36 +02:00
tzugen 42967ef45f
Remove warning about deprecated 'variant.getGenerateBuildConfig()' 2021-04-18 22:18:41 +02:00
Nite 4f7da06e26
Merge pull request #413 from tzugen/download
Migrate DownloadFile to Kotlin, add percentage display
2021-04-18 16:33:00 +02:00
tzugen 467df602ec
Show download percentage also in the title 2021-04-17 12:32:29 +02:00
tzugen 4787924fcc
Remove noisy log calls 2021-04-17 12:32:29 +02:00
tzugen 8d4d1ba660
Move function out of companion object 2021-04-17 12:32:29 +02:00
tzugen 0efb3547c3
Update license of two files. 2021-04-17 12:32:29 +02:00
tzugen d9d82b77d4
Rename a function semantically 2021-04-17 12:32:25 +02:00
tzugen a97cb30d1c
Move DownloadFile.kt to Kotlin directory 2021-04-17 12:11:21 +02:00
tzugen aac74d1eef
Make SongView display a percentag while downloading
Closes #403
2021-04-17 12:11:20 +02:00
tzugen cf68038e20
Migrate DownloadFile to Kotlin 2021-04-17 12:11:17 +02:00
tzugen 3139c94d11
Rename .java to .kt 2021-04-17 12:02:45 +02:00
tzugen a13306c680
Remove unused parameters from getDownloadInputStream() 2021-04-17 12:02:45 +02:00
tzugen 6b750dac4d
Rename download layouts semantically. 2021-04-17 12:02:45 +02:00
tzugen 0bf0d2db87
Fix a number of lint issues 2021-04-17 12:02:45 +02:00
Nite c16d6b5181
Merge pull request #414 from tzugen/mediastore
Mediastore to Kotlin, fix #383
2021-04-17 11:43:36 +02:00
tzugen b438559da6
Update license of MediaStoreService.kt 2021-04-16 11:47:16 +02:00
Nite fbcbc65be8
Merge branch 'develop' into mediastore 2021-04-15 09:48:07 +02:00
Nite ef774cf0bf
Merge pull request #419 from SaintDubious/emptyText
Clean empty text view
2021-04-15 07:53:17 +02:00
James Wells e5158c8a2f
correct for swipe refresh
Signed-off-by: James Wells <james@jameswells.net>
2021-04-14 22:25:31 -04:00
James Wells 39c94fe1e9
Clean empty text view
Signed-off-by: James Wells <james@jameswells.net>
2021-04-13 22:35:06 -04:00
Nite 9e16b69fc3
Merge pull request #410 from SaintDubious/SelectMusicFolderView
Introduce the SelectMusicFolderView
2021-04-13 09:52:10 +02:00
James Wells 38b6066258
small change to view group
Signed-off-by: James Wells <james@jameswells.net>
2021-04-13 00:12:38 -04:00
James Wells 3a1e2c96fc
Don't show header when using ID3 tags
Signed-off-by: James Wells <james@jameswells.net>
2021-04-11 16:02:07 -04:00
tzugen 8fbc2a9fa3
Remove scanMedia(), as ACTION_MEDIA_SCANNER_SCAN_FILE is deprecated,
in favour of the MediaStore API which we are already using.
2021-04-11 08:49:17 +02:00
tzugen 0349746228
Move Kotlin file 2021-04-11 01:20:16 +02:00
tzugen 86db983f81
Migrate MediaStore to Kotlin, fix issues wih wrong path 2021-04-11 01:19:27 +02:00
tzugen 46a56a1797
Rename .java to .kt 2021-04-11 01:14:17 +02:00
James Wells 268a41d1f4
Don't use musicFolderId in Recently Added filter
Signed-off-by: James Wells <james@jameswells.net>
2021-04-10 17:48:52 -04:00
James Wells f00a487b51
loads music folder list
Signed-off-by: James Wells <james@jameswells.net>
2021-04-09 22:41:38 -04:00
James Wells 580fc1aa89
review comments
Signed-off-by: James Wells <james@jameswells.net>
2021-04-06 22:55:21 -04:00
James Wells 2672ea7edf
Merge branch 'develop' into SelectMusicFolderView
Signed-off-by: James Wells <james@jameswells.net>
2021-04-06 18:36:12 -04:00
Nite 2650b51a6c
Merge pull request #405 from tzugen/NPE
Refactor LocalMediaPlayer to Kotlin, fix #390
2021-04-06 07:49:16 +02:00
James Wells 3e54e4cf05
restrict header to album list only
Signed-off-by: James Wells <james@jameswells.net>
2021-04-04 15:30:31 -04:00
James Wells 191f9512bb
Introduce the SelectMusicFolderView
Signed-off-by: James Wells <james@jameswells.net>
2021-04-01 23:00:01 -04:00
tzugen f738bca1ad
Merge remote-tracking branch 'origin/develop' into NPE 2021-03-30 14:39:09 +02:00
tzugen 05067aaf3c
Rename onCreate/onDestroy functions; create a new MediaPlayer instance on release() 2021-03-30 14:38:59 +02:00
Nite 97d68eb08f
Merge pull request #408 from nitehu/fix/video_player
Fixed VideoPlayer using wrong context
2021-03-27 20:28:04 +01:00
Nite fa5df96ee3
Fixed VideoPlayer using wrong context 2021-03-27 20:14:56 +01:00
tzugen 974afda777
Merge remote-tracking branch 'mine/NPE' into NPE 2021-03-25 20:34:48 +01:00
tzugen 970d93bd91
Pass down the autoPlay value to BufferTask() 2021-03-25 20:33:51 +01:00
tzugen b64a4d1320
Merge branch 'develop' into NPE 2021-03-25 18:44:30 +01:00
tzugen bf106b0384
Handle a case when reset() is called after release(). 2021-03-25 18:37:41 +01:00
tzugen 51dafd542a
Static analysis fixes 2021-03-25 18:37:41 +01:00
tzugen 8e7cf487fd
Checkstyle fixes 2021-03-25 18:37:41 +01:00
tzugen 8d65b1d25f
Private BufferTask() accepts only non-null now 2021-03-25 18:37:41 +01:00
tzugen 2171c971a3
Make remoteControl code more functional
Fixes #390
2021-03-25 18:37:41 +01:00
tzugen 60a0fe17dd
Fix a bad cast 2021-03-25 18:37:41 +01:00
tzugen 493a587b37
Introduce postRunnable helper function 2021-03-25 18:37:41 +01:00
tzugen 93eced9516
Move bufferAndPlay to another position in the file; annotate some possible bugs. 2021-03-25 18:37:41 +01:00
tzugen 09fb6aa487
Make doPlay method a private method, and play the only public entry point. 2021-03-25 18:37:41 +01:00
tzugen d017ca9fb2
Initialize some vals onCreate, thus making them explicitely non-null
Also modify setNextPlaying to accept only non-null files.
2021-03-25 18:37:41 +01:00
tzugen a467abf10b
Fix errors and warnings 2021-03-25 18:37:40 +01:00
tzugen 2260cc311f
Automatic conversion of LocalMediaPlayer to Kotlin 2021-03-25 18:37:40 +01:00
Nite 031c969730
Merge pull request #400 from nitehu/fix/repeat_all_next
Fixed Next button behavior when Repeat All is enabled
2021-03-19 19:52:27 +01:00
Nite d1af2d568e
Merge branch 'develop' into fix/repeat_all_next 2021-03-19 19:41:01 +01:00
Nite ef29f26893
Fixed Next behavior in additional places 2021-03-19 19:25:34 +01:00
Nite 6a53644355
Fixed Next button behavior when Repeat All is enabled 2021-03-19 17:47:32 +01:00
Óscar García Amor 4747aa3ce4
[skip ci] Updated fastlane changelogs 2021-03-19 12:27:28 +01:00
Óscar García Amor f361f584b9
Bump version to 2.19.2 2021-03-19 12:09:35 +01:00
Óscar García Amor 4cda3730f1
Merge branch 'SaintDubious-convertSelectAlbumFragment3' into develop 2021-03-19 12:06:00 +01:00
James Wells f0be828bbf
Convert SelectAlbumFragment to kotlin
Signed-off-by: James Wells <james@jameswells.net>
2021-03-18 23:23:00 -04:00
Nite 63572d84ea
Merge pull request #395 from nitehu/fix/podcasts
Fixed layout to correctly display podcasts
2021-03-17 17:41:23 +01:00
Nite a9ea033802
Fixed layout to correctly display podcasts
Added working swipe refresh
2021-03-17 17:26:16 +01:00
Óscar García Amor 88cd866e03
[skip ci] Updated fastlane changelogs 2021-03-07 18:02:31 +01:00
Óscar García Amor 0828f7bffe
Bump version to 2.19.1 2021-03-07 17:52:16 +01:00
Óscar García Amor 0bf0c1cd21
Don't remove NowPlayingFragment 2021-03-07 17:49:30 +01:00
Óscar García Amor 670a8c7886
[skip ci] Updated fastlane changelogs 2021-03-07 16:00:20 +01:00
Óscar García Amor 101d02560f
[skip ci] Remove BIG FAT WARNING 2021-03-07 15:46:35 +01:00
Óscar García Amor 5a3b52b7fe
Bump version to 2.19.0 2021-03-07 15:37:00 +01:00
Óscar García Amor 51ecfeb43f
Updated translations 2021-03-07 15:28:10 +01:00
Nite b235acf521
Merge pull request #382 from nitehu/refactor/menudrawer_to_navigationui
Refactor menudrawer to navigationui
2021-03-07 15:21:03 +01:00
Nite 2eb357a50f
Fixed ktLint 2021-03-01 17:26:58 +01:00
Nite 46859e2413
Minor cleanup, added comments 2021-03-01 17:24:25 +01:00
Nite 9910792c11
Fixed back press in Server Editor
Fixed displaying "no results" for Search
Cleaned up code
2021-02-25 18:40:41 +01:00
Nite 8a047c5b78
Fixed coroutine scope for server creation/update 2021-02-17 18:36:36 +01:00
Nite 6fcdfea1db
Fixed ChatFragment layout 2021-02-16 19:25:10 +01:00
Nite 09c8c56f59
Fixed searched text not visible for voice search 2021-02-16 19:11:06 +01:00
Nite 81ce23bafd
Updated PreferenceManager to androidx version 2021-02-15 16:30:36 +01:00
Nite 785adedd4f
Merge branch 'develop' into refactor/menudrawer_to_navigationui 2021-02-14 16:23:47 +01:00
Nite 86bfcefe93
Minor fixes
Cleaned up TODOs
Cleaned up code
2021-02-14 15:55:16 +01:00
Nite d70d2cc2fb
Fixed TODOs:
- Updated Koin
- Fixed Player start from widgets and notification
- Added cancellation to LoadingTask
- Minor fixes and cleanup
2021-02-11 21:51:27 +01:00
Nite 3dc6df9116
Fixed colors and theme changing
Other minor fixes
2021-02-10 19:57:09 +01:00
Nite c4ea2086d2
Removed unused code, minor fixes 2021-02-09 19:49:26 +01:00
Nite 1b9b127424
Refactored remaining Activities 2021-02-08 21:46:31 +01:00
Nite cf90abb77e
Added NowPlayingFragment 2021-02-08 20:24:20 +01:00
Nite f0917820cb
Migrated parts from SubsonicTabActivity, fixed theme changes 2021-02-06 11:50:57 +01:00
Nite a395bd6feb
Refactored DownloadActivity 2021-02-05 21:45:50 +01:00
Nite 95773c7994
Started refactoring to NavigationUI
Main menu items are refactored, except Now Playing
2021-02-04 20:15:58 +01:00
Óscar García Amor c3899af521
Update pt_BR translation
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2021-01-19 15:32:57 +01:00
Óscar García Amor d01266804c
Only push new original strings to Transifex
This avoid overwriting translated strings, but translations must be done
in Transifex.

Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2021-01-19 15:26:32 +01:00
Óscar García Amor 9908f76555
Add Fastlane structure. Closes #370
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2021-01-19 13:51:36 +01:00
Óscar García Amor 6dbe9fb2cc
Fix translation problem with plurals in fr_FR
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2021-01-19 09:32:02 +01:00
Óscar García Amor 3c0485c98b
Merge branch 'develop' 2021-01-19 09:07:22 +01:00
Óscar García Amor bda1deae2b
Bump version to 2.18.0
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2021-01-19 08:52:13 +01:00
Óscar García Amor 4fdab06271
Added big fat warning about #310 in README.md
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2021-01-19 08:33:57 +01:00
Óscar García Amor 193fa38dd0
Merge pull request #376 from nitehu/refactor/show_now_playing
Changed how the NowPlaying view is displayed and hidden
2021-01-15 09:14:09 +01:00
Nite 8ac0b28156
Merge branch 'develop' into refactor/show_now_playing 2021-01-14 09:49:12 +01:00
Nite 90090ba870
Changed how the NowPlaying view is displayed and hidden 2021-01-14 09:30:23 +01:00
Óscar García Amor 3c47ea890f
Bump version to 2.17.3
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2021-01-13 08:51:20 +01:00
Nite 3749f30691
Fixed Genres list 2021-01-13 08:43:40 +01:00
Nite 20de65d1fd
Merge pull request #373 from nitehu/develop
Fixed Genres list
2021-01-12 17:08:54 +01:00
Nite b7bb0851db
Fixed Genres list 2021-01-12 16:51:31 +01:00
Óscar García Amor 7d779315e5
Merge pull request #365 from lbonn/no-sort-special-collections
Do not sort special collections
2020-12-29 16:48:13 +01:00
Óscar García Amor 3c9dcbc0a3
Merge branch 'develop' into no-sort-special-collections 2020-12-29 16:31:51 +01:00
Óscar García Amor 5839c804b3
Merge pull request #364 from lbonn/compare-path-fallback
Fall back to path when comparing tracks
2020-12-29 16:31:27 +01:00
lbonn f43c107269
Do not sort special collections
It does not make sense to sort:

- random songs
- recently added albums
- recently played albums
- most played albums
- top rated albums
- random albums

As the order returned by the server is more relevant in these cases

Signed-off-by: lbonn <bonnans.l@gmail.com>
2020-12-21 23:35:19 +01:00
lbonn 7af666037d
Fall back to path when comparing tracks
Tracks will be sorted by ascending path if neither album, disc and track
id can help.

This is helpful for browsing a loose collection of tracks tagged under
the same album.

Signed-off-by: lbonn <bonnans.l@gmail.com>
2020-12-21 22:20:35 +01:00
Óscar García Amor 0ce5a56051
Update CONTRIBUTING.md
Add a link with more info about sign commits.
2020-12-18 09:42:24 +01:00
Óscar García Amor 70f86441d1
Bump version to 2.17.2
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-12-10 11:14:19 +01:00
Nite 41f9b4ad73
Merge pull request #361 from nitehu/fix/search_artist
Fixed artist items in search
2020-12-07 17:21:38 +01:00
Nite 543cb21cd1
Fixed artist items in search 2020-12-07 16:34:59 +01:00
Óscar García Amor d57248d160
Bump version to 2.17.1
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-11-30 09:14:25 +01:00
Nite e446785d0e
Merge pull request #358 from nitehu/fix/api29
Updated API to 29, fixed failing tests
2020-11-30 08:50:45 +01:00
Nite fe120a0bf2
Updated API to 29, fixed failing tests 2020-11-29 13:25:06 +01:00
Óscar García Amor 51cdbe7084
Add the new translations to resConfigs 2020-11-29 12:35:12 +01:00
Óscar García Amor 74b484fc1d
Bump version to 2.17.0
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-11-29 11:51:19 +01:00
Óscar García Amor 3afecf5fea
Merge branch 'translations' into develop 2020-11-29 11:47:14 +01:00
Óscar García Amor 5796e25923
Add new transifex translations 2020-11-29 11:40:42 +01:00
Óscar García Amor b471b9cad0
Merge branch 'nitehu-refactor/artist_list' into develop 2020-11-29 11:11:30 +01:00
Nite d98360d3fe
Really fixed getSectionName 2020-11-28 13:51:51 +01:00
Nite 7068477e23
Fixed getSectionName 2020-11-28 13:33:29 +01:00
Nite c10d08b862
Added translation for new strings 2020-11-27 16:13:00 +01:00
Nite 4336e8e243
Added setting to show artist picture 2020-11-25 20:00:12 +01:00
Nite a3c4815a07
Fixed Artist row layout
Changed Unknown image
2020-11-24 18:19:58 +01:00
Nite b3db46b1ab
Fixed ktlint errors 2020-11-23 14:02:18 +01:00
Nite 7dcaf5b1a6
Added Fastscroll Library with section popup support
Fixed collation for the sorting of tha Artist list
2020-11-23 13:55:28 +01:00
Nite 47e5675d1e
Added coverArt images to Artists
Minor fixes
2020-11-18 21:46:46 +01:00
Nite 34a6413f10
Migrated SelectArtistActivity to Kotlin
Updated SelectArtistActivity to use RecyclerView
Changed old progress display to use SwipeRefreshLayout's spinner
Added alphabetic side index
Enabled RecyclerView's FastScroll
2020-11-16 19:14:44 +01:00
Óscar García Amor 6aa29d54fe
Bump version to 2.16.0
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-11-07 11:20:18 +01:00
Nite 74d0c678df
Merge pull request #350 from nitehu/feature/single_button_play_pause
Implemented single button play/pause support for old Bluetooth devices
2020-11-06 18:23:56 +01:00
Nite 92f01fec26
Implemented single button play/pause support for old Bluetooth devices 2020-11-06 18:08:36 +01:00
Óscar García Amor a426b897e3
Merge pull request #347 from ultrasonic/bigger-star-icon
Set the star icon BIGGER
2020-11-05 13:04:29 +01:00
Óscar García Amor be3f9c4b15
Set the star icon BIGGER
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-11-05 12:54:34 +01:00
Óscar García Amor 7813c0373d
Merge pull request #346 from ultrasonic/improve-player
Improve player layout
2020-11-05 08:34:41 +01:00
Óscar García Amor 3fca8bd7a5
Improve landscape player view
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-11-04 21:20:57 +01:00
Óscar García Amor fb2dab0a25
Reorder elements in player view
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-11-04 21:00:16 +01:00
Óscar García Amor 8d45a3d106
Merge branch 'nitehu-fix/icon_sizes' into develop 2020-11-04 18:22:17 +01:00
Óscar García Amor 3f6c45f25f
Make media icons even BIGGER
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-11-04 18:21:42 +01:00
Nite d98f64ed25
Fixed too small icons and margins 2020-11-04 16:51:45 +01:00
Nite 19f8196268
Merge pull request #343 from nitehu/fix/server_settings_rotate
Fixed Server Settings screen to keep entered data when rotating the screen
2020-11-02 21:04:16 +01:00
Nite 8ebda198c6
Fixed Server Settings screen to keep entered data when rotating the screen 2020-11-02 17:13:38 +01:00
Nite 6e1d0e5b2e
Merge pull request #340 from nitehu/fix/songview_nullcheck
Fixed null checks in SongView
2020-11-01 08:13:12 +01:00
Nite df53da2439
Fixed copyright comments 2020-11-01 08:02:10 +01:00
Nite 537478430c
Fixed null checks in SongView
Migrated SongView to Kotlin
2020-10-31 08:51:43 +01:00
Óscar García Amor 645f524ce5
Bump version to 2.15.0
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-10-25 11:13:56 +01:00
Óscar García Amor 440b2109d3
Merge branch 'nitehu-fix/equalizer_visualizer_init' into develop 2020-10-25 10:58:37 +01:00
Óscar García Amor cf19736049
Merge branch 'develop' into fix/equalizer_visualizer_init 2020-10-25 10:38:49 +01:00
Óscar García Amor 5c637a2e9c
Merge branch 'nitehu-fix/store-api-version' into develop 2020-10-25 10:30:08 +01:00
Nite 5c81097df8
Merge branch 'develop' into fix/equalizer_visualizer_init 2020-10-20 11:59:37 +02:00
Nite bd5440bb96
Merge branch 'develop' into fix/store-api-version 2020-10-20 11:52:08 +02:00
Nite 9341fd3df3
Merge pull request #336 from nitehu/fix/clear_playlist
Fixed clearing the playlist when MediaPlayerService isn't running
2020-10-20 11:51:45 +02:00
Nite 9eeff3fabc
Fixed clearing the playlist when MediaPlayerService isn't running 2020-10-20 11:29:05 +02:00
Nite 30a5abaa34
Fixed exception on Room downgrade 2020-10-17 12:38:48 +02:00
Nite 0482c540bd
Updated Equalizer and Visualizer to use late initialization with LiveData 2020-10-17 12:35:30 +02:00
Nite 273bee1341
Added ACCEPT_SINGLE_VALUE_AS_ARRAY = true to JacksonMapper to maximize compatibility 2020-10-16 15:04:26 +02:00
Nite eb3ce0db8a
Added creating the client again after setting the minimum API version to NULL 2020-10-15 14:34:10 +02:00
Nite 4e6df12f4e
Moved minimumApiVersion detection to be executed before any first request
Refactored RESTMusicService to Kotlin
Refactored OfflineMusicService not to be a subclass of RESTMusicService
Minor fixes
2020-10-15 10:22:15 +02:00
Nite a396b4b27b
Fixed Subsonic API version detection
Fixed server change detection
Minor fixes
2020-10-13 21:41:01 +02:00
Óscar García Amor 356af198e0
Bump version to 2.14.0
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-10-10 11:54:11 +02:00
Óscar García Amor 0c50995ad2
Merge branch 'nitehu-fix/genres-api' into develop 2020-10-10 10:53:48 +02:00
Nite fe591ce0c6
Fixed removed constructor by minify in genres API 2020-10-09 21:38:35 +02:00
Óscar García Amor 768e8e6fb5
Remove more untranslated strings
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-10-08 14:19:24 +02:00
Óscar García Amor 3eeb9618f2
Deleted untranslated strings from values files
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-10-08 09:25:26 +02:00
Nite 0b21dfdb2d
Merge branch 'develop' into feature/log_to_file 2020-10-07 21:05:54 +02:00
Nite 778ee380e8
Merge pull request #329 from nitehu/fix/add_opus
Fixed Opus file handling and offline file name bug
2020-10-07 21:04:55 +02:00
Nite 1f7167e016
Removed file existence check to improve performance 2020-10-07 20:52:58 +02:00
Nite b16dde0b07
Fixed Opus file handling
Fixed file name generation in offline mode
2020-10-07 17:52:16 +02:00
Nite 60a8599a02
Merge branch 'develop' into feature/log_to_file 2020-10-06 17:07:16 +02:00
Óscar García Amor 25854ab808
Merge pull request #326 from Maxmystere/develop
Add Material Black Theme
2020-10-06 15:41:16 +02:00
Max Pop d4ecc5825d
Merge remote-tracking branch 'base/develop' into develop 2020-10-05 11:49:14 +02:00
Max Pop c565dfef24
Revert "Add basic translation"
This reverts commit cd4b1239fc.
2020-10-05 11:48:42 +02:00
Nite 8fa7fdd18b
Merge branch 'develop' into feature/log_to_file 2020-10-05 10:52:24 +02:00
Óscar García Amor dcd27919a2
Do not treat missing translations as errors
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-10-05 09:08:50 +02:00
Max Pop 675a32a3ef
Remove useless import 2020-10-05 01:59:53 +02:00
Max Pop cd4b1239fc
Add basic translation 2020-10-05 01:53:23 +02:00
Max Pop 6ae3c9692a
Add Material Black Theme 2020-10-05 01:45:58 +02:00
Óscar García Amor 57a89c4a77
Bump version to 2.13.0
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-10-04 11:41:51 +02:00
Nite e15fa23def
Fixed ktLint errors 2020-10-02 19:08:05 +02:00
Nite 6e7ebeabef
Implemented file logging settings
Implemented log rotation, log deletion
Minor fixes
2020-10-02 18:47:21 +02:00
Nite 5de20861ca
Added Timber to Koin and OkHttp, started implementing file logging 2020-09-30 18:17:57 +02:00
Nite 7217f2cca8
Refactored Log calls to Timber 2020-09-30 14:47:59 +02:00
Nite 60b6a5dbde
Merge pull request #318 from nitehu/feature/bluetooth_connection_settings
Implemented Bluetooth connection settings
2020-09-29 18:10:25 +02:00
Nite 6355a7ed80
Fixed missing Intent in AndroidManifest 2020-09-29 14:13:49 +02:00
Nite f6e61b5484
Clarified strings for options 2020-09-29 13:58:15 +02:00
Nite df3ef9c04e
Merge branch 'develop' into feature/bluetooth_connection_settings 2020-09-29 12:14:21 +02:00
Nite 75a9c60889
Fixed handling Bluetooth intents while Ultrasonic isn't running 2020-09-29 11:09:24 +02:00
Óscar García Amor 7a6aa950ac
Run build command with assembleRelease in CI
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-29 09:17:15 +02:00
Nite c805005b02
Added resume and pause settings for bluetooth device connection / disconnection 2020-09-28 21:14:47 +02:00
Nite 046916221d
Started implementing Bluetooth connection settings 2020-09-28 21:14:30 +02:00
Nite 9f8641388b
Merge pull request #316 from nitehu/develop
Fix gradle out of memory error
2020-09-28 18:14:03 +02:00
Nite d189026c18
Fix gradle out of memory error 2020-09-28 18:00:10 +02:00
Óscar García Amor 81448e34d6
Bump version to 2.12.0
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-28 15:15:20 +02:00
Nite ff97c0138c
Merge pull request #314 from nitehu/fix/edit_server
Fixed Edit Server button layout and exit check
2020-09-25 20:19:01 +02:00
Nite 4b85bc16d5
Merge branch 'develop' into fix/edit_server 2020-09-25 20:11:14 +02:00
Nite 4991eb8d13
Fixed Edit Server button layout and exit check 2020-09-25 20:02:59 +02:00
Óscar García Amor bd3e4df805
Merge pull request #313 from ogarcia/fix-unknown-image
Fix fatal error loading unknown album cover
2020-09-25 19:42:21 +02:00
Óscar García Amor 8c07346296
Fix fatal error loading unknown album cover
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-25 19:36:35 +02:00
Óscar García Amor 2a7ea19a0f
Merge pull request #312 from ultrasonic/vector
Vector
2020-09-25 18:33:28 +02:00
Óscar García Amor 5387b58e35
Finish convert drawables to vectors. Closes #308
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-25 18:20:18 +02:00
Óscar García Amor 89ef73ccec
Merge branch 'develop' into vector 2020-09-25 13:35:11 +02:00
Óscar García Amor 81a0ff2967
Deleted some unused assets
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-25 13:34:08 +02:00
Nite 333b147c02
Merge pull request #309 from nitehu/fix/new_server_id
Fixed issues with new Server Settings
2020-09-25 12:33:36 +02:00
Nite ef09807e3e
Fixed stopping playback when selecting the same server 2020-09-25 11:15:19 +02:00
Nite 7bbc3234f2
Added applying changes when the currently active server is modified 2020-09-25 11:04:48 +02:00
Nite db4f8b83d3
Updated missing index handling 2020-09-25 10:43:33 +02:00
Óscar García Amor 81f248f446
Convert drawables to vectors
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-24 22:00:29 +02:00
Nite 02716c827a
Added new preference key to avoid duplicate migration 2020-09-24 21:00:21 +02:00
Nite 102f3e8a04
Updated ServerSettingsModel to use Room for handling LiveData 2020-09-24 20:32:51 +02:00
Nite 5b0c9906f1
Fixed Server Id and Indexing 2020-09-24 18:20:59 +02:00
Nite 59c0054a11
Merge pull request #306 from nitehu/fix/genre_refresh
Fixed missing Genre Refresh functionality
2020-09-24 15:30:42 +02:00
Nite 481084686b
Fixed missing Genre Refresh functionality 2020-09-24 15:16:59 +02:00
Óscar García Amor 4b36730358
Add a deleted by mistake file
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-24 14:19:33 +02:00
Óscar García Amor a411ed63e2
Fix Select Folder item. Closes #305
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-24 13:19:25 +02:00
Óscar García Amor a7d29a4093
More UltraSonic -> Ultrasonic
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-24 13:03:05 +02:00
Óscar García Amor f0f9e3d438
UltraSonic -> Ultrasonic
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-24 12:35:35 +02:00
Óscar García Amor 36e25253be
Fix translations
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-24 12:21:17 +02:00
Óscar García Amor c6ed320e12
Merge branch 'nitehu-feature/updated-server-settings' into develop 2020-09-24 11:36:55 +02:00
Nite 31853cc8bc
Made the hostname part of the server url mandatory 2020-09-23 17:14:14 +02:00
Nite b7c21e4aac
Fixed Server Id to be the same as the Preference was 2020-09-23 16:26:54 +02:00
Nite 55105e52b6
Fixed ktlint errors 2020-09-22 22:01:33 +02:00
Nite 06109424ec
Index fix, added logging 2020-09-22 21:50:53 +02:00
Nite bed29ce33b
Fixed migrated server indexing 2020-09-22 20:18:36 +02:00
Nite cc954e4d5a
Fixed migration to check for empty database at every start 2020-09-22 17:03:04 +02:00
Nite e8310b2ac8
Merge branch 'develop' into feature/updated-server-settings 2020-09-22 11:40:25 +02:00
Óscar García Amor 1f06860dcf
Merge branch 'Maxmystere-develop' into develop 2020-09-22 11:29:29 +02:00
Max Pop 251e575421
Resign all with gpg key 2020-09-21 12:17:55 +02:00
Nite 234e4703a1
Fixed first time migration
Minor fixes
2020-09-19 11:56:10 +02:00
Nite 6721500202
Updated Server Settings UI and Storage
Updated Koin to latest
2020-09-18 09:37:19 +02:00
Óscar García Amor 4d8e7f0631
Fix typos in Ultrasonic word
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-07 12:31:44 +02:00
Óscar García Amor b04f309b9a
Bump version to 2.11.2
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-05 14:36:15 +02:00
Óscar García Amor 387457cf99
Merge branch 'usability' into develop 2020-09-05 14:28:59 +02:00
Óscar García Amor 0f7238e961 Fix layout in server selector
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-05 13:57:06 +02:00
Óscar García Amor f9199eebd3 Improve usability
- Add more padding to stars in song lists to avoid touch scrollbar.
- Add more space between border of screen and notification close button,
  enlarge the button in 10dp.

Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-05 13:23:37 +02:00
Óscar García Amor f07f0344fd Bump version to 2.11.1
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-04 09:24:50 +02:00
Óscar García Amor a288a0e3de Merge branch 'nitehu-fix/jukebox' into develop 2020-09-04 09:14:14 +02:00
Nite 6beb1ae42e
Fixed Jukebox start bug 2020-09-03 16:05:38 +02:00
Óscar García Amor bda0b61459 [skip ci] Remove BIG FAT WARNING from README
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-02 18:56:08 +02:00
Óscar García Amor 9887a49ed6 Bump version to 2.11.0
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2020-09-02 18:34:17 +02:00
Óscar García Amor 459b8b823d Merge branch 'nitehu-fix/server-version-update' into develop 2020-09-02 18:24:59 +02:00
Nite 0f42ee20cc
Fixed ktlint errors 2020-08-27 11:02:33 +02:00
Nite ca2bfbf14b
Updated Subsonic API version handling 2020-08-27 10:27:25 +02:00
1322 changed files with 33675 additions and 51918 deletions

View File

@ -1,54 +1,68 @@
version: 2
version: 2.1
parameters:
memory-config:
type: string
default: "-Xmx3200m -Xms256m -XX:MaxMetaspaceSize=1g"
memory-config-debug:
type: string
default: "-Xmx3200m -Xms256m -XX:MaxMetaspaceSize=1g -verbose:gc -Xlog:gc*"
jobs:
build:
docker:
- image: circleci/android:api-28
- image: cimg/android:2022.06.1
working_directory: ~/ultrasonic
envoronment:
JVM_OPTS: -Xmx3200m
environment:
JVM_OPTS: << pipeline.parameters.memory-config >>
JAVA_TOOL_OPTIONS: << pipeline.parameters.memory-config >>
GRADLE_OPTS: << pipeline.parameters.memory-config >>
steps:
- checkout
- restore_cache:
key: gradle-cache-{{ checksum "dependencies.gradle" }}
keys:
- v2-ultrasonic-{{ .Branch }}-{{ checksum "gradle/libs.versions.toml" }}
- v2-ultrasonic-{{ .Branch }}
- v2-ultrasonic
- run:
name: clean gradle.properties
command: echo -e "android.useAndroidX=true\nandroid.enableJetifier=true\n" > gradle.properties
name: configure gradle.properties for CI building
command: |
sed -i '/^org.gradle.jvmargs/d' gradle.properties
sed -i 's/^org.gradle.daemon=true/org.gradle.daemon=false/g' gradle.properties
cat gradle.properties
- run:
name: checkstyle
command: ./gradlew -Pqc ktlintCheck
- run:
name: static analysis
command: ./gradlew -Pqc detektCheck
command: ./gradlew -Pqc detekt
- run:
name: build
name: build debug
command: ./gradlew assembleDebug
- run:
name: unit-tests
command: |
./gradlew ciTest testDebugUnitTest
./gradlew jacocoFullReport
bash <(curl -s https://codecov.io/bash)
- run:
name: lint
command: ./gradlew :ultrasonic:lintRelease
- run:
name: assemble release build
name: build
command: ./gradlew buildRelease
- run:
name: assemble release
command: ./gradlew assembleRelease
- save_cache:
paths:
- ~/.gradle
key: gradle-cache-{{ checksum "dependencies.gradle" }}
key: v2-ultrasonic-{{ .Branch }}-{{ checksum "gradle/libs.versions.toml" }}
- store_artifacts:
path: ultrasonic/build/reports
destination: reports
- store_artifacts:
path: subsonic-api/build/reports
destination: reports
- store_artifacts:
path: build/reports/jacoco/jacocoFullReport/
push_translations:
docker:
- image: circleci/python:3.6
- image: cimg/python:3.6
working_directory: ~/ultrasonic
steps:
- checkout
@ -65,17 +79,22 @@ jobs:
name: push changes in translation files
command: |
. ~/venv/bin/activate
tx push -st
tx push -s
generate_signed_apk:
docker:
- image: circleci/android:api-28
- image: cimg/android:2022.06.1
working_directory: ~/ultrasonic
envoronment:
JAVA_TOOL_OPTIONS: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
environment:
JVM_OPTS: << pipeline.parameters.memory-config >>
JAVA_TOOL_OPTIONS: << pipeline.parameters.memory-config >>
GRADLE_OPTS: << pipeline.parameters.memory-config >>
steps:
- checkout
- restore_cache:
key: gradle-cache-{{ checksum "dependencies.gradle" }}
keys:
- v2-ultrasonic-{{ .Branch }}-{{ checksum "gradle/libs.versions.toml" }}
- v2-ultrasonic-{{ .Branch }}
- v2-ultrasonic
- run:
name: decrypt ultrasonic-keystore
command: openssl aes-256-cbc -K ${ULTRASONIC_KEYSTORE_KEY} -iv ${ULTRASONIC_KEYSTORE_IV} -in ultrasonic-keystore.enc -out ultrasonic-keystore -d
@ -85,23 +104,24 @@ jobs:
- run:
name: sign release apk
command: |
export PATH="${JAVA_HOME}/bin:${PATH}"
mkdir -p /tmp/ultrasonic-release
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ~/ultrasonic/ultrasonic-keystore -storepass ${ULTRASONIC_KEYSTORE_STOREPASS} -keypass ${ULTRASONIC_KEYSTORE_KEYPASS} ultrasonic/build/outputs/apk/release/ultrasonic-release-unsigned.apk ultrasonic
jarsigner -verify ultrasonic/build/outputs/apk/release/ultrasonic-release-unsigned.apk
${ANDROID_HOME}/build-tools/27.0.0/zipalign -v 4 ultrasonic/build/outputs/apk/release/ultrasonic-release-unsigned.apk /tmp/ultrasonic-release/ultrasonic-${CIRCLE_TAG}.apk
${ANDROID_HOME}/build-tools/32.0.0/zipalign -v 4 ultrasonic/build/outputs/apk/release/ultrasonic-release-unsigned.apk /tmp/ultrasonic-release/ultrasonic-${CIRCLE_TAG}.apk
${ANDROID_HOME}/build-tools/32.0.0/apksigner sign --verbose --ks ~/ultrasonic/ultrasonic-keystore --ks-pass pass:${ULTRASONIC_KEYSTORE_STOREPASS} --key-pass pass:${ULTRASONIC_KEYSTORE_KEYPASS} /tmp/ultrasonic-release/ultrasonic-${CIRCLE_TAG}.apk
${ANDROID_HOME}/build-tools/32.0.0/apksigner verify --verbose /tmp/ultrasonic-release/ultrasonic-${CIRCLE_TAG}.apk
- persist_to_workspace:
root: /tmp/ultrasonic-release
paths:
- ultrasonic-*.apk
- ultrasonic-*.apk*
publish_github_signed_apk:
docker:
- image: circleci/golang
- image: cimg/go:1.18
steps:
- attach_workspace:
at: /tmp/ultrasonic-release
- run:
name: install ghr
command: go get -v github.com/tcnksm/ghr
command: go install -v github.com/tcnksm/ghr@latest
- run:
name: publish release on github tag
command: ghr -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} ${CIRCLE_TAG} /tmp/ultrasonic-release
@ -117,11 +137,10 @@ workflows:
branches:
only:
- develop
- master
- generate_signed_apk:
filters:
tags:
only: /^[0-9]+(\.[0-9]+)*/
only: /^[0-9]+(\.[0-9]+)*(-beta\.[0-9]+)?/
branches:
ignore: /.*/
- publish_github_signed_apk:
@ -129,7 +148,7 @@ workflows:
- generate_signed_apk
filters:
tags:
only: /^[0-9]+(\.[0-9]+)*/
only: /^[0-9]+(\.[0-9]+)*(-beta\.[0-9]+)?/
branches:
ignore: /.*/

15
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,15 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gradle" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "monthly"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]

1
.gitignore vendored
View File

@ -39,6 +39,7 @@ captures/
*.iml
.idea/
# Keystore files
*.jks

View File

@ -0,0 +1,128 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value />
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

6
.idea/compiler.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="&amp;#36;file.fileName&#10;Copyright (C) 2009-&amp;#36;today.year Ultrasonic developers&#10;&#10;Distributed under terms of the GNU GPLv3 license." />
<option name="myName" value="Default" />
</copyright>
</component>

View File

@ -0,0 +1,7 @@
<component name="CopyrightManager">
<settings default="Default">
<LanguageOptions name="Kotlin">
<option name="fileTypeOverride" value="3" />
</LanguageOptions>
</settings>
</component>

View File

@ -0,0 +1,8 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Reformat" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="processChangedFilesOnly" value="true" />
</inspection_tool>
</profile>
</component>

4
.idea/misc.xml Normal file
View File

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

6
.idea/vcs.xml Normal file
View File

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

View File

@ -18,18 +18,46 @@ By default Pull Request should be opened against **develop** branch, PR against
### Here are a few guidelines you should follow before submitting:
1. **License Acceptance:** All contributions must be licensed as [GNU GPLv3](LICENSE) to be accepted.
Use `git commit --signoff` to acknowledge this.
2. **App is migrating to [Kotlin](https://kotlinlang.org/) programming language:** new Pull Requests
should be written in this programming language.
3. **No Breakage:** New features or changes to existing ones must not degrade the user experience.
4. **Coding standards:** best-practices should be followed, comment generously, and avoid "clever" algorithms.
Use `git commit --signoff` to acknowledge this.
2. **No Breakage:** New features or changes to existing ones must not degrade the user experience.
3. **Coding standards:** best-practices should be followed, comment generously, and avoid "clever" algorithms.
Refactoring existing messes is great, but watch out for breakage.
5. **No large PR:** Try to limit the scope of PR only to the related issue, so it will be easier to review
4. **No large PR:** Try to limit the scope of PR only to the related issue, so it will be easier to review
and test.
### Pull Request Process
On each Pull Request Github runs a number of checks to make sure there are no problems.
#### Signed commits
Commits must be signed. [See here how to set it up](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)
#### KtLint
This programm checks if the source code is formatted correctly.
You can run it yourself locally with
`./gradlew -Pqc ktlintFormat`
Running this command will fix common problems and will notify you of problems it couldn't fix automatically.
#### Detekt
Detekt is a static analyser. It helps to find potential bugs in our code.
You can run it yourself locally with
`./gradlew -Pqc detekt`
There is a "baseline" file, in which errors which have been in the code base before are noted.
Sometimes it is necessary to regenerate this file by running:
`./gradlew -Pqc detektBaseline`
#### Lint
Lint looks for general problems in the code or unused resources etc.
You can run it with
`./gradlew -Pqc lintRelease`
If there is a need to regenerate the baseline, remove `ultrasonic/lint-baseline.xml` and rerun the command.
1. Ensure all commits are signed-off.
2. Check tests for the new code are added.
3. Check code style is passing.
4. Check code static analysis is passing.

View File

@ -1,20 +1,28 @@
## Problem description
Describe your problem here. Describe what you want to happen, and what happens
if you try to do it. If you have a stack trace or any logs, please format them using
github triple backquote notation
Describe your problem here. Describe what you want to happen, and what
happens if you try to do it. If you have a stack trace or any logs, please
format them using GitHub triple backquote notation.
### Steps to reproduce
Describe how somebody else could observe the same behavior you do. Don't share here any logins and
passwords!
Describe how somebody else could observe the same behavior you do. Don't
share here any logins and passwords!
## System information
### Ultrasonic client
* **Ultrasonic version**: *version of the app*
* **Android version**: *Version of Android OS on the device*
* **Device info**: *Device manufacturer, model*
### Server
* **Server name**: *Airsonic, Ampache, Supysonic...*
* **Server version**: *version of server software*
* **Protocol used**: *http or https (self certificate, letsencrypt...)*
## Additional notes
Include any extra notes here. Otherwise you may remove this section.

View File

@ -1,23 +1,25 @@
# Ultrasonic
[![Build Status](https://circleci.com/gh/ultrasonic/ultrasonic/tree/develop.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/ultrasonic)
[![Codecov branch](https://img.shields.io/codecov/c/github/ultrasonic/ultrasonic/develop.svg)]()
[![ktlint](https://img.shields.io/badge/code%20style-%E2%9D%A4-FF4081.svg)](https://ktlint.github.io/)
# WE HAVE MOVED
Ultrasonic is free and open-source music streaming Android client for [Subsonic](http://www.subsonic.org/) [API](http://www.subsonic.org/pages/api.jsp) (version 1.7.0 or higher) compatible servers.
Ultrasonic code is now hosted in [GitLab][ultrasonic].
- New Web: https://ultrasonic.gitlab.io
- New Git: https://gitlab.com/ultrasonic/ultrasonic
- New bugtracker: https://gitlab.com/ultrasonic/ultrasonic/-/issues
- New releases: https://gitlab.com/ultrasonic/ultrasonic/-/packages
[ultrasonic]: https://gitlab.com/ultrasonic/ultrasonic
# Ultrasonic
Ultrasonic is free and open-source music streaming Android client for
[Subsonic][subsonic] [API][subapi] (version 1.7.0 or higher) compatible
servers.
## Help wanted
We currently don't have that much time to spend developing Subsonic, so any
contributions or active developers are always welcomed.
### BIG FAT WARNING
At this moment all new features and enhancements are blocked by [milestone
5][m5], this means that no new pull request will be accepted unless it is
related to this milestone. Obviously you can contribute closing any issue
contained in it.
[m5]: https://github.com/ultrasonic/ultrasonic/milestone/5
Have a look at [CONTRIBUTING](CONTRIBUTING.md) to get started.
## Download
@ -25,22 +27,26 @@ App is available to download at following stores:
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="70">](https://play.google.com/store/apps/details?id=org.moire.ultrasonic)
[<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="70">](https://f-droid.org/packages/org.moire.ultrasonic/)
[<img src="https://ultrasonic.github.io/assets/img/get-it-on-github.png" alt="Get it on GitHub" height="70">](https://github.com/ultrasonic/ultrasonic/releases)
[<img src="https://ultrasonic.gitlab.io/assets/img/get-it-on-gitlab.png" alt="Get it on GitLab" height="70">](https://gitlab.com/ultrasonic/ultrasonic/-/releases)
**Warning**: All three versions (Google Play, F-Droid and the APKs) are not
compatible (not signed by the same key)! You must uninstall one to install
the other, which will delete all your data.
If you want to use the version downloaded from F-Droid or from GitLab with
**Android Auto**, you must enable Unknown Sources as it is described in
[this wiki page][wikiaa].
## Bugs and issues
First, see if your issue havent been yet reported [here](https://github.com/ultrasonic/ultrasonic/issues),
otherwise open [a new issue](https://github.com/ultrasonic/ultrasonic/issues/new).
First, see if your issue havent been yet reported [here][issues], otherwise
open [a new issue][newissue].
### Known (not our) bugs
If you are using *Madsonic 5.1.X* several sections of Ultrasonic will not
work. This is caused by bad implementation of Subsonic API by Madsonic. For
more info about this you can read [this bug](https://github.com/ultrasonic/ultrasonic/issues/129).
more info about this you can read [this bug][madbug].
## Contributing
@ -48,15 +54,29 @@ See [CONTRIBUTING](CONTRIBUTING.md).
## Supported (tested) Subsonic API implementations
- [Subsonic](http://www.subsonic.org/pages/index.jsp)
- [Airsonic](https://github.com/airsonic/airsonic)
- [Supysonic](https://github.com/spl0k/supysonic)
- [Subsonic][subsonic]
- [Airsonic-Advanced][airsonic]
- [Supysonic][supysonic]
- [Ampache][ampache]
Other *Subsonic API* implementations should work as well as long as they follow API
[documentation](http://www.subsonic.org/pages/api.jsp).
Other *Subsonic API* implementations should work as well as long as they
follow API [documentation][subapi].
## License
This software is licensed under the terms of the GNU General Public License version 3 (GPLv3).
This software is licensed under the terms of the GNU General Public License
version 3 (GPLv3).
Full text of the license is available in the [LICENSE](LICENSE) file and [online](https://opensource.org/licenses/gpl-3.0.html).
Full text of the license is available in the [LICENSE](LICENSE) file and
[online][gpl3].
[wikiaa]: https://gitlab.com/ultrasonic/ultrasonic/-/wikis/Using-Ultrasonic-with-Android-Auto
[issues]: https://gitlab.com/ultrasonic/ultrasonic/-/issues
[newissue]: https://gitlab.com/ultrasonic/ultrasonic/-/issues/new
[madbug]: https://gitlab.com/ultrasonic/ultrasonic/-/issues/129
[subsonic]: http://www.subsonic.org/
[subapi]: http://www.subsonic.org/pages/api.jsp
[airsonic]: https://github.com/airsonic-advanced/airsonic-advanced
[supysonic]: https://github.com/spl0k/supysonic
[ampache]: https://ampache.org/
[gpl3]: https://opensource.org/licenses/gpl-3.0.html

View File

@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
apply from: 'dependencies.gradle'
apply from: 'gradle/versions.gradle'
ext.bootstrap = [
kotlinModule : "${project.rootDir}/gradle_scripts/kotlin-module-bootstrap.gradle",
@ -8,16 +8,15 @@ buildscript {
]
repositories {
jcenter()
google()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath gradlePlugins.androidTools
classpath gradlePlugins.kotlin
classpath gradlePlugins.ktlintGradle
classpath gradlePlugins.detekt
classpath gradlePlugins.jacoco
classpath libs.gradle
classpath libs.kotlin
classpath libs.ktlintGradle
classpath libs.detekt
}
}
@ -25,20 +24,26 @@ allprojects {
// Buildscript here is required by detekt
buildscript {
repositories {
jcenter()
mavenCentral()
google()
}
}
repositories {
jcenter()
mavenCentral()
google()
maven { url 'https://jitpack.io' }
}
// Set Kotlin JVM target to the same for all subprojects
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
apply from: 'gradle_scripts/jacoco.gradle'
wrapper {
gradleVersion(versions.gradle)
gradleVersion(libs.versions.gradle.get())
distributionType("all")
}

View File

@ -1,12 +0,0 @@
apply from: bootstrap.kotlinModule
dependencies {
api project(':core:domain')
api other.twitterSerial
testImplementation testing.kotlinJunit
testImplementation testing.mockito
testImplementation testing.mockitoInline
testImplementation testing.mockitoKotlin
testImplementation testing.kluent
}

View File

@ -1,14 +0,0 @@
package org.moire.ultrasonic.cache
import java.io.File
/**
* Provides access to generic directories:
* - for temporary caches
* - for permanent data storage
*/
interface Directories {
fun getInternalCacheDir(): File
fun getInternalDataDir(): File
fun getExternalCacheDir(): File?
}

View File

@ -1,75 +0,0 @@
package org.moire.ultrasonic.cache
import com.twitter.serial.serializer.SerializationContext
import com.twitter.serial.serializer.Serializer
import com.twitter.serial.stream.Serial
import com.twitter.serial.stream.bytebuffer.ByteBufferSerial
import java.io.File
typealias DomainEntitySerializer<T> = Serializer<T>
internal const val STORAGE_DIR_NAME = "persistent_storage"
/**
* Provides access to permanent file based storage.
*
* [serverId] is currently active server. Should be unique per server so stored data will not
* interfere with other server data.
*
* Look at [org.moire.ultrasonic.cache.serializers] package for available [DomainEntitySerializer]s.
*/
class PermanentFileStorage(
private val directories: Directories,
private val serverId: String,
private val debug: Boolean = false
) {
private val serializationContext = object : SerializationContext {
override fun isDebug(): Boolean = debug
override fun isRelease(): Boolean = !debug
}
private val serializer: Serial = ByteBufferSerial(serializationContext)
/**
* Stores given [objectToStore] using [name] as a key and [objectSerializer] as serializer.
*/
fun <T> store(
name: String,
objectToStore: T,
objectSerializer: DomainEntitySerializer<T>
) {
val storeFile = getFile(name)
if (!storeFile.exists()) storeFile.createNewFile()
storeFile.writeBytes(serializer.toByteArray(objectToStore, objectSerializer))
}
/**
* Loads object with [name] key using [objectDeserializer] deserializer.
*/
fun <T> load(
name: String,
objectDeserializer: DomainEntitySerializer<T>
): T? {
val storeFile = getFile(name)
if (!storeFile.exists()) return null
return serializer.fromByteArray(storeFile.readBytes(), objectDeserializer)
}
/**
* Clear all files in storage.
*/
fun clearAll() {
val storageDir = getStorageDir()
storageDir.listFiles().forEach { it.deleteRecursively() }
}
private fun getFile(name: String) = File(getStorageDir(), "$name.ser")
private fun getStorageDir(): File {
val mainDir = File(directories.getInternalDataDir(), STORAGE_DIR_NAME)
val serverDir = File(mainDir, serverId)
if (!serverDir.exists()) serverDir.mkdirs()
return serverDir
}
}

View File

@ -1,65 +0,0 @@
@file:JvmMultifileClass
@file:JvmName("DomainSerializers")
package org.moire.ultrasonic.cache.serializers
import com.twitter.serial.serializer.CollectionSerializers
import com.twitter.serial.serializer.ObjectSerializer
import com.twitter.serial.serializer.SerializationContext
import com.twitter.serial.stream.SerializerDefs
import com.twitter.serial.stream.SerializerInput
import com.twitter.serial.stream.SerializerOutput
import org.moire.ultrasonic.cache.DomainEntitySerializer
import org.moire.ultrasonic.domain.Artist
private const val SERIALIZER_VERSION = 1
private val artistSerializer get() = object : ObjectSerializer<Artist>(SERIALIZER_VERSION) {
override fun serializeObject(
context: SerializationContext,
output: SerializerOutput<out SerializerOutput<*>>,
item: Artist
) {
output.writeString(item.id)
.writeString(item.name)
.writeString(item.index)
.writeString(item.coverArt)
.apply {
val albumCount = item.albumCount
if (albumCount != null) writeLong(albumCount) else writeNull()
}
.writeInt(item.closeness)
}
override fun deserializeObject(
context: SerializationContext,
input: SerializerInput,
versionNumber: Int
): Artist? {
if (versionNumber != SERIALIZER_VERSION) return null
val id = input.readString()
val name = input.readString()
val index = input.readString()
val coverArt = input.readString()
val albumCount = if (input.peekType() == SerializerDefs.TYPE_NULL) {
input.readNull()
null
} else {
input.readLong()
}
val closeness = input.readInt()
return Artist(id, name, index, coverArt, albumCount, closeness)
}
}
/**
* Serializer/deserializer for [Artist] domain entity.
*/
fun getArtistsSerializer(): DomainEntitySerializer<Artist> = artistSerializer
private val artistListSerializer = CollectionSerializers.getListSerializer(artistSerializer)
/**
* Serializer/deserializer for list of [Artist] domain entities.
*/
fun getArtistListSerializer(): DomainEntitySerializer<List<Artist>> = artistListSerializer

View File

@ -1,50 +0,0 @@
@file:JvmMultifileClass
@file:JvmName("DomainSerializers")
package org.moire.ultrasonic.cache.serializers
import com.twitter.serial.serializer.ObjectSerializer
import com.twitter.serial.serializer.SerializationContext
import com.twitter.serial.stream.SerializerInput
import com.twitter.serial.stream.SerializerOutput
import org.moire.ultrasonic.cache.DomainEntitySerializer
import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.Indexes
private const val SERIALIZATION_VERSION = 1
private val indexesSerializer get() = object : ObjectSerializer<Indexes>(SERIALIZATION_VERSION) {
override fun serializeObject(
context: SerializationContext,
output: SerializerOutput<out SerializerOutput<*>>,
item: Indexes
) {
val artistListSerializer = getArtistListSerializer()
output.writeLong(item.lastModified)
.writeString(item.ignoredArticles)
.writeObject<MutableList<Artist>>(context, item.shortcuts, artistListSerializer)
.writeObject<MutableList<Artist>>(context, item.artists, artistListSerializer)
}
override fun deserializeObject(
context: SerializationContext,
input: SerializerInput,
versionNumber: Int
): Indexes? {
if (versionNumber != SERIALIZATION_VERSION) return null
val artistListDeserializer = getArtistListSerializer()
val lastModified = input.readLong()
val ignoredArticles = input.readString() ?: return null
val shortcutsList = input.readObject(context, artistListDeserializer) ?: return null
val artistsList = input.readObject(context, artistListDeserializer) ?: return null
return Indexes(
lastModified, ignoredArticles, shortcutsList.toMutableList(),
artistsList.toMutableList()
)
}
}
/**
* Get serializer/deserializer for [Indexes] entity.
*/
fun getIndexesSerializer(): DomainEntitySerializer<Indexes> = indexesSerializer

View File

@ -1,50 +0,0 @@
@file:JvmMultifileClass
@file:JvmName("DomainSerializers")
package org.moire.ultrasonic.cache.serializers
import com.twitter.serial.serializer.CollectionSerializers
import com.twitter.serial.serializer.ObjectSerializer
import com.twitter.serial.serializer.SerializationContext
import com.twitter.serial.stream.SerializerInput
import com.twitter.serial.stream.SerializerOutput
import org.moire.ultrasonic.cache.DomainEntitySerializer
import org.moire.ultrasonic.domain.MusicFolder
private const val SERIALIZATION_VERSION = 1
private val musicFolderSerializer = object : ObjectSerializer<MusicFolder>(SERIALIZATION_VERSION) {
override fun serializeObject(
context: SerializationContext,
output: SerializerOutput<out SerializerOutput<*>>,
item: MusicFolder
) {
output.writeString(item.id).writeString(item.name)
}
override fun deserializeObject(
context: SerializationContext,
input: SerializerInput,
versionNumber: Int
): MusicFolder? {
if (versionNumber != SERIALIZATION_VERSION) return null
val id = input.readString() ?: return null
val name = input.readString() ?: return null
return MusicFolder(id, name)
}
}
/**
* Serializer/deserializer for [MusicFolder] domain entity.
*/
fun getMusicFolderSerializer(): DomainEntitySerializer<MusicFolder> = musicFolderSerializer
private val musicFolderListSerializer =
CollectionSerializers.getListSerializer(musicFolderSerializer)
/**
* Serializer/deserializer for [List] of [MusicFolder] items.
*/
fun getMusicFolderListSerializer(): DomainEntitySerializer<List<MusicFolder>> =
musicFolderListSerializer

View File

@ -1,42 +0,0 @@
package org.moire.ultrasonic.cache
import com.nhaarman.mockito_kotlin.mock
import com.twitter.serial.util.SerializationUtils
import java.io.File
import org.amshove.kluent.`it returns`
import org.junit.Before
import org.junit.Rule
import org.junit.rules.TemporaryFolder
internal const val INTERNAL_DATA_FOLDER = "data"
internal const val INTERNAL_CACHE_FOLDER = "cache"
internal const val EXTERNAL_CACHE_FOLDER = "external_cache"
/**
* Base test class that inits the storage
*/
abstract class BaseStorageTest {
@get:Rule val tempFileRule = TemporaryFolder()
protected lateinit var mockDirectories: Directories
protected lateinit var storage: PermanentFileStorage
open val serverId: String = ""
@Before
fun setUp() {
mockDirectories = mock<Directories> {
on { getInternalDataDir() } `it returns` tempFileRule.newFolder(INTERNAL_DATA_FOLDER)
on { getInternalCacheDir() } `it returns` tempFileRule.newFolder(INTERNAL_CACHE_FOLDER)
on { getExternalCacheDir() } `it returns` tempFileRule.newFolder(EXTERNAL_CACHE_FOLDER)
}
storage = PermanentFileStorage(mockDirectories, serverId, true)
}
protected val storageDir get() = File(mockDirectories.getInternalDataDir(), STORAGE_DIR_NAME)
protected fun validateSerializedData(index: Int = 0) {
val serializedFileBytes = storageDir.listFiles()[index].readBytes()
SerializationUtils.validateSerializedData(serializedFileBytes)
}
}

View File

@ -1,81 +0,0 @@
package org.moire.ultrasonic.cache
import java.io.File
import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.`should contain`
import org.amshove.kluent.`should equal`
import org.junit.Test
import org.moire.ultrasonic.cache.serializers.getMusicFolderSerializer
import org.moire.ultrasonic.domain.MusicFolder
/**
* Integration test for [PermanentFileStorage].
*/
class PermanentFileStorageTest : BaseStorageTest() {
override val serverId: String
get() = "some-server-id"
@Test
fun `Should create storage dir if it is not exist`() {
val item = MusicFolder("1", "2")
storage.store("test", item, getMusicFolderSerializer())
storageDir.exists() `should be equal to` true
getServerStorageDir().exists() `should be equal to` true
}
@Test
fun `Should serialize to file`() {
val item = MusicFolder("1", "23")
val name = "some-name"
storage.store(name, item, getMusicFolderSerializer())
val storageFiles = getServerStorageDir().listFiles()
storageFiles.size `should be equal to` 1
storageFiles[0].name `should contain` name
}
@Test
fun `Should deserialize stored object`() {
val item = MusicFolder("some", "nice")
val name = "some-name"
storage.store(name, item, getMusicFolderSerializer())
val loadedItem = storage.load(name, getMusicFolderSerializer())
loadedItem `should equal` item
}
@Test
fun `Should overwrite existing stored object`() {
val name = "some-nice-name"
val item1 = MusicFolder("1", "1")
val item2 = MusicFolder("2", "2")
storage.store(name, item1, getMusicFolderSerializer())
storage.store(name, item2, getMusicFolderSerializer())
val loadedItem = storage.load(name, getMusicFolderSerializer())
loadedItem `should equal` item2
}
@Test
fun `Should clear all files when clearAll is called`() {
storage.store("name1", MusicFolder("1", "1"), getMusicFolderSerializer())
storage.store("name2", MusicFolder("2", "2"), getMusicFolderSerializer())
storage.clearAll()
getServerStorageDir().listFiles().size `should be equal to` 0
}
@Test
fun `Should return null if serialized file not available`() {
val loadedItem = storage.load("some-name", getMusicFolderSerializer())
loadedItem `should equal` null
}
private fun getServerStorageDir() = File(storageDir, serverId)
}

View File

@ -1,57 +0,0 @@
package org.moire.ultrasonic.cache.serializers
import org.amshove.kluent.`should equal`
import org.junit.Test
import org.moire.ultrasonic.cache.BaseStorageTest
import org.moire.ultrasonic.domain.Artist
/**
* [Artist] serializers test.
*/
class ArtistSerializerTest : BaseStorageTest() {
@Test
fun `Should correctly serialize Artist object`() {
val item = Artist("id", "name", "index", "coverArt", 1, 0)
storage.store("some-name", item, getArtistsSerializer())
validateSerializedData()
}
@Test
fun `Should correctly deserialize Artist object`() {
val itemName = "some-name"
val item = Artist("id", "name", "index", "coverArt", null, 0)
storage.store(itemName, item, getArtistsSerializer())
val loadedItem = storage.load(itemName, getArtistsSerializer())
loadedItem `should equal` item
}
@Test
fun `Should correctly serialize list of Artists`() {
val itemsList = listOf(
Artist(id = "1"),
Artist(id = "2", name = "some")
)
storage.store("some-name", itemsList, getArtistListSerializer())
validateSerializedData()
}
@Test
fun `Should correctly deserialize list of Artists`() {
val name = "some-name"
val itemsList = listOf(
Artist(id = "1"),
Artist(id = "2", name = "some")
)
storage.store(name, itemsList, getArtistListSerializer())
val loadedItems = storage.load(name, getArtistListSerializer())
loadedItems `should equal` itemsList
}
}

View File

@ -1,38 +0,0 @@
package org.moire.ultrasonic.cache.serializers
import org.amshove.kluent.`should equal`
import org.junit.Test
import org.moire.ultrasonic.cache.BaseStorageTest
import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.Indexes
/**
* Test [Indexes] domain entity serializer.
*/
class IndexesSerializerTest : BaseStorageTest() {
@Test
fun `Should correctly serialize Indexes object`() {
val item = Indexes(
220L, "", mutableListOf(Artist("12")),
mutableListOf(Artist("233", "some"))
)
storage.store("some-name", item, getIndexesSerializer())
validateSerializedData()
}
@Test
fun `Should correctly deserialize Indexes object`() {
val name = "some-name"
val item = Indexes(
220L, "", mutableListOf(Artist("12")),
mutableListOf(Artist("233", "some"))
)
storage.store(name, item, getIndexesSerializer())
val loadedItem = storage.load(name, getIndexesSerializer())
loadedItem `should equal` item
}
}

View File

@ -1,57 +0,0 @@
package org.moire.ultrasonic.cache.serializers
import org.amshove.kluent.`should equal`
import org.junit.Test
import org.moire.ultrasonic.cache.BaseStorageTest
import org.moire.ultrasonic.domain.MusicFolder
/**
* [MusicFolder] serializers test.
*/
class MusicFolderSerializerTest : BaseStorageTest() {
@Test
fun `Should correctly serialize MusicFolder object`() {
val item = MusicFolder("Music", "Folder")
storage.store("some-name", item, getMusicFolderSerializer())
validateSerializedData()
}
@Test
fun `Should correctly deserialize MusicFolder object`() {
val name = "name"
val item = MusicFolder("some", "none")
storage.store(name, item, getMusicFolderSerializer())
val loadedItem = storage.load(name, getMusicFolderSerializer())
loadedItem `should equal` item
}
@Test
fun `Should correctly serialize list of MusicFolders objects`() {
val itemsList = listOf(
MusicFolder("1", "1"),
MusicFolder("2", "2")
)
storage.store("some-name", itemsList, getMusicFolderListSerializer())
validateSerializedData()
}
@Test
fun `Should correctly deserialize list of MusicFolder objects`() {
val name = "some-name"
val itemsList = listOf(
MusicFolder("1", "1"),
MusicFolder("2", "2")
)
storage.store(name, itemsList, getMusicFolderListSerializer())
val loadedItem = storage.load(name, getMusicFolderListSerializer())
loadedItem `should equal` itemsList
}
}

View File

@ -1,10 +1,8 @@
apply from: bootstrap.kotlinModule
apply from: bootstrap.androidModule
apply plugin: 'kotlin-kapt'
ext {
jacocoExclude = [
'**/domain/**'
]
}
dependencies {
api other.semver
implementation libs.roomRuntime
implementation libs.roomKtx
kapt libs.room
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.moire.ultrasonic.subsonic.domain">
</manifest>

View File

@ -0,0 +1,38 @@
/*
* Album.kt
* Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.domain
import androidx.room.ColumnInfo
import androidx.room.Entity
import java.util.Date
@Entity(tableName = "albums", primaryKeys = ["id", "serverId"])
data class Album(
override var id: String,
@ColumnInfo(defaultValue = "-1")
override var serverId: Int = -1,
override var parent: String? = null,
override var album: String? = null,
override var title: String? = null,
override val name: String? = null,
override var discNumber: Int? = 0,
override var coverArt: String? = null,
override var songCount: Long? = null,
override var created: Date? = null,
override var artist: String? = null,
override var artistId: String? = null,
override var duration: Int? = 0,
override var year: Int? = 0,
override var genre: String? = null,
override var starred: Boolean = false,
override var path: String? = null,
override var closeness: Int = 0,
) : MusicDirectory.Child() {
override var isDirectory = true
override var isVideo = false
}

View File

@ -1,16 +1,23 @@
/*
* Artist.kt
* Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.domain
import java.io.Serializable
import androidx.room.ColumnInfo
import androidx.room.Entity
@Entity(tableName = "artists", primaryKeys = ["id", "serverId"])
data class Artist(
var id: String? = null,
var name: String? = null,
var index: String? = null,
var coverArt: String? = null,
var albumCount: Long? = null,
var closeness: Int = 0
) : Serializable {
companion object {
private const val serialVersionUID = -5790532593784846982L
}
}
override var id: String,
@ColumnInfo(defaultValue = "-1")
override var serverId: Int = -1,
override var name: String? = null,
override var index: String? = null,
override var coverArt: String? = null,
override var albumCount: Long? = null,
override var closeness: Int = 0
) : ArtistOrIndex(id, serverId)

View File

@ -0,0 +1,45 @@
/*
* ArtistOrIndex.kt
* Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.domain
import androidx.room.Ignore
@Suppress("LongParameterList")
abstract class ArtistOrIndex(
@Ignore
override var id: String,
@Ignore
open var serverId: Int,
@Ignore
override var name: String? = null,
@Ignore
open var index: String? = null,
@Ignore
open var coverArt: String? = null,
@Ignore
open var albumCount: Long? = null,
@Ignore
open var closeness: Int = 0
) : GenericEntry() {
fun compareTo(other: ArtistOrIndex): Int {
return when {
this.closeness == other.closeness -> {
0
}
this.closeness > other.closeness -> {
-1
}
else -> {
1
}
}
}
override fun compareTo(other: Identifiable) = compareTo(other as ArtistOrIndex)
}

View File

@ -2,7 +2,6 @@ package org.moire.ultrasonic.domain
import java.io.Serializable
import java.util.Date
import org.moire.ultrasonic.domain.MusicDirectory.Entry
data class Bookmark(
val position: Int = 0,
@ -10,7 +9,7 @@ data class Bookmark(
val comment: String,
val created: Date? = null,
val changed: Date? = null,
val entry: Entry
val track: Track
) : Serializable {
companion object {
private const val serialVersionUID = 8988990025189807803L

View File

@ -1,10 +1,13 @@
package org.moire.ultrasonic.domain
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
@Entity
data class Genre(
val name: String,
val index: String
@PrimaryKey val index: String,
val name: String
) : Serializable {
companion object {
private const val serialVersionUID = -3943025175219134028L

View File

@ -0,0 +1,19 @@
package org.moire.ultrasonic.domain
import androidx.room.Ignore
abstract class GenericEntry : Identifiable {
@Ignore
open val name: String? = null
}
interface Identifiable : Comparable<Identifiable> {
val id: String
val longId: Long
get() = id.hashCode().toLong()
override fun compareTo(other: Identifiable): Int {
return longId.compareTo(other.longId)
}
}

View File

@ -0,0 +1,24 @@
/*
* Index.kt
* Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.domain
import androidx.room.ColumnInfo
import androidx.room.Entity
@Entity(tableName = "indexes", primaryKeys = ["id", "serverId"])
data class Index(
override var id: String,
@ColumnInfo(defaultValue = "-1")
override var serverId: Int = -1,
override var name: String? = null,
override var index: String? = null,
override var coverArt: String? = null,
override var albumCount: Long? = null,
override var closeness: Int = 0,
var musicFolderId: String? = null
) : ArtistOrIndex(id, serverId)

View File

@ -1,14 +0,0 @@
package org.moire.ultrasonic.domain
import java.io.Serializable
data class Indexes(
val lastModified: Long,
val ignoredArticles: String,
val shortcuts: MutableList<Artist> = mutableListOf(),
val artists: MutableList<Artist> = mutableListOf()
) : Serializable {
companion object {
private const val serialVersionUID = 8156117238598414701L
}
}

View File

@ -1,78 +1,61 @@
/*
* MusicDirectory.kt
* Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.domain
import java.io.Serializable
import java.util.Date
class MusicDirectory {
class MusicDirectory : ArrayList<MusicDirectory.Child>() {
var name: String? = null
private val children = mutableListOf<Entry>()
fun addAll(entries: Collection<Entry>) {
children.addAll(entries)
}
fun addFirst(child: Entry) {
children.add(0, child)
}
fun addChild(child: Entry) {
children.add(child)
}
fun findChild(id: String): Entry? = children.lastOrNull { it.id == id }
fun getAllChild(): List<Entry> = children.toList()
@JvmOverloads
fun getChildren(
includeDirs: Boolean = true,
includeFiles: Boolean = true
): List<Entry> {
): List<Child> {
if (includeDirs && includeFiles) {
return children
return toList()
}
return children.filter { it.isDirectory && includeDirs || !it.isDirectory && includeFiles }
return filter { it.isDirectory && includeDirs || !it.isDirectory && includeFiles }
}
data class Entry(
var id: String? = null,
var parent: String? = null,
var isDirectory: Boolean = false,
var title: String? = null,
var album: String? = null,
var albumId: String? = null,
var artist: String? = null,
var artistId: String? = null,
var track: Int? = 0,
var year: Int? = 0,
var genre: String? = null,
var contentType: String? = null,
var suffix: String? = null,
var transcodedContentType: String? = null,
var transcodedSuffix: String? = null,
var coverArt: String? = null,
var size: Long? = null,
var songCount: Long? = null,
var duration: Int? = null,
var bitRate: Int? = null,
var path: String? = null,
var isVideo: Boolean = false,
var starred: Boolean = false,
var discNumber: Int? = null,
var type: String? = null,
var created: Date? = null,
var closeness: Int = 0,
var bookmarkPosition: Int = 0,
var userRating: Int? = null,
var averageRating: Float? = null
) : Serializable {
fun setDuration(duration: Long) {
this.duration = duration.toInt()
fun getTracks(): List<Track> {
return mapNotNull {
it as? Track
}
}
companion object {
private const val serialVersionUID = -3339106650010798108L
fun getAlbums(): List<Album> {
return mapNotNull {
it as? Album
}
}
abstract class Child : GenericEntry() {
abstract override var id: String
abstract var serverId: Int
abstract var parent: String?
abstract var isDirectory: Boolean
abstract var album: String?
abstract var title: String?
abstract override val name: String?
abstract var discNumber: Int?
abstract var coverArt: String?
abstract var songCount: Long?
abstract var created: Date?
abstract var artist: String?
abstract var artistId: String?
abstract var duration: Int?
abstract var year: Int?
abstract var genre: String?
abstract var starred: Boolean
abstract var path: String?
abstract var closeness: Int
abstract var isVideo: Boolean
}
}

View File

@ -1,9 +1,22 @@
/*
* MusicFolder.kt
* Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.domain
import androidx.room.ColumnInfo
import androidx.room.Entity
/**
* Represents a top level directory in which music or other media is stored.
*/
@Entity(tableName = "music_folders", primaryKeys = ["id", "serverId"])
data class MusicFolder(
val id: String,
val name: String
)
override val id: String,
override val name: String,
@ColumnInfo(defaultValue = "-1")
var serverId: Int
) : GenericEntry()

View File

@ -1,12 +0,0 @@
package org.moire.ultrasonic.domain
enum class PlayerState {
IDLE,
DOWNLOADING,
PREPARING,
PREPARED,
STARTED,
STOPPED,
PAUSED,
COMPLETED
}

View File

@ -3,14 +3,14 @@ package org.moire.ultrasonic.domain
import java.io.Serializable
data class Playlist @JvmOverloads constructor(
val id: String,
var name: String,
override val id: String,
override var name: String,
val owner: String = "",
val comment: String = "",
val songCount: String = "",
val created: String = "",
val public: Boolean? = null
) : Serializable {
) : Serializable, GenericEntry() {
companion object {
private const val serialVersionUID = -4160515427075433798L
}

View File

@ -3,12 +3,12 @@ package org.moire.ultrasonic.domain
import java.io.Serializable
data class PodcastsChannel(
val id: String,
override val id: String,
val title: String?,
val url: String?,
val description: String?,
val status: String?
) : Serializable {
) : Serializable, GenericEntry() {
companion object {
private const val serialVersionUID = -4160515427075433798L
}

View File

@ -1,15 +0,0 @@
package org.moire.ultrasonic.domain
enum class RepeatMode {
OFF {
override operator fun next(): RepeatMode = ALL
},
ALL {
override operator fun next(): RepeatMode = SINGLE
},
SINGLE {
override operator fun next(): RepeatMode = OFF
};
abstract operator fun next(): RepeatMode
}

View File

@ -1,12 +1,10 @@
package org.moire.ultrasonic.domain
import org.moire.ultrasonic.domain.MusicDirectory.Entry
/**
* The result of a search. Contains matching artists, albums and songs.
*/
data class SearchResult(
val artists: List<Artist>,
val albums: List<Entry>,
val songs: List<Entry>
val artists: List<ArtistOrIndex> = listOf(),
val albums: List<Album> = listOf(),
val songs: List<Track> = listOf()
)

View File

@ -1,10 +1,9 @@
package org.moire.ultrasonic.domain
import java.io.Serializable
import org.moire.ultrasonic.domain.MusicDirectory.Entry
data class Share(
var id: String? = null,
override var id: String,
var url: String? = null,
var description: String? = null,
var username: String? = null,
@ -12,17 +11,22 @@ data class Share(
var lastVisited: String? = null,
var expires: String? = null,
var visitCount: Long? = null,
private val entries: MutableList<Entry> = mutableListOf()
) : Serializable {
val name: String?
get() = url?.let { urlPattern.matcher(url).replaceFirst("$1") }
private val tracks: MutableList<Track> = mutableListOf()
) : Serializable, GenericEntry() {
override val name: String?
get() {
if (url != null) {
return urlPattern.matcher(url!!).replaceFirst("$1")
}
return null
}
fun getEntries(): List<Entry> {
return entries.toList()
fun getEntries(): List<Track> {
return tracks.toList()
}
fun addEntry(entry: Entry) {
entries.add(entry)
fun addEntry(track: Track) {
tracks.add(track)
}
companion object {

View File

@ -0,0 +1,74 @@
/*
* Track.kt
* Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.domain
import androidx.room.ColumnInfo
import androidx.room.Entity
import java.io.Serializable
import java.util.Date
@Entity(tableName = "tracks", primaryKeys = ["id", "serverId"])
data class Track(
override var id: String,
@ColumnInfo(defaultValue = "-1")
override var serverId: Int = -1,
override var parent: String? = null,
override var isDirectory: Boolean = false,
override var title: String? = null,
override var album: String? = null,
var albumId: String? = null,
override var artist: String? = null,
override var artistId: String? = null,
var track: Int? = null,
override var year: Int? = null,
override var genre: String? = null,
var contentType: String? = null,
var suffix: String? = null,
var transcodedContentType: String? = null,
var transcodedSuffix: String? = null,
override var coverArt: String? = null,
var size: Long? = null,
override var songCount: Long? = null,
override var duration: Int? = null,
var bitRate: Int? = null,
override var path: String? = null,
override var isVideo: Boolean = false,
override var starred: Boolean = false,
override var discNumber: Int? = null,
var type: String? = null,
override var created: Date? = null,
override var closeness: Int = 0,
var bookmarkPosition: Int = 0,
var userRating: Int? = null,
var averageRating: Float? = null,
override var name: String? = null
) : Serializable, MusicDirectory.Child() {
fun setDuration(duration: Long) {
this.duration = duration.toInt()
}
companion object {
private const val serialVersionUID = -3339106650010798108L
}
fun compareTo(other: Track): Int {
when {
this.closeness == other.closeness -> {
return 0
}
this.closeness > other.closeness -> {
return -1
}
else -> {
return 1
}
}
}
override fun compareTo(other: Identifiable) = compareTo(other as Track)
}

View File

@ -1,27 +0,0 @@
package org.moire.ultrasonic.domain
import net.swiftzer.semver.SemVer
/**
* Represents the version number of the Subsonic Android app.
*/
data class Version(
val version: SemVer
) : Comparable<Version> {
override fun compareTo(other: Version): Int {
return version.compareTo(other.version)
}
companion object {
/**
* Creates a new version instance by parsing the given string.
*
* @param version A string of the format "1.27", "1.27.2" or "1.27.beta3".
*/
@JvmStatic
fun fromCharSequence(version: String): Version {
return Version(SemVer.parse(version))
}
}
}

View File

@ -1,13 +0,0 @@
apply from: bootstrap.androidModule
apply plugin: 'com.android.library'
android {
lintOptions {
baselineFile file("lint-baseline.xml")
abortOnError true
}
}
dependencies {
api androidSupport.support
}

View File

@ -1,59 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="4" by="lint 2.3.3">
<issue
id="NewApi"
message="Call requires API level 21 (current min is 14): android.widget.AbsListView#setSelectionFromTop"
errorLine1=" setSelectionFromTop(movePos, top - padTop);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/mobeta/android/dslv/DragSortListView.java"
line="2936"
column="13"/>
</issue>
<issue
id="OldTargetApi"
message="Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the `android.os.Build.VERSION_CODES` javadoc for details."
errorLine1=" &lt;uses-sdk android:targetSdkVersion=&quot;7&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="6"
column="15"/>
</issue>
<issue
id="GradleOverrides"
message="This `targetSdkVersion` value (`7`) is not used; it is always overridden by the value specified in the Gradle build script (`22`)"
errorLine1=" &lt;uses-sdk android:targetSdkVersion=&quot;7&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="6"
column="15"/>
</issue>
<issue
id="GradleOverrides"
message="This `minSdkVersion` value (`7`) is not used; it is always overridden by the value specified in the Gradle build script (`14`)"
errorLine1=" android:minSdkVersion=&quot;7&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="7"
column="7"/>
</issue>
<issue
id="ClickableViewAccessibility"
message="`com/mobeta/android/dslv/DragSortController#onTouch` should call `View#performClick` when a click is detected"
errorLine1=" public boolean onTouch(View v, MotionEvent ev) {"
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/mobeta/android/dslv/DragSortController.java"
line="238"
column="20"/>
</issue>
</issues>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mobeta.android.dslv"
android:versionCode="4"
android:versionName="0.6.1">
</manifest>

View File

@ -1,468 +0,0 @@
package com.mobeta.android.dslv;
import android.graphics.Point;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AdapterView;
/**
* Class that starts and stops item drags on a {@link DragSortListView}
* based on touch gestures. This class also inherits from
* {@link SimpleFloatViewManager}, which provides basic float View
* creation.
*
* An instance of this class is meant to be passed to the methods
* {@link DragSortListView#setTouchListener()} and
* {@link DragSortListView#setFloatViewManager()} of your
* {@link DragSortListView} instance.
*/
public class DragSortController extends SimpleFloatViewManager implements View.OnTouchListener, GestureDetector.OnGestureListener {
/**
* Drag init mode enum.
*/
public static final int ON_DOWN = 0;
public static final int ON_DRAG = 1;
public static final int ON_LONG_PRESS = 2;
private int mDragInitMode = ON_DOWN;
private boolean mSortEnabled = true;
/**
* Remove mode enum.
*/
public static final int CLICK_REMOVE = 0;
public static final int FLING_REMOVE = 1;
/**
* The current remove mode.
*/
private int mRemoveMode;
private boolean mRemoveEnabled = false;
private boolean mIsRemoving = false;
private GestureDetector mDetector;
private GestureDetector mFlingRemoveDetector;
private int mTouchSlop;
public static final int MISS = -1;
private int mHitPos = MISS;
private int mFlingHitPos = MISS;
private int mClickRemoveHitPos = MISS;
private int[] mTempLoc = new int[2];
private int mItemX;
private int mItemY;
private int mCurrX;
private int mCurrY;
private boolean mDragging = false;
private float mFlingSpeed = 500f;
private int mDragHandleId;
private int mClickRemoveId;
private int mFlingHandleId;
private boolean mCanDrag;
private DragSortListView mDslv;
private int mPositionX;
/**
* Calls {@link #DragSortController(DragSortListView, int)} with a
* 0 drag handle id, FLING_RIGHT_REMOVE remove mode,
* and ON_DOWN drag init. By default, sorting is enabled, and
* removal is disabled.
*
* @param dslv The DSLV instance
*/
public DragSortController(DragSortListView dslv) {
this(dslv, 0, ON_DOWN, FLING_REMOVE);
}
public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode) {
this(dslv, dragHandleId, dragInitMode, removeMode, 0);
}
public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode, int clickRemoveId) {
this(dslv, dragHandleId, dragInitMode, removeMode, clickRemoveId, 0);
}
/**
* By default, sorting is enabled, and removal is disabled.
*
* @param dslv The DSLV instance
* @param dragHandleId The resource id of the View that represents
* the drag handle in a list item.
*/
public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode,
int removeMode, int clickRemoveId, int flingHandleId) {
super(dslv);
mDslv = dslv;
mDetector = new GestureDetector(dslv.getContext(), this);
mFlingRemoveDetector = new GestureDetector(dslv.getContext(), mFlingRemoveListener);
mFlingRemoveDetector.setIsLongpressEnabled(false);
mTouchSlop = ViewConfiguration.get(dslv.getContext()).getScaledTouchSlop();
mDragHandleId = dragHandleId;
mClickRemoveId = clickRemoveId;
mFlingHandleId = flingHandleId;
setRemoveMode(removeMode);
setDragInitMode(dragInitMode);
}
public int getDragInitMode() {
return mDragInitMode;
}
/**
* Set how a drag is initiated. Needs to be one of
* {@link ON_DOWN}, {@link ON_DRAG}, or {@link ON_LONG_PRESS}.
*
* @param mode The drag init mode.
*/
public void setDragInitMode(int mode) {
mDragInitMode = mode;
}
/**
* Enable/Disable list item sorting. Disabling is useful if only item
* removal is desired. Prevents drags in the vertical direction.
*
* @param enabled Set <code>true</code> to enable list
* item sorting.
*/
public void setSortEnabled(boolean enabled) {
mSortEnabled = enabled;
}
public boolean isSortEnabled() {
return mSortEnabled;
}
/**
* One of {@link CLICK_REMOVE}, {@link FLING_RIGHT_REMOVE},
* {@link FLING_LEFT_REMOVE},
* {@link SLIDE_RIGHT_REMOVE}, or {@link SLIDE_LEFT_REMOVE}.
*/
public void setRemoveMode(int mode) {
mRemoveMode = mode;
}
public int getRemoveMode() {
return mRemoveMode;
}
/**
* Enable/Disable item removal without affecting remove mode.
*/
public void setRemoveEnabled(boolean enabled) {
mRemoveEnabled = enabled;
}
public boolean isRemoveEnabled() {
return mRemoveEnabled;
}
/**
* Set the resource id for the View that represents the drag
* handle in a list item.
*
* @param id An android resource id.
*/
public void setDragHandleId(int id) {
mDragHandleId = id;
}
/**
* Set the resource id for the View that represents the fling
* handle in a list item.
*
* @param id An android resource id.
*/
public void setFlingHandleId(int id) {
mFlingHandleId = id;
}
/**
* Set the resource id for the View that represents click
* removal button.
*
* @param id An android resource id.
*/
public void setClickRemoveId(int id) {
mClickRemoveId = id;
}
/**
* Sets flags to restrict certain motions of the floating View
* based on DragSortController settings (such as remove mode).
* Starts the drag on the DragSortListView.
*
* @param position The list item position (includes headers).
* @param deltaX Touch x-coord minus left edge of floating View.
* @param deltaY Touch y-coord minus top edge of floating View.
*
* @return True if drag started, false otherwise.
*/
public boolean startDrag(int position, int deltaX, int deltaY) {
int dragFlags = 0;
if (mSortEnabled && !mIsRemoving) {
dragFlags |= DragSortListView.DRAG_POS_Y | DragSortListView.DRAG_NEG_Y;
}
if (mRemoveEnabled && mIsRemoving) {
dragFlags |= DragSortListView.DRAG_POS_X;
dragFlags |= DragSortListView.DRAG_NEG_X;
}
mDragging = mDslv.startDrag(position - mDslv.getHeaderViewsCount(), dragFlags, deltaX,
deltaY);
return mDragging;
}
@Override
public boolean onTouch(View v, MotionEvent ev) {
if (!mDslv.isDragEnabled() || mDslv.listViewIntercepted()) {
return false;
}
mDetector.onTouchEvent(ev);
if (mRemoveEnabled && mDragging && mRemoveMode == FLING_REMOVE) {
mFlingRemoveDetector.onTouchEvent(ev);
}
int action = ev.getAction() & MotionEvent.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_DOWN:
mCurrX = (int) ev.getX();
mCurrY = (int) ev.getY();
break;
case MotionEvent.ACTION_UP:
if (mRemoveEnabled && mIsRemoving) {
int x = mPositionX >= 0 ? mPositionX : -mPositionX;
int removePoint = mDslv.getWidth() / 2;
if (x > removePoint) {
mDslv.stopDragWithVelocity(true, 0);
}
}
case MotionEvent.ACTION_CANCEL:
mIsRemoving = false;
mDragging = false;
break;
}
return false;
}
/**
* Overrides to provide fading when slide removal is enabled.
*/
@Override
public void onDragFloatView(View floatView, Point position, Point touch) {
if (mRemoveEnabled && mIsRemoving) {
mPositionX = position.x;
}
}
/**
* Get the position to start dragging based on the ACTION_DOWN
* MotionEvent. This function simply calls
* {@link #dragHandleHitPosition(MotionEvent)}. Override
* to change drag handle behavior;
* this function is called internally when an ACTION_DOWN
* event is detected.
*
* @param ev The ACTION_DOWN MotionEvent.
*
* @return The list position to drag if a drag-init gesture is
* detected; MISS if unsuccessful.
*/
public int startDragPosition(MotionEvent ev) {
return dragHandleHitPosition(ev);
}
public int startFlingPosition(MotionEvent ev) {
return mRemoveMode == FLING_REMOVE ? flingHandleHitPosition(ev) : MISS;
}
/**
* Checks for the touch of an item's drag handle (specified by
* {@link #setDragHandleId(int)}), and returns that item's position
* if a drag handle touch was detected.
*
* @param ev The ACTION_DOWN MotionEvent.
* @return The list position of the item whose drag handle was
* touched; MISS if unsuccessful.
*/
public int dragHandleHitPosition(MotionEvent ev) {
return viewIdHitPosition(ev, mDragHandleId);
}
public int flingHandleHitPosition(MotionEvent ev) {
return viewIdHitPosition(ev, mFlingHandleId);
}
public int viewIdHitPosition(MotionEvent ev, int id) {
final int x = (int) ev.getX();
final int y = (int) ev.getY();
int touchPos = mDslv.pointToPosition(x, y); // includes headers/footers
final int numHeaders = mDslv.getHeaderViewsCount();
final int numFooters = mDslv.getFooterViewsCount();
final int count = mDslv.getCount();
// Log.d("mobeta", "touch down on position " + itemnum);
// We're only interested if the touch was on an
// item that's not a header or footer.
if (touchPos != AdapterView.INVALID_POSITION && touchPos >= numHeaders
&& touchPos < (count - numFooters)) {
final View item = mDslv.getChildAt(touchPos - mDslv.getFirstVisiblePosition());
final int rawX = (int) ev.getRawX();
final int rawY = (int) ev.getRawY();
View dragBox = id == 0 ? item : (View) item.findViewById(id);
if (dragBox != null) {
dragBox.getLocationOnScreen(mTempLoc);
if (rawX > mTempLoc[0] && rawY > mTempLoc[1] &&
rawX < mTempLoc[0] + dragBox.getWidth() &&
rawY < mTempLoc[1] + dragBox.getHeight()) {
mItemX = item.getLeft();
mItemY = item.getTop();
return touchPos;
}
}
}
return MISS;
}
@Override
public boolean onDown(MotionEvent ev) {
if (mRemoveEnabled && mRemoveMode == CLICK_REMOVE) {
mClickRemoveHitPos = viewIdHitPosition(ev, mClickRemoveId);
}
mHitPos = startDragPosition(ev);
if (mHitPos != MISS && mDragInitMode == ON_DOWN) {
startDrag(mHitPos, (int) ev.getX() - mItemX, (int) ev.getY() - mItemY);
}
mIsRemoving = false;
mCanDrag = true;
mPositionX = 0;
mFlingHitPos = startFlingPosition(ev);
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
final int x1 = (int) e1.getX();
final int y1 = (int) e1.getY();
final int x2 = (int) e2.getX();
final int y2 = (int) e2.getY();
final int deltaX = x2 - mItemX;
final int deltaY = y2 - mItemY;
if (mCanDrag && !mDragging && (mHitPos != MISS || mFlingHitPos != MISS)) {
if (mHitPos != MISS) {
if (mDragInitMode == ON_DRAG && Math.abs(y2 - y1) > mTouchSlop && mSortEnabled) {
startDrag(mHitPos, deltaX, deltaY);
}
else if (mDragInitMode != ON_DOWN && Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled)
{
mIsRemoving = true;
startDrag(mFlingHitPos, deltaX, deltaY);
}
} else if (mFlingHitPos != MISS) {
if (Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled) {
mIsRemoving = true;
startDrag(mFlingHitPos, deltaX, deltaY);
} else if (Math.abs(y2 - y1) > mTouchSlop) {
mCanDrag = false; // if started to scroll the list then
// don't allow sorting nor fling-removing
}
}
}
// return whatever
return false;
}
@Override
public void onLongPress(MotionEvent e) {
// Log.d("mobeta", "lift listener long pressed");
if (mHitPos != MISS && mDragInitMode == ON_LONG_PRESS) {
mDslv.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
startDrag(mHitPos, mCurrX - mItemX, mCurrY - mItemY);
}
}
// complete the OnGestureListener interface
@Override
public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
// complete the OnGestureListener interface
@Override
public boolean onSingleTapUp(MotionEvent ev) {
if (mRemoveEnabled && mRemoveMode == CLICK_REMOVE) {
if (mClickRemoveHitPos != MISS) {
mDslv.removeItem(mClickRemoveHitPos - mDslv.getHeaderViewsCount());
}
}
return true;
}
// complete the OnGestureListener interface
@Override
public void onShowPress(MotionEvent ev) {
// do nothing
}
private GestureDetector.OnGestureListener mFlingRemoveListener =
new GestureDetector.SimpleOnGestureListener() {
@Override
public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// Log.d("mobeta", "on fling remove called");
if (mRemoveEnabled && mIsRemoving) {
int w = mDslv.getWidth();
int minPos = w / 5;
if (velocityX > mFlingSpeed) {
if (mPositionX > -minPos) {
mDslv.stopDragWithVelocity(true, velocityX);
}
} else if (velocityX < -mFlingSpeed) {
if (mPositionX < minPos) {
mDslv.stopDragWithVelocity(true, velocityX);
}
}
mIsRemoving = false;
}
return false;
}
};
}

View File

@ -1,241 +0,0 @@
package com.mobeta.android.dslv;
import java.util.ArrayList;
import android.content.Context;
import android.database.Cursor;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
import androidx.cursoradapter.widget.CursorAdapter;
/**
* A subclass of {@link android.widget.CursorAdapter} that provides
* reordering of the elements in the Cursor based on completed
* drag-sort operations. The reordering is a simple mapping of
* list positions into Cursor positions (the Cursor is unchanged).
* To persist changes made by drag-sorts, one can retrieve the
* mapping with the {@link #getCursorPositions()} method, which
* returns the reordered list of Cursor positions.
*
* An instance of this class is passed
* to {@link DragSortListView#setAdapter(ListAdapter)} and, since
* this class implements the {@link DragSortListView.DragSortListener}
* interface, it is automatically set as the DragSortListener for
* the DragSortListView instance.
*/
public abstract class DragSortCursorAdapter extends CursorAdapter implements DragSortListView.DragSortListener {
public static final int REMOVED = -1;
/**
* Key is ListView position, value is Cursor position
*/
private SparseIntArray mListMapping = new SparseIntArray();
private ArrayList<Integer> mRemovedCursorPositions = new ArrayList<Integer>();
public DragSortCursorAdapter(Context context, Cursor c) {
super(context, c);
}
public DragSortCursorAdapter(Context context, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);
}
public DragSortCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
}
/**
* Swaps Cursor and clears list-Cursor mapping.
*
* @see android.widget.CursorAdapter#swapCursor(android.database.Cursor)
*/
@Override
public Cursor swapCursor(Cursor newCursor) {
Cursor old = super.swapCursor(newCursor);
resetMappings();
return old;
}
/**
* Changes Cursor and clears list-Cursor mapping.
*
* @see android.widget.CursorAdapter#changeCursor(android.database.Cursor)
*/
@Override
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
resetMappings();
}
/**
* Resets list-cursor mapping.
*/
public void reset() {
resetMappings();
notifyDataSetChanged();
}
private void resetMappings() {
mListMapping.clear();
mRemovedCursorPositions.clear();
}
@Override
public Object getItem(int position) {
return super.getItem(mListMapping.get(position, position));
}
@Override
public long getItemId(int position) {
return super.getItemId(mListMapping.get(position, position));
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return super.getDropDownView(mListMapping.get(position, position), convertView, parent);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return super.getView(mListMapping.get(position, position), convertView, parent);
}
/**
* On drop, this updates the mapping between Cursor positions
* and ListView positions. The Cursor is unchanged. Retrieve
* the current mapping with {@link getCursorPositions()}.
*
* @see DragSortListView.DropListener#drop(int, int)
*/
@Override
public void drop(int from, int to) {
if (from != to) {
int cursorFrom = mListMapping.get(from, from);
if (from > to) {
for (int i = from; i > to; --i) {
mListMapping.put(i, mListMapping.get(i - 1, i - 1));
}
} else {
for (int i = from; i < to; ++i) {
mListMapping.put(i, mListMapping.get(i + 1, i + 1));
}
}
mListMapping.put(to, cursorFrom);
cleanMapping();
notifyDataSetChanged();
}
}
/**
* On remove, this updates the mapping between Cursor positions
* and ListView positions. The Cursor is unchanged. Retrieve
* the current mapping with {@link getCursorPositions()}.
*
* @see DragSortListView.RemoveListener#remove(int)
*/
@Override
public void remove(int which) {
int cursorPos = mListMapping.get(which, which);
if (!mRemovedCursorPositions.contains(cursorPos)) {
mRemovedCursorPositions.add(cursorPos);
}
int newCount = getCount();
for (int i = which; i < newCount; ++i) {
mListMapping.put(i, mListMapping.get(i + 1, i + 1));
}
mListMapping.delete(newCount);
cleanMapping();
notifyDataSetChanged();
}
/**
* Does nothing. Just completes DragSortListener interface.
*/
@Override
public void drag(int from, int to) {
// do nothing
}
/**
* Remove unnecessary mappings from sparse array.
*/
private void cleanMapping() {
ArrayList<Integer> toRemove = new ArrayList<Integer>();
int size = mListMapping.size();
for (int i = 0; i < size; ++i) {
if (mListMapping.keyAt(i) == mListMapping.valueAt(i)) {
toRemove.add(mListMapping.keyAt(i));
}
}
size = toRemove.size();
for (int i = 0; i < size; ++i) {
mListMapping.delete(toRemove.get(i));
}
}
@Override
public int getCount() {
return super.getCount() - mRemovedCursorPositions.size();
}
/**
* Get the Cursor position mapped to by the provided list position
* (given all previously handled drag-sort
* operations).
*
* @param position List position
*
* @return The mapped-to Cursor position
*/
public int getCursorPosition(int position) {
return mListMapping.get(position, position);
}
/**
* Get the current order of Cursor positions presented by the
* list.
*/
public ArrayList<Integer> getCursorPositions() {
ArrayList<Integer> result = new ArrayList<Integer>();
for (int i = 0; i < getCount(); ++i) {
result.add(mListMapping.get(i, i));
}
return result;
}
/**
* Get the list position mapped to by the provided Cursor position.
* If the provided Cursor position has been removed by a drag-sort,
* this returns {@link #REMOVED}.
*
* @param cursorPosition A Cursor position
* @return The mapped-to list position or REMOVED
*/
public int getListPosition(int cursorPosition) {
if (mRemovedCursorPositions.contains(cursorPosition)) {
return REMOVED;
}
int index = mListMapping.indexOfValue(cursorPosition);
if (index < 0) {
return cursorPosition;
} else {
return mListMapping.keyAt(index);
}
}
}

View File

@ -1,100 +0,0 @@
package com.mobeta.android.dslv;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.util.Log;
/**
* Lightweight ViewGroup that wraps list items obtained from user's
* ListAdapter. ItemView expects a single child that has a definite
* height (i.e. the child's layout height is not MATCH_PARENT).
* The width of
* ItemView will always match the width of its child (that is,
* the width MeasureSpec given to ItemView is passed directly
* to the child, and the ItemView measured width is set to the
* child's measured width). The height of ItemView can be anything;
* the
*
*
* The purpose of this class is to optimize slide
* shuffle animations.
*/
public class DragSortItemView extends ViewGroup {
private int mGravity = Gravity.TOP;
public DragSortItemView(Context context) {
super(context);
// always init with standard ListView layout params
setLayoutParams(new AbsListView.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
//setClipChildren(true);
}
public void setGravity(int gravity) {
mGravity = gravity;
}
public int getGravity() {
return mGravity;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final View child = getChildAt(0);
if (child == null) {
return;
}
if (mGravity == Gravity.TOP) {
child.layout(0, 0, getMeasuredWidth(), child.getMeasuredHeight());
} else {
child.layout(0, getMeasuredHeight() - child.getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight());
}
}
/**
*
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
final View child = getChildAt(0);
if (child == null) {
setMeasuredDimension(0, width);
return;
}
if (child.isLayoutRequested()) {
// Always let child be as tall as it wants.
measureChild(child, widthMeasureSpec,
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
}
if (heightMode == MeasureSpec.UNSPECIFIED) {
ViewGroup.LayoutParams lp = getLayoutParams();
if (lp.height > 0) {
height = lp.height;
} else {
height = child.getMeasuredHeight();
}
}
setMeasuredDimension(width, height);
}
}

View File

@ -1,55 +0,0 @@
package com.mobeta.android.dslv;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.Checkable;
import android.util.Log;
/**
* Lightweight ViewGroup that wraps list items obtained from user's
* ListAdapter. ItemView expects a single child that has a definite
* height (i.e. the child's layout height is not MATCH_PARENT).
* The width of
* ItemView will always match the width of its child (that is,
* the width MeasureSpec given to ItemView is passed directly
* to the child, and the ItemView measured width is set to the
* child's measured width). The height of ItemView can be anything;
* the
*
*
* The purpose of this class is to optimize slide
* shuffle animations.
*/
public class DragSortItemViewCheckable extends DragSortItemView implements Checkable {
public DragSortItemViewCheckable(Context context) {
super(context);
}
@Override
public boolean isChecked() {
View child = getChildAt(0);
if (child instanceof Checkable)
return ((Checkable) child).isChecked();
else
return false;
}
@Override
public void setChecked(boolean checked) {
View child = getChildAt(0);
if (child instanceof Checkable)
((Checkable) child).setChecked(checked);
}
@Override
public void toggle() {
View child = getChildAt(0);
if (child instanceof Checkable)
((Checkable) child).toggle();
}
}

View File

@ -1,133 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mobeta.android.dslv;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater;
// taken from v4 rev. 10 ResourceCursorAdapter.java
/**
* Static library support version of the framework's {@link android.widget.ResourceCursorAdapter}.
* Used to write apps that run on platforms prior to Android 3.0. When running
* on Android 3.0 or above, this implementation is still used; it does not try
* to switch to the framework's implementation. See the framework SDK
* documentation for a class overview.
*/
public abstract class ResourceDragSortCursorAdapter extends DragSortCursorAdapter {
private int mLayout;
private int mDropDownLayout;
private LayoutInflater mInflater;
/**
* Constructor the enables auto-requery.
*
* @deprecated This option is discouraged, as it results in Cursor queries
* being performed on the application's UI thread and thus can cause poor
* responsiveness or even Application Not Responding errors. As an alternative,
* use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
*
* @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
*/
@Deprecated
public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c) {
super(context, c);
mLayout = mDropDownLayout = layout;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/**
* Constructor with default behavior as per
* {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is recommended
* you not use this, but instead {@link #ResourceCursorAdapter(Context, int, Cursor, int)}.
* When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
* will always be set.
*
* @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
* @param c The cursor from which to get the data.
* @param autoRequery If true the adapter will call requery() on the
* cursor whenever it changes so the most recent
* data is always displayed. Using true here is discouraged.
*/
public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);
mLayout = mDropDownLayout = layout;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/**
* Standard constructor.
*
* @param context The context where the ListView associated with this adapter is running
* @param layout Resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
* @param c The cursor from which to get the data.
* @param flags Flags used to determine the behavior of the adapter,
* as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
*/
public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c, int flags) {
super(context, c, flags);
mLayout = mDropDownLayout = layout;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/**
* Inflates view(s) from the specified XML file.
*
* @see android.widget.CursorAdapter#newView(android.content.Context,
* android.database.Cursor, ViewGroup)
*/
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(mLayout, parent, false);
}
@Override
public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(mDropDownLayout, parent, false);
}
/**
* <p>Sets the layout resource of the item views.</p>
*
* @param layout the layout resources used to create item views
*/
public void setViewResource(int layout) {
mLayout = layout;
}
/**
* <p>Sets the layout resource of the drop down views.</p>
*
* @param dropDownLayout the layout resources used to create drop down views
*/
public void setDropDownViewResource(int dropDownLayout) {
mDropDownLayout = dropDownLayout;
}
}

View File

@ -1,422 +0,0 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mobeta.android.dslv;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.view.View;
import android.widget.TextView;
import android.widget.ImageView;
// taken from sdk/sources/android-16/android/widget/SimpleCursorAdapter.java
/**
* An easy adapter to map columns from a cursor to TextViews or ImageViews
* defined in an XML file. You can specify which columns you want, which
* views you want to display the columns, and the XML file that defines
* the appearance of these views.
*
* Binding occurs in two phases. First, if a
* {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,
* {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
* is invoked. If the returned value is true, binding has occured. If the
* returned value is false and the view to bind is a TextView,
* {@link #setViewText(TextView, String)} is invoked. If the returned value
* is false and the view to bind is an ImageView,
* {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
* binding can be found, an {@link IllegalStateException} is thrown.
*
* If this adapter is used with filtering, for instance in an
* {@link android.widget.AutoCompleteTextView}, you can use the
* {@link android.widget.SimpleCursorAdapter.CursorToStringConverter} and the
* {@link android.widget.FilterQueryProvider} interfaces
* to get control over the filtering process. You can refer to
* {@link #convertToString(android.database.Cursor)} and
* {@link #runQueryOnBackgroundThread(CharSequence)} for more information.
*/
public class SimpleDragSortCursorAdapter extends ResourceDragSortCursorAdapter {
/**
* A list of columns containing the data to bind to the UI.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected int[] mFrom;
/**
* A list of View ids representing the views to which the data must be bound.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected int[] mTo;
private int mStringConversionColumn = -1;
private CursorToStringConverter mCursorToStringConverter;
private ViewBinder mViewBinder;
String[] mOriginalFrom;
/**
* Constructor the enables auto-requery.
*
* @deprecated This option is discouraged, as it results in Cursor queries
* being performed on the application's UI thread and thus can cause poor
* responsiveness or even Application Not Responding errors. As an alternative,
* use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
*/
@Deprecated
public SimpleDragSortCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c);
mTo = to;
mOriginalFrom = from;
findColumns(c, from);
}
/**
* Standard constructor.
*
* @param context The context where the ListView associated with this
* SimpleListItemFactory is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. The layout file should include at least
* those named views defined in "to"
* @param c The database cursor. Can be null if the cursor is not available yet.
* @param from A list of column names representing the data to bind to the UI. Can be null
* if the cursor is not available yet.
* @param to The views that should display column in the "from" parameter.
* These should all be TextViews. The first N views in this list
* are given the values of the first N columns in the from
* parameter. Can be null if the cursor is not available yet.
* @param flags Flags used to determine the behavior of the adapter,
* as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
*/
public SimpleDragSortCursorAdapter(Context context, int layout,
Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, flags);
mTo = to;
mOriginalFrom = from;
findColumns(c, from);
}
/**
* Binds all of the field names passed into the "to" parameter of the
* constructor with their corresponding cursor columns as specified in the
* "from" parameter.
*
* Binding occurs in two phases. First, if a
* {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,
* {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
* is invoked. If the returned value is true, binding has occured. If the
* returned value is false and the view to bind is a TextView,
* {@link #setViewText(TextView, String)} is invoked. If the returned value is
* false and the view to bind is an ImageView,
* {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
* binding can be found, an {@link IllegalStateException} is thrown.
*
* @throws IllegalStateException if binding cannot occur
*
* @see android.widget.CursorAdapter#bindView(android.view.View,
* android.content.Context, android.database.Cursor)
* @see #getViewBinder()
* @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)
* @see #setViewImage(ImageView, String)
* @see #setViewText(TextView, String)
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewBinder binder = mViewBinder;
final int count = mTo.length;
final int[] from = mFrom;
final int[] to = mTo;
for (int i = 0; i < count; i++) {
final View v = view.findViewById(to[i]);
if (v != null) {
boolean bound = false;
if (binder != null) {
bound = binder.setViewValue(v, cursor, from[i]);
}
if (!bound) {
String text = cursor.getString(from[i]);
if (text == null) {
text = "";
}
if (v instanceof TextView) {
setViewText((TextView) v, text);
} else if (v instanceof ImageView) {
setViewImage((ImageView) v, text);
} else {
throw new IllegalStateException(v.getClass().getName() + " is not a " +
" view that can be bounds by this SimpleCursorAdapter");
}
}
}
}
}
/**
* Returns the {@link ViewBinder} used to bind data to views.
*
* @return a ViewBinder or null if the binder does not exist
*
* @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
* @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)
*/
public ViewBinder getViewBinder() {
return mViewBinder;
}
/**
* Sets the binder used to bind data to views.
*
* @param viewBinder the binder used to bind data to views, can be null to
* remove the existing binder
*
* @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
* @see #getViewBinder()
*/
public void setViewBinder(ViewBinder viewBinder) {
mViewBinder = viewBinder;
}
/**
* Called by bindView() to set the image for an ImageView but only if
* there is no existing ViewBinder or if the existing ViewBinder cannot
* handle binding to an ImageView.
*
* By default, the value will be treated as an image resource. If the
* value cannot be used as an image resource, the value is used as an
* image Uri.
*
* Intended to be overridden by Adapters that need to filter strings
* retrieved from the database.
*
* @param v ImageView to receive an image
* @param value the value retrieved from the cursor
*/
public void setViewImage(ImageView v, String value) {
try {
v.setImageResource(Integer.parseInt(value));
} catch (NumberFormatException nfe) {
v.setImageURI(Uri.parse(value));
}
}
/**
* Called by bindView() to set the text for a TextView but only if
* there is no existing ViewBinder or if the existing ViewBinder cannot
* handle binding to a TextView.
*
* Intended to be overridden by Adapters that need to filter strings
* retrieved from the database.
*
* @param v TextView to receive text
* @param text the text to be set for the TextView
*/
public void setViewText(TextView v, String text) {
v.setText(text);
}
/**
* Return the index of the column used to get a String representation
* of the Cursor.
*
* @return a valid index in the current Cursor or -1
*
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
* @see #setStringConversionColumn(int)
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
* @see #getCursorToStringConverter()
*/
public int getStringConversionColumn() {
return mStringConversionColumn;
}
/**
* Defines the index of the column in the Cursor used to get a String
* representation of that Cursor. The column is used to convert the
* Cursor to a String only when the current CursorToStringConverter
* is null.
*
* @param stringConversionColumn a valid index in the current Cursor or -1 to use the default
* conversion mechanism
*
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
* @see #getStringConversionColumn()
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
* @see #getCursorToStringConverter()
*/
public void setStringConversionColumn(int stringConversionColumn) {
mStringConversionColumn = stringConversionColumn;
}
/**
* Returns the converter used to convert the filtering Cursor
* into a String.
*
* @return null if the converter does not exist or an instance of
* {@link android.widget.SimpleCursorAdapter.CursorToStringConverter}
*
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
* @see #getStringConversionColumn()
* @see #setStringConversionColumn(int)
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
*/
public CursorToStringConverter getCursorToStringConverter() {
return mCursorToStringConverter;
}
/**
* Sets the converter used to convert the filtering Cursor
* into a String.
*
* @param cursorToStringConverter the Cursor to String converter, or
* null to remove the converter
*
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
* @see #getStringConversionColumn()
* @see #setStringConversionColumn(int)
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
*/
public void setCursorToStringConverter(CursorToStringConverter cursorToStringConverter) {
mCursorToStringConverter = cursorToStringConverter;
}
/**
* Returns a CharSequence representation of the specified Cursor as defined
* by the current CursorToStringConverter. If no CursorToStringConverter
* has been set, the String conversion column is used instead. If the
* conversion column is -1, the returned String is empty if the cursor
* is null or Cursor.toString().
*
* @param cursor the Cursor to convert to a CharSequence
*
* @return a non-null CharSequence representing the cursor
*/
@Override
public CharSequence convertToString(Cursor cursor) {
if (mCursorToStringConverter != null) {
return mCursorToStringConverter.convertToString(cursor);
} else if (mStringConversionColumn > -1) {
return cursor.getString(mStringConversionColumn);
}
return super.convertToString(cursor);
}
/**
* Create a map from an array of strings to an array of column-id integers in cursor c.
* If c is null, the array will be discarded.
*
* @param c the cursor to find the columns from
* @param from the Strings naming the columns of interest
*/
private void findColumns(Cursor c, String[] from) {
if (c != null) {
int i;
int count = from.length;
if (mFrom == null || mFrom.length != count) {
mFrom = new int[count];
}
for (i = 0; i < count; i++) {
mFrom[i] = c.getColumnIndexOrThrow(from[i]);
}
} else {
mFrom = null;
}
}
@Override
public Cursor swapCursor(Cursor c) {
// super.swapCursor() will notify observers before we have
// a valid mapping, make sure we have a mapping before this
// happens
findColumns(c, mOriginalFrom);
return super.swapCursor(c);
}
/**
* Change the cursor and change the column-to-view mappings at the same time.
*
* @param c The database cursor. Can be null if the cursor is not available yet.
* @param from A list of column names representing the data to bind to the UI. Can be null
* if the cursor is not available yet.
* @param to The views that should display column in the "from" parameter.
* These should all be TextViews. The first N views in this list
* are given the values of the first N columns in the from
* parameter. Can be null if the cursor is not available yet.
*/
public void changeCursorAndColumns(Cursor c, String[] from, int[] to) {
mOriginalFrom = from;
mTo = to;
// super.changeCursor() will notify observers before we have
// a valid mapping, make sure we have a mapping before this
// happens
findColumns(c, mOriginalFrom);
super.changeCursor(c);
}
/**
* This class can be used by external clients of SimpleCursorAdapter
* to bind values fom the Cursor to views.
*
* You should use this class to bind values from the Cursor to views
* that are not directly supported by SimpleCursorAdapter or to
* change the way binding occurs for views supported by
* SimpleCursorAdapter.
*
* @see SimpleCursorAdapter#bindView(android.view.View, android.content.Context, android.database.Cursor)
* @see SimpleCursorAdapter#setViewImage(ImageView, String)
* @see SimpleCursorAdapter#setViewText(TextView, String)
*/
public static interface ViewBinder {
/**
* Binds the Cursor column defined by the specified index to the specified view.
*
* When binding is handled by this ViewBinder, this method must return true.
* If this method returns false, SimpleCursorAdapter will attempts to handle
* the binding on its own.
*
* @param view the view to bind the data to
* @param cursor the cursor to get the data from
* @param columnIndex the column at which the data can be found in the cursor
*
* @return true if the data was bound to the view, false otherwise
*/
boolean setViewValue(View view, Cursor cursor, int columnIndex);
}
/**
* This class can be used by external clients of SimpleCursorAdapter
* to define how the Cursor should be converted to a String.
*
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
*/
public static interface CursorToStringConverter {
/**
* Returns a CharSequence representing the specified Cursor.
*
* @param cursor the cursor for which a CharSequence representation
* is requested
*
* @return a non-null CharSequence representing the cursor
*/
CharSequence convertToString(Cursor cursor);
}
}

View File

@ -1,89 +0,0 @@
package com.mobeta.android.dslv;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Color;
import android.widget.ListView;
import android.widget.ImageView;
import android.view.View;
import android.view.ViewGroup;
import android.util.Log;
/**
* Simple implementation of the FloatViewManager class. Uses list
* items as they appear in the ListView to create the floating View.
*/
public class SimpleFloatViewManager implements DragSortListView.FloatViewManager {
private Bitmap mFloatBitmap;
private ImageView mImageView;
private int mFloatBGColor = Color.BLACK;
private ListView mListView;
public SimpleFloatViewManager(ListView lv) {
mListView = lv;
}
public void setBackgroundColor(int color) {
mFloatBGColor = color;
}
/**
* This simple implementation creates a Bitmap copy of the
* list item currently shown at ListView <code>position</code>.
*/
@Override
public View onCreateFloatView(int position) {
// Guaranteed that this will not be null? I think so. Nope, got
// a NullPointerException once...
View v = mListView.getChildAt(position + mListView.getHeaderViewsCount() - mListView.getFirstVisiblePosition());
if (v == null) {
return null;
}
v.setPressed(false);
// Create a copy of the drawing cache so that it does not get
// recycled by the framework when the list tries to clean up memory
//v.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
v.setDrawingCacheEnabled(true);
mFloatBitmap = Bitmap.createBitmap(v.getDrawingCache());
v.setDrawingCacheEnabled(false);
if (mImageView == null) {
mImageView = new ImageView(mListView.getContext());
}
mImageView.setBackgroundColor(mFloatBGColor);
mImageView.setPadding(0, 0, 0, 0);
mImageView.setImageBitmap(mFloatBitmap);
mImageView.setLayoutParams(new ViewGroup.LayoutParams(v.getWidth(), v.getHeight()));
return mImageView;
}
/**
* This does nothing
*/
@Override
public void onDragFloatView(View floatView, Point position, Point touch) {
// do nothing
}
/**
* Removes the Bitmap from the ImageView created in
* onCreateFloatView() and tells the system to recycle it.
*/
@Override
public void onDestroyFloatView(View floatView) {
((ImageView) floatView).setImageDrawable(null);
mFloatBitmap.recycle();
mFloatBitmap = null;
}
}

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DragSortListView">
<attr name="collapsed_height" format="dimension" />
<attr name="drag_scroll_start" format="float" />
<attr name="max_drag_scroll_speed" format="float" />
<attr name="float_background_color" format="color" />
<attr name="remove_mode">
<enum name="clickRemove" value="0" />
<enum name="flingRemove" value="1" />
</attr>
<attr name="track_drag_sort" format="boolean"/>
<attr name="float_alpha" format="float"/>
<attr name="slide_shuffle_speed" format="float"/>
<attr name="remove_animation_duration" format="integer"/>
<attr name="drop_animation_duration" format="integer"/>
<attr name="drag_enabled" format="boolean" />
<attr name="sort_enabled" format="boolean" />
<attr name="remove_enabled" format="boolean" />
<attr name="drag_start_mode">
<enum name="onDown" value="0" />
<enum name="onMove" value="1" />
<enum name="onLongPress" value="2"/>
</attr>
<attr name="drag_handle_id" format="integer" />
<attr name="fling_handle_id" format="integer" />
<attr name="click_remove_id" format="integer" />
<attr name="use_default_controller" format="boolean" />
</declare-styleable>
</resources>

View File

@ -1,8 +0,0 @@
apply from: bootstrap.androidModule
android {
lintOptions {
baselineFile file("lint-baseline.xml")
abortOnError true
}
}

View File

@ -1,246 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="4" by="lint 2.3.3">
<issue
id="InlinedApi"
message="Field requires API level 17 (current min is 14): `android.view.View#LAYOUT_DIRECTION_RTL`"
errorLine1=" if (mSlideDrawable != null) mSlideDrawable.setIsRtl(layoutDirection == LAYOUT_DIRECTION_RTL);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/MenuDrawer.java"
line="882"
column="80"/>
</issue>
<issue
id="InlinedApi"
message="Field requires API level 17 (current min is 14): `android.view.View#LAYOUT_DIRECTION_RTL`"
errorLine1=" mSlideDrawable.setIsRtl(ViewHelper.getLayoutDirection(this) == LAYOUT_DIRECTION_RTL);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/MenuDrawer.java"
line="1325"
column="72"/>
</issue>
<issue
id="OldTargetApi"
message="Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the `android.os.Build.VERSION_CODES` javadoc for details."
errorLine1=" &lt;uses-sdk android:minSdkVersion=&quot;7&quot; android:targetSdkVersion=&quot;16&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="7"
column="41"/>
</issue>
<issue
id="GradleOverrides"
message="This `minSdkVersion` value (`7`) is not used; it is always overridden by the value specified in the Gradle build script (`14`)"
errorLine1=" &lt;uses-sdk android:minSdkVersion=&quot;7&quot; android:targetSdkVersion=&quot;16&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="7"
column="15"/>
</issue>
<issue
id="GradleOverrides"
message="This `targetSdkVersion` value (`16`) is not used; it is always overridden by the value specified in the Gradle build script (`22`)"
errorLine1=" &lt;uses-sdk android:minSdkVersion=&quot;7&quot; android:targetSdkVersion=&quot;16&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="7"
column="41"/>
</issue>
<issue
id="ParcelClassLoader"
message="Using the default class loader will not work if you are restoring your own classes. Consider using for example `readBundle(getClass().getClassLoader())` instead."
errorLine1=" mState = in.readBundle();"
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/MenuDrawer.java"
line="1630"
column="25"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is never &lt; 14"
errorLine1=" if (mUsesCompat &amp;&amp; Build.VERSION.SDK_INT &lt; Build.VERSION_CODES.ICE_CREAM_SANDWICH) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
line="41"
column="28"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
line="43"
column="20"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is never &lt; 14"
errorLine1=" if (mUsesCompat &amp;&amp; Build.VERSION.SDK_INT &lt; Build.VERSION_CODES.ICE_CREAM_SANDWICH) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
line="51"
column="28"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
line="53"
column="20"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is never &lt; 14"
errorLine1=" if (mUsesCompat &amp;&amp; Build.VERSION.SDK_INT &lt; Build.VERSION_CODES.ICE_CREAM_SANDWICH) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
line="59"
column="28"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
line="61"
column="20"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is never &lt; 14"
errorLine1=" if (mUsesCompat &amp;&amp; Build.VERSION.SDK_INT &lt; Build.VERSION_CODES.ICE_CREAM_SANDWICH) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
line="67"
column="28"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
line="69"
column="20"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is never &lt; 14"
errorLine1=" if (mUsesCompat &amp;&amp; Build.VERSION.SDK_INT &lt; Build.VERSION_CODES.ICE_CREAM_SANDWICH) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
line="77"
column="28"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
line="79"
column="20"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/DraggableDrawer.java"
line="572"
column="13"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/DraggableDrawer.java"
line="580"
column="13"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/DraggableDrawer.java"
line="588"
column="13"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/DraggableDrawer.java"
line="596"
column="13"/>
</issue>
<issue
id="FloatMath"
message="Use `java.lang.Math#sqrt` instead of `android.util.FloatMath#sqrt()` since it is faster as of API 8"
errorLine1=" float hyp = FloatMath.sqrt(dx * dx + dy * dy);"
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/Scroller.java"
line="374"
column="25"/>
</issue>
<issue
id="FloatMath"
message="Use `java.lang.Math#sqrt` instead of `android.util.FloatMath#sqrt()` since it is faster as of API 8"
errorLine1=" float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);"
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/java/net/simonvt/menudrawer/Scroller.java"
line="391"
column="26"/>
</issue>
</issues>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.simonvt.menudrawer"
android:versionCode="6"
android:versionName="3.0.2">
</manifest>

View File

@ -1,99 +0,0 @@
package net.simonvt.menudrawer;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.FrameLayout;
/**
* FrameLayout which caches the hardware layer if available.
* <p/>
* If it's not posted twice the layer either wont be built on start, or it'll be built twice.
*/
class BuildLayerFrameLayout extends FrameLayout {
private boolean mChanged;
private boolean mHardwareLayersEnabled = true;
private boolean mAttached;
private boolean mFirst = true;
public BuildLayerFrameLayout(Context context) {
super(context);
if (MenuDrawer.USE_TRANSLATIONS) {
setLayerType(LAYER_TYPE_HARDWARE, null);
}
}
public BuildLayerFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
if (MenuDrawer.USE_TRANSLATIONS) {
setLayerType(LAYER_TYPE_HARDWARE, null);
}
}
public BuildLayerFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (MenuDrawer.USE_TRANSLATIONS) {
setLayerType(LAYER_TYPE_HARDWARE, null);
}
}
void setHardwareLayersEnabled(boolean enabled) {
mHardwareLayersEnabled = enabled;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mAttached = true;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mAttached = false;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (MenuDrawer.USE_TRANSLATIONS && mHardwareLayersEnabled) {
post(new Runnable() {
@Override
public void run() {
mChanged = true;
invalidate();
}
});
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mChanged && MenuDrawer.USE_TRANSLATIONS) {
post(new Runnable() {
@Override
public void run() {
if (mAttached) {
final int layerType = getLayerType();
// If it's already a hardware layer, it'll be built anyway.
if (layerType != LAYER_TYPE_HARDWARE || mFirst) {
mFirst = false;
setLayerType(LAYER_TYPE_HARDWARE, null);
buildLayer();
setLayerType(LAYER_TYPE_NONE, null);
}
}
}
});
mChanged = false;
}
}
}

View File

@ -1,170 +0,0 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.simonvt.menudrawer;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
/**
* A specialized Drawable that fills the Canvas with a specified color.
* Note that a ColorDrawable ignores the ColorFilter.
* <p/>
* <p>It can be defined in an XML file with the <code>&lt;color></code> element.</p>
*
* @attr ref android.R.styleable#ColorDrawable_color
*/
class ColorDrawable extends Drawable {
private ColorState mState;
private final Paint mPaint = new Paint();
/** Creates a new black ColorDrawable. */
public ColorDrawable() {
this(null);
}
/**
* Creates a new ColorDrawable with the specified color.
*
* @param color The color to draw.
*/
public ColorDrawable(int color) {
this(null);
setColor(color);
}
private ColorDrawable(ColorState state) {
mState = new ColorState(state);
}
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations() | mState.mChangingConfigurations;
}
@Override
public void draw(Canvas canvas) {
if ((mState.mUseColor >>> 24) != 0) {
mPaint.setColor(mState.mUseColor);
canvas.drawRect(getBounds(), mPaint);
}
}
/**
* Gets the drawable's color value.
*
* @return int The color to draw.
*/
public int getColor() {
return mState.mUseColor;
}
/**
* Sets the drawable's color value. This action will clobber the results of prior calls to
* {@link #setAlpha(int)} on this object, which side-affected the underlying color.
*
* @param color The color to draw.
*/
public void setColor(int color) {
if (mState.mBaseColor != color || mState.mUseColor != color) {
invalidateSelf();
mState.mBaseColor = mState.mUseColor = color;
}
}
/**
* Returns the alpha value of this drawable's color.
*
* @return A value between 0 and 255.
*/
public int getAlpha() {
return mState.mUseColor >>> 24;
}
/**
* Sets the color's alpha value.
*
* @param alpha The alpha value to set, between 0 and 255.
*/
public void setAlpha(int alpha) {
alpha += alpha >> 7; // make it 0..256
int baseAlpha = mState.mBaseColor >>> 24;
int useAlpha = baseAlpha * alpha >> 8;
int oldUseColor = mState.mUseColor;
mState.mUseColor = (mState.mBaseColor << 8 >>> 8) | (useAlpha << 24);
if (oldUseColor != mState.mUseColor) {
invalidateSelf();
}
}
/**
* Setting a color filter on a ColorDrawable has no effect.
*
* @param colorFilter Ignore.
*/
public void setColorFilter(ColorFilter colorFilter) {
}
public int getOpacity() {
switch (mState.mUseColor >>> 24) {
case 255:
return PixelFormat.OPAQUE;
case 0:
return PixelFormat.TRANSPARENT;
}
return PixelFormat.TRANSLUCENT;
}
@Override
public ConstantState getConstantState() {
mState.mChangingConfigurations = getChangingConfigurations();
return mState;
}
static final class ColorState extends ConstantState {
int mBaseColor; // base color, independent of setAlpha()
int mUseColor; // basecolor modulated by setAlpha()
int mChangingConfigurations;
ColorState(ColorState state) {
if (state != null) {
mBaseColor = state.mBaseColor;
mUseColor = state.mUseColor;
}
}
@Override
public Drawable newDrawable() {
return new ColorDrawable(this);
}
@Override
public Drawable newDrawable(Resources res) {
return new ColorDrawable(this);
}
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
}
}
}

View File

@ -1,619 +0,0 @@
package net.simonvt.menudrawer;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
public abstract class DraggableDrawer extends MenuDrawer {
/**
* Key used when saving menu visibility state.
*/
private static final String STATE_MENU_VISIBLE = "net.simonvt.menudrawer.MenuDrawer.menuVisible";
/**
* Interpolator used for peeking at the drawer.
*/
private static final Interpolator PEEK_INTERPOLATOR = new PeekInterpolator();
/**
* The maximum alpha of the dark menu overlay used for dimming the menu.
*/
protected static final int MAX_MENU_OVERLAY_ALPHA = 185;
/**
* Default delay from {@link #peekDrawer()} is called until first animation is run.
*/
private static final long DEFAULT_PEEK_START_DELAY = 5000;
/**
* Default delay between each subsequent animation, after {@link #peekDrawer()} has been called.
*/
private static final long DEFAULT_PEEK_DELAY = 10000;
/**
* The duration of the peek animation.
*/
protected static final int PEEK_DURATION = 5000;
/**
* Distance in dp from closed position from where the drawer is considered closed with regards to touch events.
*/
private static final int CLOSE_ENOUGH = 3;
protected static final int INVALID_POINTER = -1;
/**
* Slop before starting a drag.
*/
protected int mTouchSlop;
/**
* Runnable used when the peek animation is running.
*/
protected final Runnable mPeekRunnable = new Runnable() {
@Override
public void run() {
peekDrawerInvalidate();
}
};
/**
* Runnable used when animating the drawer open/closed.
*/
private final Runnable mDragRunnable = new Runnable() {
@Override
public void run() {
postAnimationInvalidate();
}
};
/**
* Indicates whether the drawer is currently being dragged.
*/
protected boolean mIsDragging;
/**
* The current pointer id.
*/
protected int mActivePointerId = INVALID_POINTER;
/**
* The initial X position of a drag.
*/
protected float mInitialMotionX;
/**
* The initial Y position of a drag.
*/
protected float mInitialMotionY;
/**
* The last X position of a drag.
*/
protected float mLastMotionX = -1;
/**
* The last Y position of a drag.
*/
protected float mLastMotionY = -1;
/**
* Default delay between each subsequent animation, after {@link #peekDrawer()} has been called.
*/
protected long mPeekDelay;
/**
* Scroller used for the peek drawer animation.
*/
protected Scroller mPeekScroller;
/**
* Velocity tracker used when animating the drawer open/closed after a drag.
*/
protected VelocityTracker mVelocityTracker;
/**
* Maximum velocity allowed when animating the drawer open/closed.
*/
protected int mMaxVelocity;
/**
* Indicates whether the menu should be offset when dragging the drawer.
*/
protected boolean mOffsetMenu = true;
/**
* Distance in px from closed position from where the drawer is considered closed with regards to touch events.
*/
protected int mCloseEnough;
/**
* Runnable used for first call to {@link #startPeek()} after {@link #peekDrawer()} has been called.
*/
private Runnable mPeekStartRunnable;
/**
* Scroller used when animating the drawer open/closed.
*/
private Scroller mScroller;
/**
* Indicates whether the current layer type is {@link android.view.View#LAYER_TYPE_HARDWARE}.
*/
protected boolean mLayerTypeHardware;
DraggableDrawer(Activity activity, int dragMode) {
super(activity, dragMode);
}
public DraggableDrawer(Context context) {
super(context);
}
public DraggableDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DraggableDrawer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {
super.initDrawer(context, attrs, defStyle);
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
mMaxVelocity = configuration.getScaledMaximumFlingVelocity();
mScroller = new Scroller(context, MenuDrawer.SMOOTH_INTERPOLATOR);
mPeekScroller = new Scroller(context, DraggableDrawer.PEEK_INTERPOLATOR);
mCloseEnough = dpToPx(DraggableDrawer.CLOSE_ENOUGH);
}
public void toggleMenu(boolean animate) {
if (mDrawerState == STATE_OPEN || mDrawerState == STATE_OPENING) {
closeMenu(animate);
} else if (mDrawerState == STATE_CLOSED || mDrawerState == STATE_CLOSING) {
openMenu(animate);
}
}
public boolean isMenuVisible() {
return mMenuVisible;
}
public void setMenuSize(final int size) {
mMenuSize = size;
if (mDrawerState == STATE_OPEN || mDrawerState == STATE_OPENING) {
setOffsetPixels(mMenuSize);
}
requestLayout();
invalidate();
}
public void setOffsetMenuEnabled(boolean offsetMenu) {
if (offsetMenu != mOffsetMenu) {
mOffsetMenu = offsetMenu;
requestLayout();
invalidate();
}
}
public boolean getOffsetMenuEnabled() {
return mOffsetMenu;
}
public void peekDrawer() {
peekDrawer(DEFAULT_PEEK_START_DELAY, DEFAULT_PEEK_DELAY);
}
public void peekDrawer(long delay) {
peekDrawer(DEFAULT_PEEK_START_DELAY, delay);
}
public void peekDrawer(final long startDelay, final long delay) {
if (startDelay < 0) {
throw new IllegalArgumentException("startDelay must be zero or larger.");
}
if (delay < 0) {
throw new IllegalArgumentException("delay must be zero or larger");
}
removeCallbacks(mPeekRunnable);
removeCallbacks(mPeekStartRunnable);
mPeekDelay = delay;
mPeekStartRunnable = new Runnable() {
@Override
public void run() {
startPeek();
}
};
postDelayed(mPeekStartRunnable, startDelay);
}
public void setHardwareLayerEnabled(boolean enabled) {
if (enabled != mHardwareLayersEnabled) {
mHardwareLayersEnabled = enabled;
mMenuContainer.setHardwareLayersEnabled(enabled);
mContentContainer.setHardwareLayersEnabled(enabled);
stopLayerTranslation();
}
}
public int getTouchMode() {
return mTouchMode;
}
public void setTouchMode(int mode) {
if (mTouchMode != mode) {
mTouchMode = mode;
updateTouchAreaSize();
}
}
public void setTouchBezelSize(int size) {
mTouchBezelSize = size;
}
public int getTouchBezelSize() {
return mTouchBezelSize;
}
/**
* If possible, set the layer type to {@link android.view.View#LAYER_TYPE_HARDWARE}.
*/
protected void startLayerTranslation() {
if (USE_TRANSLATIONS && mHardwareLayersEnabled && !mLayerTypeHardware) {
mLayerTypeHardware = true;
mContentContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
mMenuContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
}
/**
* If the current layer type is {@link android.view.View#LAYER_TYPE_HARDWARE}, this will set it to
* {@link View#LAYER_TYPE_NONE}.
*/
protected void stopLayerTranslation() {
if (mLayerTypeHardware) {
mLayerTypeHardware = false;
mContentContainer.setLayerType(View.LAYER_TYPE_NONE, null);
mMenuContainer.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
/**
* Called when a drag has been ended.
*/
protected void endDrag() {
mIsDragging = false;
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
/**
* Stops ongoing animation of the drawer.
*/
protected void stopAnimation() {
removeCallbacks(mDragRunnable);
mScroller.abortAnimation();
stopLayerTranslation();
}
/**
* Called when a drawer animation has successfully completed.
*/
private void completeAnimation() {
mScroller.abortAnimation();
final int finalX = mScroller.getFinalX();
setOffsetPixels(finalX);
setDrawerState(finalX == 0 ? STATE_CLOSED : STATE_OPEN);
stopLayerTranslation();
}
protected void cancelContentTouch() {
final long now = SystemClock.uptimeMillis();
final MotionEvent cancelEvent = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
getChildAt(i).dispatchTouchEvent(cancelEvent);
}
mContentContainer.dispatchTouchEvent(cancelEvent);
cancelEvent.recycle();
}
/**
* Moves the drawer to the position passed.
*
* @param position The position the content is moved to.
* @param velocity Optional velocity if called by releasing a drag event.
* @param animate Whether the move is animated.
*/
protected void animateOffsetTo(int position, int velocity, boolean animate) {
endDrag();
endPeek();
final int startX = (int) mOffsetPixels;
final int dx = position - startX;
if (dx == 0 || !animate) {
setOffsetPixels(position);
setDrawerState(position == 0 ? STATE_CLOSED : STATE_OPEN);
stopLayerTranslation();
return;
}
int duration;
velocity = Math.abs(velocity);
if (velocity > 0) {
duration = 4 * Math.round(1000.f * Math.abs((float) dx / velocity));
} else {
duration = (int) (600.f * Math.abs((float) dx / mMenuSize));
}
duration = Math.min(duration, mMaxAnimationDuration);
animateOffsetTo(position, duration);
}
protected void animateOffsetTo(int position, int duration) {
final int startX = (int) mOffsetPixels;
final int dx = position - startX;
if (dx > 0) {
setDrawerState(STATE_OPENING);
mScroller.startScroll(startX, 0, dx, 0, duration);
} else {
setDrawerState(STATE_CLOSING);
mScroller.startScroll(startX, 0, dx, 0, duration);
}
startLayerTranslation();
postAnimationInvalidate();
}
/**
* Callback when each frame in the drawer animation should be drawn.
*/
private void postAnimationInvalidate() {
if (mScroller.computeScrollOffset()) {
final int oldX = (int) mOffsetPixels;
final int x = mScroller.getCurrX();
if (x != oldX) setOffsetPixels(x);
if (x != mScroller.getFinalX()) {
postOnAnimation(mDragRunnable);
return;
}
}
completeAnimation();
}
/**
* Starts peek drawer animation.
*/
protected void startPeek() {
initPeekScroller();
startLayerTranslation();
peekDrawerInvalidate();
}
protected abstract void initPeekScroller();
/**
* Callback when each frame in the peek drawer animation should be drawn.
*/
private void peekDrawerInvalidate() {
if (mPeekScroller.computeScrollOffset()) {
final int oldX = (int) mOffsetPixels;
final int x = mPeekScroller.getCurrX();
if (x != oldX) setOffsetPixels(x);
if (!mPeekScroller.isFinished()) {
postOnAnimation(mPeekRunnable);
return;
} else if (mPeekDelay > 0) {
mPeekStartRunnable = new Runnable() {
@Override
public void run() {
startPeek();
}
};
postDelayed(mPeekStartRunnable, mPeekDelay);
}
}
completePeek();
}
/**
* Called when the peek drawer animation has successfully completed.
*/
private void completePeek() {
mPeekScroller.abortAnimation();
setOffsetPixels(0);
setDrawerState(STATE_CLOSED);
stopLayerTranslation();
}
/**
* Stops ongoing peek drawer animation.
*/
protected void endPeek() {
removeCallbacks(mPeekStartRunnable);
removeCallbacks(mPeekRunnable);
stopLayerTranslation();
}
protected boolean isCloseEnough() {
return Math.abs(mOffsetPixels) <= mCloseEnough;
}
protected boolean canChildrenScroll(int dx, int dy, int x, int y) {
boolean canScroll = false;
switch (getPosition()) {
case LEFT:
case RIGHT:
if (!mMenuVisible) {
canScroll = canChildScrollHorizontally(mContentContainer, false, dx,
x - ViewHelper.getLeft(mContentContainer), y - ViewHelper.getTop(mContentContainer));
} else {
canScroll = canChildScrollHorizontally(mMenuContainer, false, dx,
x - ViewHelper.getLeft(mMenuContainer), y - ViewHelper.getTop(mContentContainer));
}
break;
case TOP:
case BOTTOM:
if (!mMenuVisible) {
canScroll = canChildScrollVertically(mContentContainer, false, dy,
x - ViewHelper.getLeft(mContentContainer), y - ViewHelper.getTop(mContentContainer));
} else {
canScroll = canChildScrollVertically(mMenuContainer, false, dy,
x - ViewHelper.getLeft(mMenuContainer), y - ViewHelper.getTop(mContentContainer));
}
}
return canScroll;
}
/**
* Tests scrollability within child views of v given a delta of dx.
*
* @param v View to test for horizontal scrollability
* @param checkV Whether the view should be checked for draggability
* @param dx Delta scrolled in pixels
* @param x X coordinate of the active touch point
* @param y Y coordinate of the active touch point
* @return true if child views of v can be scrolled by delta of dx.
*/
protected boolean canChildScrollHorizontally(View v, boolean checkV, int dx, int x, int y) {
if (v instanceof ViewGroup) {
final ViewGroup group = (ViewGroup) v;
final int count = group.getChildCount();
// Count backwards - let topmost views consume scroll distance first.
for (int i = count - 1; i >= 0; i--) {
final View child = group.getChildAt(i);
final int childLeft = child.getLeft() + supportGetTranslationX(child);
final int childRight = child.getRight() + supportGetTranslationX(child);
final int childTop = child.getTop() + supportGetTranslationY(child);
final int childBottom = child.getBottom() + supportGetTranslationY(child);
if (x >= childLeft && x < childRight && y >= childTop && y < childBottom
&& canChildScrollHorizontally(child, true, dx, x - childLeft, y - childTop)) {
return true;
}
}
}
return checkV && mOnInterceptMoveEventListener.isViewDraggable(v, dx, x, y);
}
/**
* Tests scrollability within child views of v given a delta of dx.
*
* @param v View to test for horizontal scrollability
* @param checkV Whether the view should be checked for draggability
* @param dx Delta scrolled in pixels
* @param x X coordinate of the active touch point
* @param y Y coordinate of the active touch point
* @return true if child views of v can be scrolled by delta of dx.
*/
protected boolean canChildScrollVertically(View v, boolean checkV, int dx, int x, int y) {
if (v instanceof ViewGroup) {
final ViewGroup group = (ViewGroup) v;
final int count = group.getChildCount();
// Count backwards - let topmost views consume scroll distance first.
for (int i = count - 1; i >= 0; i--) {
final View child = group.getChildAt(i);
final int childLeft = child.getLeft() + supportGetTranslationX(child);
final int childRight = child.getRight() + supportGetTranslationX(child);
final int childTop = child.getTop() + supportGetTranslationY(child);
final int childBottom = child.getBottom() + supportGetTranslationY(child);
if (x >= childLeft && x < childRight && y >= childTop && y < childBottom
&& canChildScrollVertically(child, true, dx, x - childLeft, y - childTop)) {
return true;
}
}
}
return checkV && mOnInterceptMoveEventListener.isViewDraggable(v, dx, x, y);
}
protected float getXVelocity(VelocityTracker velocityTracker) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
return velocityTracker.getXVelocity(mActivePointerId);
}
return velocityTracker.getXVelocity();
}
protected float getYVelocity(VelocityTracker velocityTracker) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
return velocityTracker.getYVelocity(mActivePointerId);
}
return velocityTracker.getYVelocity();
}
private int supportGetTranslationY(View v) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
return (int) v.getTranslationY();
}
return 0;
}
private int supportGetTranslationX(View v) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
return (int) v.getTranslationX();
}
return 0;
}
void saveState(Bundle state) {
final boolean menuVisible = mDrawerState == STATE_OPEN || mDrawerState == STATE_OPENING;
state.putBoolean(STATE_MENU_VISIBLE, menuVisible);
}
public void restoreState(Parcelable in) {
super.restoreState(in);
Bundle state = (Bundle) in;
final boolean menuOpen = state.getBoolean(STATE_MENU_VISIBLE);
if (menuOpen) {
openMenu(false);
} else {
setOffsetPixels(0);
}
mDrawerState = menuOpen ? STATE_OPEN : STATE_CLOSED;
}
}

View File

@ -1,175 +0,0 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.simonvt.menudrawer;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
/**
* This class encapsulates scrolling. The duration of the scroll
* can be passed in the constructor and specifies the maximum time that
* the scrolling animation should take. Past this time, the scrolling is
* automatically moved to its final stage and computeScrollOffset()
* will always return false to indicate that scrolling is over.
*/
class FloatScroller {
private float mStart;
private float mFinal;
private float mCurr;
private long mStartTime;
private int mDuration;
private float mDurationReciprocal;
private float mDeltaX;
private boolean mFinished;
private Interpolator mInterpolator;
/**
* Create a Scroller with the specified interpolator. If the interpolator is
* null, the default (viscous) interpolator will be used. Specify whether or
* not to support progressive "flywheel" behavior in flinging.
*/
public FloatScroller(Interpolator interpolator) {
mFinished = true;
mInterpolator = interpolator;
}
/**
* Returns whether the scroller has finished scrolling.
*
* @return True if the scroller has finished scrolling, false otherwise.
*/
public final boolean isFinished() {
return mFinished;
}
/**
* Force the finished field to a particular value.
*
* @param finished The new finished value.
*/
public final void forceFinished(boolean finished) {
mFinished = finished;
}
/**
* Returns how long the scroll event will take, in milliseconds.
*
* @return The duration of the scroll in milliseconds.
*/
public final int getDuration() {
return mDuration;
}
/**
* Returns the current offset in the scroll.
*
* @return The new offset as an absolute distance from the origin.
*/
public final float getCurr() {
return mCurr;
}
/**
* Returns the start offset in the scroll.
*
* @return The start offset as an absolute distance from the origin.
*/
public final float getStart() {
return mStart;
}
/**
* Returns where the scroll will end. Valid only for "fling" scrolls.
*
* @return The final offset as an absolute distance from the origin.
*/
public final float getFinal() {
return mFinal;
}
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
float x = timePassed * mDurationReciprocal;
x = mInterpolator.getInterpolation(x);
mCurr = mStart + x * mDeltaX;
} else {
mCurr = mFinal;
mFinished = true;
}
return true;
}
public void startScroll(float start, float delta, int duration) {
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStart = start;
mFinal = start + delta;
mDeltaX = delta;
mDurationReciprocal = 1.0f / (float) mDuration;
}
/**
* Stops the animation. Contrary to {@link #forceFinished(boolean)},
* aborting the animating cause the scroller to move to the final x and y
* position
*
* @see #forceFinished(boolean)
*/
public void abortAnimation() {
mCurr = mFinal;
mFinished = true;
}
/**
* Extend the scroll animation. This allows a running animation to scroll
* further and longer, when used with {@link #setFinal(float)}.
*
* @param extend Additional time to scroll in milliseconds.
* @see #setFinal(float)
*/
public void extendDuration(int extend) {
int passed = timePassed();
mDuration = passed + extend;
mDurationReciprocal = 1.0f / mDuration;
mFinished = false;
}
/**
* Returns the time elapsed since the beginning of the scrolling.
*
* @return The elapsed time in milliseconds.
*/
public int timePassed() {
return (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
}
public void setFinal(float newVal) {
mFinal = newVal;
mDeltaX = mFinal - mStart;
mFinished = false;
}
}

View File

@ -1,28 +0,0 @@
package net.simonvt.menudrawer;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
/**
* FrameLayout which doesn't let touch events propagate to views positioned behind it in the view hierarchy.
*/
class NoClickThroughFrameLayout extends BuildLayerFrameLayout {
public NoClickThroughFrameLayout(Context context) {
super(context);
}
public NoClickThroughFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoClickThroughFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
}

View File

@ -1,781 +0,0 @@
package net.simonvt.menudrawer;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
public class OverlayDrawer extends DraggableDrawer {
private static final String TAG = "OverlayDrawer";
private int mPeekSize;
private Runnable mRevealRunnable = new Runnable() {
@Override
public void run() {
cancelContentTouch();
int animateTo = 0;
switch (getPosition()) {
case RIGHT:
case BOTTOM:
animateTo = -mPeekSize;
break;
default:
animateTo = mPeekSize;
break;
}
animateOffsetTo(animateTo, 250);
}
};
OverlayDrawer(Activity activity, int dragMode) {
super(activity, dragMode);
}
public OverlayDrawer(Context context) {
super(context);
}
public OverlayDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public OverlayDrawer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {
super.initDrawer(context, attrs, defStyle);
super.addView(mContentContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
if (USE_TRANSLATIONS) {
mContentContainer.setLayerType(View.LAYER_TYPE_NONE, null);
}
mContentContainer.setHardwareLayersEnabled(false);
super.addView(mMenuContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mPeekSize = dpToPx(20);
}
@Override
protected void drawOverlay(Canvas canvas) {
final int width = getWidth();
final int height = getHeight();
final int offsetPixels = (int) mOffsetPixels;
final float openRatio = Math.abs(mOffsetPixels) / mMenuSize;
switch (getPosition()) {
case LEFT:
mMenuOverlay.setBounds(offsetPixels, 0, width, height);
break;
case RIGHT:
mMenuOverlay.setBounds(0, 0, width + offsetPixels, height);
break;
case TOP:
mMenuOverlay.setBounds(0, offsetPixels, width, height);
break;
case BOTTOM:
mMenuOverlay.setBounds(0, 0, width, height + offsetPixels);
break;
}
mMenuOverlay.setAlpha((int) (MAX_MENU_OVERLAY_ALPHA * openRatio));
mMenuOverlay.draw(canvas);
}
@Override
public void openMenu(boolean animate) {
int animateTo = 0;
switch (getPosition()) {
case LEFT:
case TOP:
animateTo = mMenuSize;
break;
case RIGHT:
case BOTTOM:
animateTo = -mMenuSize;
break;
}
animateOffsetTo(animateTo, 0, animate);
}
@Override
public void closeMenu(boolean animate) {
animateOffsetTo(0, 0, animate);
}
@Override
protected void onOffsetPixelsChanged(int offsetPixels) {
if (USE_TRANSLATIONS) {
switch (getPosition()) {
case LEFT:
mMenuContainer.setTranslationX(offsetPixels - mMenuSize);
break;
case TOP:
mMenuContainer.setTranslationY(offsetPixels - mMenuSize);
break;
case RIGHT:
mMenuContainer.setTranslationX(offsetPixels + mMenuSize);
break;
case BOTTOM:
mMenuContainer.setTranslationY(offsetPixels + mMenuSize);
break;
}
} else {
switch (getPosition()) {
case TOP:
mMenuContainer.offsetTopAndBottom(offsetPixels - mMenuContainer.getBottom());
break;
case BOTTOM:
mMenuContainer.offsetTopAndBottom(offsetPixels - (mMenuContainer.getTop() - getHeight()));
break;
case LEFT:
mMenuContainer.offsetLeftAndRight(offsetPixels - mMenuContainer.getRight());
break;
case RIGHT:
mMenuContainer.offsetLeftAndRight(offsetPixels - (mMenuContainer.getLeft() - getWidth()));
break;
}
}
invalidate();
}
@Override
protected void initPeekScroller() {
switch (getPosition()) {
case RIGHT:
case BOTTOM: {
final int dx = -mPeekSize;
mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);
break;
}
default: {
final int dx = mPeekSize;
mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);
break;
}
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
onOffsetPixelsChanged((int) mOffsetPixels);
}
@Override
protected GradientDrawable.Orientation getDropShadowOrientation() {
switch (getPosition()) {
case TOP:
return GradientDrawable.Orientation.TOP_BOTTOM;
case RIGHT:
return GradientDrawable.Orientation.RIGHT_LEFT;
case BOTTOM:
return GradientDrawable.Orientation.BOTTOM_TOP;
default:
return GradientDrawable.Orientation.LEFT_RIGHT;
}
}
@Override
protected void updateDropShadowRect() {
final float openRatio = Math.abs(mOffsetPixels) / mMenuSize;
final int dropShadowSize = (int) (mDropShadowSize * openRatio);
switch (getPosition()) {
case LEFT:
mDropShadowRect.top = 0;
mDropShadowRect.bottom = getHeight();
mDropShadowRect.left = ViewHelper.getRight(mMenuContainer);
mDropShadowRect.right = mDropShadowRect.left + dropShadowSize;
break;
case TOP:
mDropShadowRect.left = 0;
mDropShadowRect.right = getWidth();
mDropShadowRect.top = ViewHelper.getBottom(mMenuContainer);
mDropShadowRect.bottom = mDropShadowRect.top + dropShadowSize;
break;
case RIGHT:
mDropShadowRect.top = 0;
mDropShadowRect.bottom = getHeight();
mDropShadowRect.right = ViewHelper.getLeft(mMenuContainer);
mDropShadowRect.left = mDropShadowRect.right - dropShadowSize;
break;
case BOTTOM:
mDropShadowRect.left = 0;
mDropShadowRect.right = getWidth();
mDropShadowRect.bottom = ViewHelper.getTop(mMenuContainer);
mDropShadowRect.top = mDropShadowRect.bottom - dropShadowSize;
break;
}
}
@Override
protected void startLayerTranslation() {
if (USE_TRANSLATIONS && mHardwareLayersEnabled && !mLayerTypeHardware) {
mLayerTypeHardware = true;
mMenuContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
}
@Override
protected void stopLayerTranslation() {
if (mLayerTypeHardware) {
mLayerTypeHardware = false;
mMenuContainer.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int width = r - l;
final int height = b - t;
mContentContainer.layout(0, 0, width, height);
if (USE_TRANSLATIONS) {
switch (getPosition()) {
case LEFT:
mMenuContainer.layout(0, 0, mMenuSize, height);
break;
case RIGHT:
mMenuContainer.layout(width - mMenuSize, 0, width, height);
break;
case TOP:
mMenuContainer.layout(0, 0, width, mMenuSize);
break;
case BOTTOM:
mMenuContainer.layout(0, height - mMenuSize, width, height);
break;
}
} else {
final int offsetPixels = (int) mOffsetPixels;
final int menuSize = mMenuSize;
switch (getPosition()) {
case LEFT:
mMenuContainer.layout(-menuSize + offsetPixels, 0, offsetPixels, height);
break;
case RIGHT:
mMenuContainer.layout(width + offsetPixels, 0, width + menuSize + offsetPixels, height);
break;
case TOP:
mMenuContainer.layout(0, -menuSize + offsetPixels, width, offsetPixels);
break;
case BOTTOM:
mMenuContainer.layout(0, height + offsetPixels, width, height + menuSize + offsetPixels);
break;
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
throw new IllegalStateException("Must measure with an exact size");
}
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int height = MeasureSpec.getSize(heightMeasureSpec);
if (mOffsetPixels == -1) openMenu(false);
int menuWidthMeasureSpec;
int menuHeightMeasureSpec;
switch (getPosition()) {
case TOP:
case BOTTOM:
menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);
menuHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, mMenuSize);
break;
default:
// LEFT/RIGHT
menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, mMenuSize);
menuHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);
}
mMenuContainer.measure(menuWidthMeasureSpec, menuHeightMeasureSpec);
final int contentWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);
final int contentHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);
mContentContainer.measure(contentWidthMeasureSpec, contentHeightMeasureSpec);
setMeasuredDimension(width, height);
updateTouchAreaSize();
}
private boolean isContentTouch(int x, int y) {
boolean contentTouch = false;
switch (getPosition()) {
case LEFT:
contentTouch = ViewHelper.getRight(mMenuContainer) < x;
break;
case RIGHT:
contentTouch = ViewHelper.getLeft(mMenuContainer) > x;
break;
case TOP:
contentTouch = ViewHelper.getBottom(mMenuContainer) < y;
break;
case BOTTOM:
contentTouch = ViewHelper.getTop(mMenuContainer) > y;
break;
}
return contentTouch;
}
protected boolean onDownAllowDrag(int x, int y) {
switch (getPosition()) {
case LEFT:
return (!mMenuVisible && mInitialMotionX <= mTouchSize)
|| (mMenuVisible && mInitialMotionX <= mOffsetPixels);
case RIGHT:
final int width = getWidth();
final int initialMotionX = (int) mInitialMotionX;
return (!mMenuVisible && initialMotionX >= width - mTouchSize)
|| (mMenuVisible && initialMotionX >= width + mOffsetPixels);
case TOP:
return (!mMenuVisible && mInitialMotionY <= mTouchSize)
|| (mMenuVisible && mInitialMotionY <= mOffsetPixels);
case BOTTOM:
final int height = getHeight();
return (!mMenuVisible && mInitialMotionY >= height - mTouchSize)
|| (mMenuVisible && mInitialMotionY >= height + mOffsetPixels);
}
return false;
}
protected boolean onMoveAllowDrag(int x, int y, float dx, float dy) {
if (mMenuVisible && mTouchMode == TOUCH_MODE_FULLSCREEN) {
return true;
}
switch (getPosition()) {
case LEFT:
return (!mMenuVisible && mInitialMotionX <= mTouchSize && (dx > 0)) // Drawer closed
|| (mMenuVisible && x <= mOffsetPixels) // Drawer open
|| (Math.abs(mOffsetPixels) <= mPeekSize && mMenuVisible); // Drawer revealed
case RIGHT:
final int width = getWidth();
return (!mMenuVisible && mInitialMotionX >= width - mTouchSize && (dx < 0))
|| (mMenuVisible && x >= width - mOffsetPixels)
|| (Math.abs(mOffsetPixels) <= mPeekSize && mMenuVisible);
case TOP:
return (!mMenuVisible && mInitialMotionY <= mTouchSize && (dy > 0))
|| (mMenuVisible && x <= mOffsetPixels)
|| (Math.abs(mOffsetPixels) <= mPeekSize && mMenuVisible);
case BOTTOM:
final int height = getHeight();
return (!mMenuVisible && mInitialMotionY >= height - mTouchSize && (dy < 0))
|| (mMenuVisible && x >= height - mOffsetPixels)
|| (Math.abs(mOffsetPixels) <= mPeekSize && mMenuVisible);
}
return false;
}
protected void onMoveEvent(float dx, float dy) {
switch (getPosition()) {
case LEFT:
setOffsetPixels(Math.min(Math.max(mOffsetPixels + dx, 0), mMenuSize));
break;
case RIGHT:
setOffsetPixels(Math.max(Math.min(mOffsetPixels + dx, 0), -mMenuSize));
break;
case TOP:
setOffsetPixels(Math.min(Math.max(mOffsetPixels + dy, 0), mMenuSize));
break;
case BOTTOM:
setOffsetPixels(Math.max(Math.min(mOffsetPixels + dy, 0), -mMenuSize));
break;
}
}
protected void onUpEvent(int x, int y) {
final int offsetPixels = (int) mOffsetPixels;
switch (getPosition()) {
case LEFT: {
if (mIsDragging) {
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
final int initialVelocity = (int) getXVelocity(mVelocityTracker);
mLastMotionX = x;
animateOffsetTo(initialVelocity > 0 ? mMenuSize : 0, initialVelocity, true);
// Close the menu when content is clicked while the menu is visible.
} else if (mMenuVisible) {
closeMenu();
}
break;
}
case TOP: {
if (mIsDragging) {
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
final int initialVelocity = (int) getYVelocity(mVelocityTracker);
mLastMotionY = y;
animateOffsetTo(initialVelocity > 0 ? mMenuSize : 0, initialVelocity, true);
// Close the menu when content is clicked while the menu is visible.
} else if (mMenuVisible) {
closeMenu();
}
break;
}
case RIGHT: {
final int width = getWidth();
if (mIsDragging) {
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
final int initialVelocity = (int) getXVelocity(mVelocityTracker);
mLastMotionX = x;
animateOffsetTo(initialVelocity > 0 ? 0 : -mMenuSize, initialVelocity, true);
// Close the menu when content is clicked while the menu is visible.
} else if (mMenuVisible) {
closeMenu();
}
break;
}
case BOTTOM: {
if (mIsDragging) {
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
final int initialVelocity = (int) getYVelocity(mVelocityTracker);
mLastMotionY = y;
animateOffsetTo(initialVelocity < 0 ? -mMenuSize : 0, initialVelocity, true);
// Close the menu when content is clicked while the menu is visible.
} else if (mMenuVisible) {
closeMenu();
}
break;
}
}
}
protected boolean checkTouchSlop(float dx, float dy) {
switch (getPosition()) {
case TOP:
case BOTTOM:
return Math.abs(dy) > mTouchSlop && Math.abs(dy) > Math.abs(dx);
default:
return Math.abs(dx) > mTouchSlop && Math.abs(dx) > Math.abs(dy);
}
}
@Override
protected void stopAnimation() {
super.stopAnimation();
removeCallbacks(mRevealRunnable);
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
removeCallbacks(mRevealRunnable);
mActivePointerId = INVALID_POINTER;
mIsDragging = false;
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
if (Math.abs(mOffsetPixels) > mMenuSize / 2) {
openMenu();
} else {
closeMenu();
}
return false;
}
if (action == MotionEvent.ACTION_DOWN && mMenuVisible && isCloseEnough()) {
setOffsetPixels(0);
stopAnimation();
endPeek();
setDrawerState(STATE_CLOSED);
mIsDragging = false;
}
// Always intercept events over the content while menu is visible.
if (mMenuVisible) {
int index = 0;
if (mActivePointerId != INVALID_POINTER) {
index = ev.findPointerIndex(mActivePointerId);
index = index == -1 ? 0 : index;
}
final int x = (int) ev.getX(index);
final int y = (int) ev.getY(index);
if (isContentTouch(x, y)) {
return true;
}
}
if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {
return false;
}
if (action != MotionEvent.ACTION_DOWN && mIsDragging) {
return true;
}
switch (action) {
case MotionEvent.ACTION_DOWN: {
mLastMotionX = mInitialMotionX = ev.getX();
mLastMotionY = mInitialMotionY = ev.getY();
final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);
mActivePointerId = ev.getPointerId(0);
if (allowDrag) {
setDrawerState(mMenuVisible ? STATE_OPEN : STATE_CLOSED);
stopAnimation();
endPeek();
if (!mMenuVisible && mInitialMotionX <= mPeekSize) {
postDelayed(mRevealRunnable, 160);
}
mIsDragging = false;
}
break;
}
case MotionEvent.ACTION_MOVE: {
final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER) {
// If we don't have a valid id, the touch down wasn't on content.
break;
}
final int pointerIndex = ev.findPointerIndex(activePointerId);
if (pointerIndex == -1) {
mIsDragging = false;
mActivePointerId = INVALID_POINTER;
endDrag();
closeMenu(true);
return false;
}
final float x = ev.getX(pointerIndex);
final float dx = x - mLastMotionX;
final float y = ev.getY(pointerIndex);
final float dy = y - mLastMotionY;
if (Math.abs(dx) >= mTouchSlop || Math.abs(dy) >= mTouchSlop) {
removeCallbacks(mRevealRunnable);
endPeek();
}
if (checkTouchSlop(dx, dy)) {
if (mOnInterceptMoveEventListener != null && (mTouchMode == TOUCH_MODE_FULLSCREEN || mMenuVisible)
&& canChildrenScroll((int) dx, (int) dy, (int) x, (int) y)) {
endDrag(); // Release the velocity tracker
requestDisallowInterceptTouchEvent(true);
return false;
}
final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);
if (allowDrag) {
endPeek();
stopAnimation();
setDrawerState(STATE_DRAGGING);
mIsDragging = true;
mLastMotionX = x;
mLastMotionY = y;
}
}
break;
}
case MotionEvent.ACTION_POINTER_UP:
onPointerUp(ev);
mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));
break;
}
if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(ev);
return mIsDragging;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {
return false;
}
final int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(ev);
switch (action) {
case MotionEvent.ACTION_DOWN: {
mLastMotionX = mInitialMotionX = ev.getX();
mLastMotionY = mInitialMotionY = ev.getY();
final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);
mActivePointerId = ev.getPointerId(0);
if (allowDrag) {
stopAnimation();
endPeek();
if (!mMenuVisible && mLastMotionX <= mPeekSize) {
postDelayed(mRevealRunnable, 160);
}
startLayerTranslation();
}
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
mIsDragging = false;
mActivePointerId = INVALID_POINTER;
endDrag();
closeMenu(true);
return false;
}
if (!mIsDragging) {
final float x = ev.getX(pointerIndex);
final float dx = x - mLastMotionX;
final float y = ev.getY(pointerIndex);
final float dy = y - mLastMotionY;
if (checkTouchSlop(dx, dy)) {
final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);
if (allowDrag) {
endPeek();
stopAnimation();
setDrawerState(STATE_DRAGGING);
mIsDragging = true;
mLastMotionX = x;
mLastMotionY = y;
} else {
mInitialMotionX = x;
mInitialMotionY = y;
}
}
}
if (mIsDragging) {
startLayerTranslation();
final float x = ev.getX(pointerIndex);
final float dx = x - mLastMotionX;
final float y = ev.getY(pointerIndex);
final float dy = y - mLastMotionY;
mLastMotionX = x;
mLastMotionY = y;
onMoveEvent(dx, dy);
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
removeCallbacks(mRevealRunnable);
int index = ev.findPointerIndex(mActivePointerId);
index = index == -1 ? 0 : index;
final int x = (int) ev.getX(index);
final int y = (int) ev.getY(index);
onUpEvent(x, y);
mActivePointerId = INVALID_POINTER;
mIsDragging = false;
break;
}
case MotionEvent.ACTION_POINTER_DOWN:
final int index = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
mLastMotionX = ev.getX(index);
mLastMotionY = ev.getY(index);
mActivePointerId = ev.getPointerId(index);
break;
case MotionEvent.ACTION_POINTER_UP:
onPointerUp(ev);
mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));
break;
}
return true;
}
private void onPointerUp(MotionEvent ev) {
final int pointerIndex = ev.getActionIndex();
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastMotionX = ev.getX(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
if (mVelocityTracker != null) {
mVelocityTracker.clear();
}
}
}
}

View File

@ -1,28 +0,0 @@
package net.simonvt.menudrawer;
import android.view.animation.Interpolator;
class PeekInterpolator implements Interpolator {
private static final String TAG = "PeekInterpolator";
private static final SinusoidalInterpolator SINUSOIDAL_INTERPOLATOR = new SinusoidalInterpolator();
@Override
public float getInterpolation(float input) {
float result;
if (input < 1.f / 3.f) {
result = SINUSOIDAL_INTERPOLATOR.getInterpolation(input * 3);
} else if (input > 2.f / 3.f) {
final float val = ((input + 1.f / 3.f) - 1.f) * 3.f;
result = 1.f - SINUSOIDAL_INTERPOLATOR.getInterpolation(val);
} else {
result = 1.f;
}
return result;
}
}

View File

@ -1,50 +0,0 @@
package net.simonvt.menudrawer;
import android.util.SparseArray;
/**
* Enums used for positioning the drawer.
*/
public enum Position {
// Positions the drawer to the left of the content.
LEFT(0),
// Positions the drawer above the content.
TOP(1),
// Positions the drawer to the right of the content.
RIGHT(2),
// Positions the drawer below the content.
BOTTOM(3),
/**
* Position the drawer at the start edge. This will position the drawer to the {@link #LEFT} with LTR languages and
* {@link #RIGHT} with RTL languages.
*/
START(4),
/**
* Position the drawer at the end edge. This will position the drawer to the {@link #RIGHT} with LTR languages and
* {@link #LEFT} with RTL languages.
*/
END(5);
final int mValue;
Position(int value) {
mValue = value;
}
private static final SparseArray<Position> STRING_MAPPING = new SparseArray<Position>();
static {
for (Position via : Position.values()) {
STRING_MAPPING.put(via.mValue, via);
}
}
public static Position fromValue(int value) {
return STRING_MAPPING.get(value);
}
}

View File

@ -1,504 +0,0 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.simonvt.menudrawer;
import android.content.Context;
import android.hardware.SensorManager;
import android.os.Build;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
/**
* This class encapsulates scrolling. The duration of the scroll
* can be passed in the constructor and specifies the maximum time that
* the scrolling animation should take. Past this time, the scrolling is
* automatically moved to its final stage and computeScrollOffset()
* will always return false to indicate that scrolling is over.
*/
class Scroller {
private int mMode;
private int mStartX;
private int mStartY;
private int mFinalX;
private int mFinalY;
private int mMinX;
private int mMaxX;
private int mMinY;
private int mMaxY;
private int mCurrX;
private int mCurrY;
private long mStartTime;
private int mDuration;
private float mDurationReciprocal;
private float mDeltaX;
private float mDeltaY;
private boolean mFinished;
private Interpolator mInterpolator;
private boolean mFlywheel;
private float mVelocity;
private static final int DEFAULT_DURATION = 250;
private static final int SCROLL_MODE = 0;
private static final int FLING_MODE = 1;
private static final float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9));
private static final float ALPHA = 800; // pixels / seconds
private static final float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance)
private static final float END_TENSION = 1.0f - START_TENSION;
private static final int NB_SAMPLES = 100;
private static final float[] SPLINE = new float[NB_SAMPLES + 1];
private float mDeceleration;
private final float mPpi;
static {
float xMin = 0.0f;
for (int i = 0; i <= NB_SAMPLES; i++) {
final float t = (float) i / NB_SAMPLES;
float xMax = 1.0f;
float x, tx, coef;
while (true) {
x = xMin + (xMax - xMin) / 2.0f;
coef = 3.0f * x * (1.0f - x);
tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x;
if (Math.abs(tx - t) < 1E-5) break;
if (tx > t) xMax = x;
else xMin = x;
}
final float d = coef + x * x * x;
SPLINE[i] = d;
}
SPLINE[NB_SAMPLES] = 1.0f;
// This controls the viscous fluid effect (how much of it)
sViscousFluidScale = 8.0f;
// must be set to 1.0 (used in viscousFluid())
sViscousFluidNormalize = 1.0f;
sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
}
private static float sViscousFluidScale;
private static float sViscousFluidNormalize;
/**
* Create a Scroller with the default duration and interpolator.
*/
public Scroller(Context context) {
this(context, null);
}
/**
* Create a Scroller with the specified interpolator. If the interpolator is
* null, the default (viscous) interpolator will be used. "Flywheel" behavior will
* be in effect for apps targeting Honeycomb or newer.
*/
public Scroller(Context context, Interpolator interpolator) {
this(context, interpolator,
context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
}
/**
* Create a Scroller with the specified interpolator. If the interpolator is
* null, the default (viscous) interpolator will be used. Specify whether or
* not to support progressive "flywheel" behavior in flinging.
*/
public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
mFinished = true;
mInterpolator = interpolator;
mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
mFlywheel = flywheel;
}
/**
* The amount of friction applied to flings. The default value
* is {@link android.view.ViewConfiguration#getScrollFriction}.
*
* @param friction A scalar dimension-less value representing the coefficient of
* friction.
*/
public final void setFriction(float friction) {
mDeceleration = computeDeceleration(friction);
}
private float computeDeceleration(float friction) {
return SensorManager.GRAVITY_EARTH // g (m/s^2)
* 39.37f // inch/meter
* mPpi // pixels per inch
* friction;
}
/**
*
* Returns whether the scroller has finished scrolling.
*
* @return True if the scroller has finished scrolling, false otherwise.
*/
public final boolean isFinished() {
return mFinished;
}
/**
* Force the finished field to a particular value.
*
* @param finished The new finished value.
*/
public final void forceFinished(boolean finished) {
mFinished = finished;
}
/**
* Returns how long the scroll event will take, in milliseconds.
*
* @return The duration of the scroll in milliseconds.
*/
public final int getDuration() {
return mDuration;
}
/**
* Returns the current X offset in the scroll.
*
* @return The new X offset as an absolute distance from the origin.
*/
public final int getCurrX() {
return mCurrX;
}
/**
* Returns the current Y offset in the scroll.
*
* @return The new Y offset as an absolute distance from the origin.
*/
public final int getCurrY() {
return mCurrY;
}
/**
* Returns the current velocity.
*
* @return The original velocity less the deceleration. Result may be
* negative.
*/
public float getCurrVelocity() {
return mVelocity - mDeceleration * timePassed() / 2000.0f;
}
/**
* Returns the start X offset in the scroll.
*
* @return The start X offset as an absolute distance from the origin.
*/
public final int getStartX() {
return mStartX;
}
/**
* Returns the start Y offset in the scroll.
*
* @return The start Y offset as an absolute distance from the origin.
*/
public final int getStartY() {
return mStartY;
}
/**
* Returns where the scroll will end. Valid only for "fling" scrolls.
*
* @return The final X offset as an absolute distance from the origin.
*/
public final int getFinalX() {
return mFinalX;
}
/**
* Returns where the scroll will end. Valid only for "fling" scrolls.
*
* @return The final Y offset as an absolute distance from the origin.
*/
public final int getFinalY() {
return mFinalY;
}
/**
* Call this when you want to know the new location. If it returns true,
* the animation is not yet finished. loc will be altered to provide the
* new location.
*/
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
float x = timePassed * mDurationReciprocal;
if (mInterpolator == null)
x = viscousFluid(x);
else
x = mInterpolator.getInterpolation(x);
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
case FLING_MODE:
final float t = (float) timePassed / mDuration;
final int index = (int) (NB_SAMPLES * t);
final float tInf = (float) index / NB_SAMPLES;
final float tSup = (float) (index + 1) / NB_SAMPLES;
final float dInf = SPLINE[index];
final float dSup = SPLINE[index + 1];
final float distanceCoef = dInf + (t - tInf) / (tSup - tInf) * (dSup - dInf);
mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
// Pin to mMinX <= mCurrX <= mMaxX
mCurrX = Math.min(mCurrX, mMaxX);
mCurrX = Math.max(mCurrX, mMinX);
mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
// Pin to mMinY <= mCurrY <= mMaxY
mCurrY = Math.min(mCurrY, mMaxY);
mCurrY = Math.max(mCurrY, mMinY);
if (mCurrX == mFinalX && mCurrY == mFinalY) {
mFinished = true;
}
break;
}
} else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
/**
* Start scrolling by providing a starting point and the distance to travel.
* The scroll will use the default value of 250 milliseconds for the
* duration.
*
* @param startX Starting horizontal scroll offset in pixels. Positive
* numbers will scroll the content to the left.
* @param startY Starting vertical scroll offset in pixels. Positive numbers
* will scroll the content up.
* @param dx Horizontal distance to travel. Positive numbers will scroll the
* content to the left.
* @param dy Vertical distance to travel. Positive numbers will scroll the
* content up.
*/
public void startScroll(int startX, int startY, int dx, int dy) {
startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
}
/**
* Start scrolling by providing a starting point and the distance to travel.
*
* @param startX Starting horizontal scroll offset in pixels. Positive
* numbers will scroll the content to the left.
* @param startY Starting vertical scroll offset in pixels. Positive numbers
* will scroll the content up.
* @param dx Horizontal distance to travel. Positive numbers will scroll the
* content to the left.
* @param dy Vertical distance to travel. Positive numbers will scroll the
* content up.
* @param duration Duration of the scroll in milliseconds.
*/
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
}
/**
* Start scrolling based on a fling gesture. The distance travelled will
* depend on the initial velocity of the fling.
*
* @param startX Starting point of the scroll (X)
* @param startY Starting point of the scroll (Y)
* @param velocityX Initial velocity of the fling (X) measured in pixels per
* second.
* @param velocityY Initial velocity of the fling (Y) measured in pixels per
* second
* @param minX Minimum X value. The scroller will not scroll past this
* point.
* @param maxX Maximum X value. The scroller will not scroll past this
* point.
* @param minY Minimum Y value. The scroller will not scroll past this
* point.
* @param maxY Maximum Y value. The scroller will not scroll past this
* point.
*/
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
// Continue a scroll or fling in progress
if (mFlywheel && !mFinished) {
float oldVel = getCurrVelocity();
float dx = (float) (mFinalX - mStartX);
float dy = (float) (mFinalY - mStartY);
float hyp = (float) Math.sqrt(dx * dx + dy * dy);
float ndx = dx / hyp;
float ndy = dy / hyp;
float oldVelocityX = ndx * oldVel;
float oldVelocityY = ndy * oldVel;
if (Math.signum(velocityX) == Math.signum(oldVelocityX)
&& Math.signum(velocityY) == Math.signum(oldVelocityY)) {
velocityX += oldVelocityX;
velocityY += oldVelocityY;
}
}
mMode = FLING_MODE;
mFinished = false;
float velocity = (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY);
mVelocity = velocity;
final double l = Math.log(START_TENSION * velocity / ALPHA);
mDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0)));
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
int totalDistance =
(int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
mMinX = minX;
mMaxX = maxX;
mMinY = minY;
mMaxY = maxY;
mFinalX = startX + Math.round(totalDistance * coeffX);
// Pin to mMinX <= mFinalX <= mMaxX
mFinalX = Math.min(mFinalX, mMaxX);
mFinalX = Math.max(mFinalX, mMinX);
mFinalY = startY + Math.round(totalDistance * coeffY);
// Pin to mMinY <= mFinalY <= mMaxY
mFinalY = Math.min(mFinalY, mMaxY);
mFinalY = Math.max(mFinalY, mMinY);
}
static float viscousFluid(float x) {
x *= sViscousFluidScale;
if (x < 1.0f) {
x -= (1.0f - (float) Math.exp(-x));
} else {
float start = 0.36787944117f; // 1/e == exp(-1)
x = 1.0f - (float) Math.exp(1.0f - x);
x = start + x * (1.0f - start);
}
x *= sViscousFluidNormalize;
return x;
}
/**
* Stops the animation. Contrary to {@link #forceFinished(boolean)},
* aborting the animating cause the scroller to move to the final x and y
* position
*
* @see #forceFinished(boolean)
*/
public void abortAnimation() {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
/**
* Extend the scroll animation. This allows a running animation to scroll
* further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
*
* @param extend Additional time to scroll in milliseconds.
* @see #setFinalX(int)
* @see #setFinalY(int)
*/
public void extendDuration(int extend) {
int passed = timePassed();
mDuration = passed + extend;
mDurationReciprocal = 1.0f / mDuration;
mFinished = false;
}
/**
* Returns the time elapsed since the beginning of the scrolling.
*
* @return The elapsed time in milliseconds.
*/
public int timePassed() {
return (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
}
/**
* Sets the final position (X) for this scroller.
*
* @param newX The new X offset as an absolute distance from the origin.
* @see #extendDuration(int)
* @see #setFinalY(int)
*/
public void setFinalX(int newX) {
mFinalX = newX;
mDeltaX = mFinalX - mStartX;
mFinished = false;
}
/**
* Sets the final position (Y) for this scroller.
*
* @param newY The new Y offset as an absolute distance from the origin.
* @see #extendDuration(int)
* @see #setFinalX(int)
*/
public void setFinalY(int newY) {
mFinalY = newY;
mDeltaY = mFinalY - mStartY;
mFinished = false;
}
/**
* @hide
*/
public boolean isScrollingInDirection(float xvel, float yvel) {
return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX)
&& Math.signum(yvel) == Math.signum(mFinalY - mStartY);
}
}

View File

@ -1,15 +0,0 @@
package net.simonvt.menudrawer;
import android.view.animation.Interpolator;
/**
* Interpolator which, when drawn from 0 to 1, looks like half a sine-wave. Used for smoother opening/closing when
* peeking at the drawer.
*/
class SinusoidalInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
return (float) (0.5f + 0.5f * Math.sin(input * Math.PI - Math.PI / 2.f));
}
}

View File

@ -1,187 +0,0 @@
package net.simonvt.menudrawer;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
public class SlideDrawable extends Drawable implements Drawable.Callback {
private Drawable mWrapped;
private float mOffset;
private final Rect mTmpRect = new Rect();
private boolean mIsRtl;
public SlideDrawable(Drawable wrapped) {
mWrapped = wrapped;
}
public void setOffset(float offset) {
mOffset = offset;
invalidateSelf();
}
public float getOffset() {
return mOffset;
}
void setIsRtl(boolean isRtl) {
mIsRtl = isRtl;
invalidateSelf();
}
@Override
public void draw(Canvas canvas) {
mWrapped.copyBounds(mTmpRect);
canvas.save();
if (mIsRtl) {
canvas.translate(1.f / 3 * mTmpRect.width() * mOffset, 0);
} else {
canvas.translate(1.f / 3 * mTmpRect.width() * -mOffset, 0);
}
mWrapped.draw(canvas);
canvas.restore();
}
@Override
public void setChangingConfigurations(int configs) {
mWrapped.setChangingConfigurations(configs);
}
@Override
public int getChangingConfigurations() {
return mWrapped.getChangingConfigurations();
}
@Override
public void setDither(boolean dither) {
mWrapped.setDither(dither);
}
@Override
public void setFilterBitmap(boolean filter) {
mWrapped.setFilterBitmap(filter);
}
@Override
public void setAlpha(int alpha) {
mWrapped.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mWrapped.setColorFilter(cf);
}
@Override
public void setColorFilter(int color, PorterDuff.Mode mode) {
mWrapped.setColorFilter(color, mode);
}
@Override
public void clearColorFilter() {
mWrapped.clearColorFilter();
}
@Override
public boolean isStateful() {
return mWrapped.isStateful();
}
@Override
public boolean setState(int[] stateSet) {
return mWrapped.setState(stateSet);
}
@Override
public int[] getState() {
return mWrapped.getState();
}
@Override
public Drawable getCurrent() {
return mWrapped.getCurrent();
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
return super.setVisible(visible, restart);
}
@Override
public int getOpacity() {
return mWrapped.getOpacity();
}
@Override
public Region getTransparentRegion() {
return mWrapped.getTransparentRegion();
}
@Override
protected boolean onStateChange(int[] state) {
mWrapped.setState(state);
return super.onStateChange(state);
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mWrapped.setBounds(bounds);
}
@Override
public int getIntrinsicWidth() {
return mWrapped.getIntrinsicWidth();
}
@Override
public int getIntrinsicHeight() {
return mWrapped.getIntrinsicHeight();
}
@Override
public int getMinimumWidth() {
return mWrapped.getMinimumWidth();
}
@Override
public int getMinimumHeight() {
return mWrapped.getMinimumHeight();
}
@Override
public boolean getPadding(Rect padding) {
return mWrapped.getPadding(padding);
}
@Override
public ConstantState getConstantState() {
return super.getConstantState();
}
@Override
public void invalidateDrawable(Drawable who) {
if (who == mWrapped) {
invalidateSelf();
}
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
if (who == mWrapped) {
scheduleSelf(what, when);
}
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
if (who == mWrapped) {
unscheduleSelf(what);
}
}
}

View File

@ -1,707 +0,0 @@
package net.simonvt.menudrawer;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
public class SlidingDrawer extends DraggableDrawer {
private static final String TAG = "OverlayDrawer";
SlidingDrawer(Activity activity, int dragMode) {
super(activity, dragMode);
}
public SlidingDrawer(Context context) {
super(context);
}
public SlidingDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlidingDrawer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {
super.initDrawer(context, attrs, defStyle);
super.addView(mMenuContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
super.addView(mContentContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
}
@Override
public void openMenu(boolean animate) {
int animateTo = 0;
switch (getPosition()) {
case LEFT:
case TOP:
animateTo = mMenuSize;
break;
case RIGHT:
case BOTTOM:
animateTo = -mMenuSize;
break;
}
animateOffsetTo(animateTo, 0, animate);
}
@Override
public void closeMenu(boolean animate) {
animateOffsetTo(0, 0, animate);
}
@Override
protected void onOffsetPixelsChanged(int offsetPixels) {
if (USE_TRANSLATIONS) {
switch (getPosition()) {
case TOP:
case BOTTOM:
mContentContainer.setTranslationY(offsetPixels);
break;
default:
mContentContainer.setTranslationX(offsetPixels);
break;
}
} else {
switch (getPosition()) {
case TOP:
case BOTTOM:
mContentContainer.offsetTopAndBottom(offsetPixels - mContentContainer.getTop());
break;
default:
mContentContainer.offsetLeftAndRight(offsetPixels - mContentContainer.getLeft());
break;
}
}
offsetMenu(offsetPixels);
invalidate();
}
@Override
protected void initPeekScroller() {
switch (getPosition()) {
case RIGHT:
case BOTTOM: {
final int dx = -mMenuSize / 3;
mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);
break;
}
default: {
final int dx = mMenuSize / 3;
mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);
break;
}
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
onOffsetPixelsChanged((int) mOffsetPixels);
}
@Override
protected void drawOverlay(Canvas canvas) {
final int width = getWidth();
final int height = getHeight();
final int offsetPixels = (int) mOffsetPixels;
final float openRatio = Math.abs(mOffsetPixels) / mMenuSize;
switch (getPosition()) {
case LEFT:
mMenuOverlay.setBounds(0, 0, offsetPixels, height);
break;
case RIGHT:
mMenuOverlay.setBounds(width + offsetPixels, 0, width, height);
break;
case TOP:
mMenuOverlay.setBounds(0, 0, width, offsetPixels);
break;
case BOTTOM:
mMenuOverlay.setBounds(0, height + offsetPixels, width, height);
break;
}
mMenuOverlay.setAlpha((int) (MAX_MENU_OVERLAY_ALPHA * (1.f - openRatio)));
mMenuOverlay.draw(canvas);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int width = r - l;
final int height = b - t;
if (USE_TRANSLATIONS) {
mContentContainer.layout(0, 0, width, height);
} else {
final int offsetPixels = (int) mOffsetPixels;
if (getPosition() == Position.LEFT || getPosition() == Position.RIGHT) {
mContentContainer.layout(offsetPixels, 0, width + offsetPixels, height);
} else {
mContentContainer.layout(0, offsetPixels, width, height + offsetPixels);
}
}
switch (getPosition()) {
case LEFT:
mMenuContainer.layout(0, 0, mMenuSize, height);
break;
case RIGHT:
mMenuContainer.layout(width - mMenuSize, 0, width, height);
break;
case TOP:
mMenuContainer.layout(0, 0, width, mMenuSize);
break;
case BOTTOM:
mMenuContainer.layout(0, height - mMenuSize, width, height);
break;
}
}
/**
* Offsets the menu relative to its original position based on the position of the content.
*
* @param offsetPixels The number of pixels the content if offset.
*/
private void offsetMenu(int offsetPixels) {
if (!mOffsetMenu || mMenuSize == 0) {
return;
}
final int width = getWidth();
final int height = getHeight();
final int menuSize = mMenuSize;
final int sign = (int) (mOffsetPixels / Math.abs(mOffsetPixels));
final float openRatio = Math.abs(mOffsetPixels) / menuSize;
final int offset = (int) (-0.25f * ((1.0f - openRatio) * menuSize) * sign);
switch (getPosition()) {
case LEFT: {
if (USE_TRANSLATIONS) {
if (offsetPixels > 0) {
mMenuContainer.setTranslationX(offset);
} else {
mMenuContainer.setTranslationX(-menuSize);
}
} else {
mMenuContainer.offsetLeftAndRight(offset - mMenuContainer.getLeft());
mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);
}
break;
}
case RIGHT: {
if (USE_TRANSLATIONS) {
if (offsetPixels != 0) {
mMenuContainer.setTranslationX(offset);
} else {
mMenuContainer.setTranslationX(menuSize);
}
} else {
final int oldOffset = mMenuContainer.getRight() - width;
final int offsetBy = offset - oldOffset;
mMenuContainer.offsetLeftAndRight(offsetBy);
mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);
}
break;
}
case TOP: {
if (USE_TRANSLATIONS) {
if (offsetPixels > 0) {
mMenuContainer.setTranslationY(offset);
} else {
mMenuContainer.setTranslationY(-menuSize);
}
} else {
mMenuContainer.offsetTopAndBottom(offset - mMenuContainer.getTop());
mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);
}
break;
}
case BOTTOM: {
if (USE_TRANSLATIONS) {
if (offsetPixels != 0) {
mMenuContainer.setTranslationY(offset);
} else {
mMenuContainer.setTranslationY(menuSize);
}
} else {
final int oldOffset = mMenuContainer.getBottom() - height;
final int offsetBy = offset - oldOffset;
mMenuContainer.offsetTopAndBottom(offsetBy);
mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);
}
break;
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
throw new IllegalStateException("Must measure with an exact size");
}
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int height = MeasureSpec.getSize(heightMeasureSpec);
if (mOffsetPixels == -1) openMenu(false);
int menuWidthMeasureSpec;
int menuHeightMeasureSpec;
switch (getPosition()) {
case TOP:
case BOTTOM:
menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);
menuHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, mMenuSize);
break;
default:
// LEFT/RIGHT
menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, mMenuSize);
menuHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);
}
mMenuContainer.measure(menuWidthMeasureSpec, menuHeightMeasureSpec);
final int contentWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);
final int contentHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);
mContentContainer.measure(contentWidthMeasureSpec, contentHeightMeasureSpec);
setMeasuredDimension(width, height);
updateTouchAreaSize();
}
private boolean isContentTouch(int x, int y) {
boolean contentTouch = false;
switch (getPosition()) {
case LEFT:
contentTouch = ViewHelper.getLeft(mContentContainer) < x;
break;
case RIGHT:
contentTouch = ViewHelper.getRight(mContentContainer) > x;
break;
case TOP:
contentTouch = ViewHelper.getTop(mContentContainer) < y;
break;
case BOTTOM:
contentTouch = ViewHelper.getBottom(mContentContainer) > y;
break;
}
return contentTouch;
}
protected boolean onDownAllowDrag(int x, int y) {
switch (getPosition()) {
case LEFT:
return (!mMenuVisible && mInitialMotionX <= mTouchSize)
|| (mMenuVisible && mInitialMotionX >= mOffsetPixels);
case RIGHT:
final int width = getWidth();
final int initialMotionX = (int) mInitialMotionX;
return (!mMenuVisible && initialMotionX >= width - mTouchSize)
|| (mMenuVisible && initialMotionX <= width + mOffsetPixels);
case TOP:
return (!mMenuVisible && mInitialMotionY <= mTouchSize)
|| (mMenuVisible && mInitialMotionY >= mOffsetPixels);
case BOTTOM:
final int height = getHeight();
return (!mMenuVisible && mInitialMotionY >= height - mTouchSize)
|| (mMenuVisible && mInitialMotionY <= height + mOffsetPixels);
}
return false;
}
protected boolean onMoveAllowDrag(int x, int y, float dx, float dy) {
switch (getPosition()) {
case LEFT:
return (!mMenuVisible && mInitialMotionX <= mTouchSize && (dx > 0))
|| (mMenuVisible && x >= mOffsetPixels);
case RIGHT:
final int width = getWidth();
return (!mMenuVisible && mInitialMotionX >= width - mTouchSize && (dx < 0))
|| (mMenuVisible && x <= width + mOffsetPixels);
case TOP:
return (!mMenuVisible && mInitialMotionY <= mTouchSize && (dy > 0))
|| (mMenuVisible && y >= mOffsetPixels);
case BOTTOM:
final int height = getHeight();
return (!mMenuVisible && mInitialMotionY >= height - mTouchSize && (dy < 0))
|| (mMenuVisible && y <= height + mOffsetPixels);
}
return false;
}
protected void onMoveEvent(float dx, float dy) {
switch (getPosition()) {
case LEFT:
setOffsetPixels(Math.min(Math.max(mOffsetPixels + dx, 0), mMenuSize));
break;
case RIGHT:
setOffsetPixels(Math.max(Math.min(mOffsetPixels + dx, 0), -mMenuSize));
break;
case TOP:
setOffsetPixels(Math.min(Math.max(mOffsetPixels + dy, 0), mMenuSize));
break;
case BOTTOM:
setOffsetPixels(Math.max(Math.min(mOffsetPixels + dy, 0), -mMenuSize));
break;
}
}
protected void onUpEvent(int x, int y) {
final int offsetPixels = (int) mOffsetPixels;
switch (getPosition()) {
case LEFT: {
if (mIsDragging) {
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
final int initialVelocity = (int) getXVelocity(mVelocityTracker);
mLastMotionX = x;
animateOffsetTo(initialVelocity > 0 ? mMenuSize : 0, initialVelocity, true);
// Close the menu when content is clicked while the menu is visible.
} else if (mMenuVisible && x > offsetPixels) {
closeMenu();
}
break;
}
case TOP: {
if (mIsDragging) {
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
final int initialVelocity = (int) getYVelocity(mVelocityTracker);
mLastMotionY = y;
animateOffsetTo(initialVelocity > 0 ? mMenuSize : 0, initialVelocity, true);
// Close the menu when content is clicked while the menu is visible.
} else if (mMenuVisible && y > offsetPixels) {
closeMenu();
}
break;
}
case RIGHT: {
final int width = getWidth();
if (mIsDragging) {
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
final int initialVelocity = (int) getXVelocity(mVelocityTracker);
mLastMotionX = x;
animateOffsetTo(initialVelocity > 0 ? 0 : -mMenuSize, initialVelocity, true);
// Close the menu when content is clicked while the menu is visible.
} else if (mMenuVisible && x < width + offsetPixels) {
closeMenu();
}
break;
}
case BOTTOM: {
if (mIsDragging) {
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
final int initialVelocity = (int) getYVelocity(mVelocityTracker);
mLastMotionY = y;
animateOffsetTo(initialVelocity < 0 ? -mMenuSize : 0, initialVelocity, true);
// Close the menu when content is clicked while the menu is visible.
} else if (mMenuVisible && y < getHeight() + offsetPixels) {
closeMenu();
}
break;
}
}
}
protected boolean checkTouchSlop(float dx, float dy) {
switch (getPosition()) {
case TOP:
case BOTTOM:
return Math.abs(dy) > mTouchSlop && Math.abs(dy) > Math.abs(dx);
default:
return Math.abs(dx) > mTouchSlop && Math.abs(dx) > Math.abs(dy);
}
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
mActivePointerId = INVALID_POINTER;
mIsDragging = false;
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
if (Math.abs(mOffsetPixels) > mMenuSize / 2) {
openMenu();
} else {
closeMenu();
}
return false;
}
if (action == MotionEvent.ACTION_DOWN && mMenuVisible && isCloseEnough()) {
setOffsetPixels(0);
stopAnimation();
endPeek();
setDrawerState(STATE_CLOSED);
mIsDragging = false;
}
// Always intercept events over the content while menu is visible.
if (mMenuVisible) {
int index = 0;
if (mActivePointerId != INVALID_POINTER) {
index = ev.findPointerIndex(mActivePointerId);
index = index == -1 ? 0 : index;
}
final int x = (int) ev.getX(index);
final int y = (int) ev.getY(index);
if (isContentTouch(x, y)) {
return true;
}
}
if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {
return false;
}
if (action != MotionEvent.ACTION_DOWN && mIsDragging) {
return true;
}
switch (action) {
case MotionEvent.ACTION_DOWN: {
mLastMotionX = mInitialMotionX = ev.getX();
mLastMotionY = mInitialMotionY = ev.getY();
final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);
mActivePointerId = ev.getPointerId(0);
if (allowDrag) {
setDrawerState(mMenuVisible ? STATE_OPEN : STATE_CLOSED);
stopAnimation();
endPeek();
mIsDragging = false;
}
break;
}
case MotionEvent.ACTION_MOVE: {
final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER) {
// If we don't have a valid id, the touch down wasn't on content.
break;
}
final int pointerIndex = ev.findPointerIndex(activePointerId);
if (pointerIndex == -1) {
mIsDragging = false;
mActivePointerId = INVALID_POINTER;
endDrag();
closeMenu(true);
return false;
}
final float x = ev.getX(pointerIndex);
final float dx = x - mLastMotionX;
final float y = ev.getY(pointerIndex);
final float dy = y - mLastMotionY;
if (checkTouchSlop(dx, dy)) {
if (mOnInterceptMoveEventListener != null && (mTouchMode == TOUCH_MODE_FULLSCREEN || mMenuVisible)
&& canChildrenScroll((int) dx, (int) dy, (int) x, (int) y)) {
endDrag(); // Release the velocity tracker
requestDisallowInterceptTouchEvent(true);
return false;
}
final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);
if (allowDrag) {
setDrawerState(STATE_DRAGGING);
mIsDragging = true;
mLastMotionX = x;
mLastMotionY = y;
}
}
break;
}
case MotionEvent.ACTION_POINTER_UP:
onPointerUp(ev);
mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));
break;
}
if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(ev);
return mIsDragging;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {
return false;
}
final int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(ev);
switch (action) {
case MotionEvent.ACTION_DOWN: {
mLastMotionX = mInitialMotionX = ev.getX();
mLastMotionY = mInitialMotionY = ev.getY();
final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);
mActivePointerId = ev.getPointerId(0);
if (allowDrag) {
stopAnimation();
endPeek();
startLayerTranslation();
}
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
mIsDragging = false;
mActivePointerId = INVALID_POINTER;
endDrag();
closeMenu(true);
return false;
}
if (!mIsDragging) {
final float x = ev.getX(pointerIndex);
final float dx = x - mLastMotionX;
final float y = ev.getY(pointerIndex);
final float dy = y - mLastMotionY;
if (checkTouchSlop(dx, dy)) {
final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);
if (allowDrag) {
setDrawerState(STATE_DRAGGING);
mIsDragging = true;
mLastMotionX = x;
mLastMotionY = y;
} else {
mInitialMotionX = x;
mInitialMotionY = y;
}
}
}
if (mIsDragging) {
startLayerTranslation();
final float x = ev.getX(pointerIndex);
final float dx = x - mLastMotionX;
final float y = ev.getY(pointerIndex);
final float dy = y - mLastMotionY;
mLastMotionX = x;
mLastMotionY = y;
onMoveEvent(dx, dy);
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
int index = ev.findPointerIndex(mActivePointerId);
index = index == -1 ? 0 : index;
final int x = (int) ev.getX(index);
final int y = (int) ev.getY(index);
onUpEvent(x, y);
mActivePointerId = INVALID_POINTER;
mIsDragging = false;
break;
}
case MotionEvent.ACTION_POINTER_DOWN:
final int index = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
mLastMotionX = ev.getX(index);
mLastMotionY = ev.getY(index);
mActivePointerId = ev.getPointerId(index);
break;
case MotionEvent.ACTION_POINTER_UP:
onPointerUp(ev);
mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));
break;
}
return true;
}
private void onPointerUp(MotionEvent ev) {
final int pointerIndex = ev.getActionIndex();
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastMotionX = ev.getX(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
if (mVelocityTracker != null) {
mVelocityTracker.clear();
}
}
}
}

View File

@ -1,12 +0,0 @@
package net.simonvt.menudrawer;
import android.view.animation.Interpolator;
class SmoothInterpolator implements Interpolator {
@Override
public float getInterpolation(float t) {
t -= 1.0f;
return t * t * t * t * t + 1.0f;
}
}

View File

@ -1,218 +0,0 @@
package net.simonvt.menudrawer;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
public class StaticDrawer extends MenuDrawer {
public StaticDrawer(Context context) {
super(context);
}
public StaticDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StaticDrawer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {
super.initDrawer(context, attrs, defStyle);
super.addView(mMenuContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
super.addView(mContentContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mIsStatic = true;
}
@Override
protected void drawOverlay(Canvas canvas) {
// NO-OP
}
@Override
protected void onOffsetPixelsChanged(int offsetPixels) {
// NO-OP
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int width = r - l;
final int height = b - t;
switch (getPosition()) {
case LEFT:
mMenuContainer.layout(0, 0, mMenuSize, height);
mContentContainer.layout(mMenuSize, 0, width, height);
break;
case RIGHT:
mMenuContainer.layout(width - mMenuSize, 0, width, height);
mContentContainer.layout(0, 0, width - mMenuSize, height);
break;
case TOP:
mMenuContainer.layout(0, 0, width, mMenuSize);
mContentContainer.layout(0, mMenuSize, width, height);
break;
case BOTTOM:
mMenuContainer.layout(0, height - mMenuSize, width, height);
mContentContainer.layout(0, 0, width, height - mMenuSize);
break;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
throw new IllegalStateException("Must measure with an exact size");
}
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int height = MeasureSpec.getSize(heightMeasureSpec);
switch (getPosition()) {
case LEFT:
case RIGHT: {
final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
final int menuWidth = mMenuSize;
final int menuWidthMeasureSpec = MeasureSpec.makeMeasureSpec(menuWidth, MeasureSpec.EXACTLY);
final int contentWidth = width - menuWidth;
final int contentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY);
mContentContainer.measure(contentWidthMeasureSpec, childHeightMeasureSpec);
mMenuContainer.measure(menuWidthMeasureSpec, childHeightMeasureSpec);
break;
}
case TOP:
case BOTTOM: {
final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
final int menuHeight = mMenuSize;
final int menuHeightMeasureSpec = MeasureSpec.makeMeasureSpec(menuHeight, MeasureSpec.EXACTLY);
final int contentHeight = height - menuHeight;
final int contentHeightMeasureSpec = MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY);
mContentContainer.measure(childWidthMeasureSpec, contentHeightMeasureSpec);
mMenuContainer.measure(childWidthMeasureSpec, menuHeightMeasureSpec);
break;
}
}
setMeasuredDimension(width, height);
}
@Override
public void toggleMenu(boolean animate) {
// NO-OP
}
@Override
public void openMenu(boolean animate) {
// NO-OP
}
@Override
public void closeMenu(boolean animate) {
// NO-OP
}
@Override
public boolean isMenuVisible() {
return true;
}
@Override
public void setMenuSize(int size) {
mMenuSize = size;
requestLayout();
invalidate();
}
@Override
public void setOffsetMenuEnabled(boolean offsetMenu) {
// NO-OP
}
@Override
public boolean getOffsetMenuEnabled() {
return false;
}
@Override
public void peekDrawer() {
// NO-OP
}
@Override
public void peekDrawer(long delay) {
// NO-OP
}
@Override
public void peekDrawer(long startDelay, long delay) {
// NO-OP
}
@Override
public void setHardwareLayerEnabled(boolean enabled) {
// NO-OP
}
@Override
public int getTouchMode() {
return TOUCH_MODE_NONE;
}
@Override
public void setTouchMode(int mode) {
// NO-OP
}
@Override
public void setTouchBezelSize(int size) {
// NO-OP
}
@Override
public int getTouchBezelSize() {
return 0;
}
@Override
public void setSlideDrawable(int drawableRes) {
// NO-OP
}
@Override
public void setSlideDrawable(Drawable drawable) {
// NO-OP
}
@Override
public void setupUpIndicator(Activity activity) {
// NO-OP
}
@Override
public void setDrawerIndicatorEnabled(boolean enabled) {
// NO-OP
}
@Override
public boolean isDrawerIndicatorEnabled() {
return false;
}
}

View File

@ -1,50 +0,0 @@
package net.simonvt.menudrawer;
import android.os.Build;
import android.view.View;
final class ViewHelper {
private ViewHelper() {
}
public static int getLeft(View v) {
if (MenuDrawer.USE_TRANSLATIONS) {
return (int) (v.getLeft() + v.getTranslationX());
}
return v.getLeft();
}
public static int getTop(View v) {
if (MenuDrawer.USE_TRANSLATIONS) {
return (int) (v.getTop() + v.getTranslationY());
}
return v.getTop();
}
public static int getRight(View v) {
if (MenuDrawer.USE_TRANSLATIONS) {
return (int) (v.getRight() + v.getTranslationX());
}
return v.getRight();
}
public static int getBottom(View v) {
if (MenuDrawer.USE_TRANSLATIONS) {
return (int) (v.getBottom() + v.getTranslationY());
}
return v.getBottom();
}
public static int getLayoutDirection(View v) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return v.getLayoutDirection();
}
return View.LAYOUT_DIRECTION_LTR;
}
}

View File

@ -1,83 +0,0 @@
package net.simonvt.menudrawer.compat;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import java.lang.reflect.Method;
public final class ActionBarHelper {
private static final String TAG = "ActionBarHelper";
static final boolean DEBUG = false;
private Activity mActivity;
private Object mIndicatorInfo;
private boolean mUsesCompat;
public ActionBarHelper(Activity activity) {
mActivity = activity;
try {
Class clazz = activity.getClass();
Method m = clazz.getMethod("getSupportActionBar");
mUsesCompat = true;
} catch (NoSuchMethodException e) {
if (DEBUG) {
Log.e(TAG,
"Activity " + activity.getClass().getSimpleName() + " does not use a compatibility action bar",
e);
}
}
mIndicatorInfo = getIndicatorInfo();
}
private Object getIndicatorInfo() {
if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return ActionBarHelperCompat.getIndicatorInfo(mActivity);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
return ActionBarHelperNative.getIndicatorInfo(mActivity);
}
return null;
}
public void setActionBarUpIndicator(Drawable drawable, int contentDesc) {
if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ActionBarHelperCompat.setActionBarUpIndicator(mIndicatorInfo, mActivity, drawable, contentDesc);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ActionBarHelperNative.setActionBarUpIndicator(mIndicatorInfo, mActivity, drawable, contentDesc);
}
}
public void setActionBarDescription(int contentDesc) {
if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ActionBarHelperCompat.setActionBarDescription(mIndicatorInfo, mActivity, contentDesc);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ActionBarHelperNative.setActionBarDescription(mIndicatorInfo, mActivity, contentDesc);
}
}
public Drawable getThemeUpIndicator() {
if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return ActionBarHelperCompat.getThemeUpIndicator(mIndicatorInfo);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
return ActionBarHelperNative.getThemeUpIndicator(mIndicatorInfo, mActivity);
}
return null;
}
public void setDisplayShowHomeAsUpEnabled(boolean enabled) {
if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ActionBarHelperCompat.setDisplayHomeAsUpEnabled(mIndicatorInfo, enabled);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ActionBarHelperNative.setDisplayHomeAsUpEnabled(mActivity, enabled);
}
}
}

View File

@ -1,107 +0,0 @@
package net.simonvt.menudrawer.compat;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.lang.reflect.Method;
final class ActionBarHelperCompat {
private static final String TAG = "ActionBarHelperCompat";
private ActionBarHelperCompat() {
}
public static void setActionBarUpIndicator(Object info, Activity activity, Drawable drawable, int contentDescRes) {
final SetIndicatorInfo sii = (SetIndicatorInfo) info;
if (sii.mUpIndicatorView != null) {
sii.mUpIndicatorView.setImageDrawable(drawable);
final String contentDescription = contentDescRes == 0 ? null : activity.getString(contentDescRes);
sii.mUpIndicatorView.setContentDescription(contentDescription);
}
}
public static void setActionBarDescription(Object info, Activity activity, int contentDescRes) {
final SetIndicatorInfo sii = (SetIndicatorInfo) info;
if (sii.mUpIndicatorView != null) {
final String contentDescription = contentDescRes == 0 ? null : activity.getString(contentDescRes);
sii.mUpIndicatorView.setContentDescription(contentDescription);
}
}
public static Drawable getThemeUpIndicator(Object info) {
final SetIndicatorInfo sii = (SetIndicatorInfo) info;
if (sii.mUpIndicatorView != null) {
return sii.mUpIndicatorView.getDrawable();
}
return null;
}
public static Object getIndicatorInfo(Activity activity) {
return new SetIndicatorInfo(activity);
}
public static void setDisplayHomeAsUpEnabled(Object info, boolean enabled) {
final SetIndicatorInfo sii = (SetIndicatorInfo) info;
if (sii.mHomeAsUpEnabled != null) {
try {
sii.mHomeAsUpEnabled.invoke(sii.mActionBar, enabled);
} catch (Throwable t) {
if (ActionBarHelper.DEBUG) {
Log.e(TAG, "Unable to call setHomeAsUpEnabled", t);
}
}
}
}
private static class SetIndicatorInfo {
public ImageView mUpIndicatorView;
public Object mActionBar;
public Method mHomeAsUpEnabled;
SetIndicatorInfo(Activity activity) {
try {
String appPackage = activity.getPackageName();
try {
// Attempt to find ActionBarSherlock up indicator
final int homeId = activity.getResources().getIdentifier("abs__home", "id", appPackage);
View v = activity.findViewById(homeId);
ViewGroup parent = (ViewGroup) v.getParent();
final int upId = activity.getResources().getIdentifier("abs__up", "id", appPackage);
mUpIndicatorView = (ImageView) parent.findViewById(upId);
} catch (Throwable t) {
if (ActionBarHelper.DEBUG) {
Log.e(TAG, "ABS action bar not found", t);
}
}
if (mUpIndicatorView == null) {
// Attempt to find AppCompat up indicator
final int homeId = activity.getResources().getIdentifier("home", "id", appPackage);
View v = activity.findViewById(homeId);
ViewGroup parent = (ViewGroup) v.getParent();
final int upId = activity.getResources().getIdentifier("up", "id", appPackage);
mUpIndicatorView = (ImageView) parent.findViewById(upId);
}
Class supportActivity = activity.getClass();
Method getActionBar = supportActivity.getMethod("getSupportActionBar");
mActionBar = getActionBar.invoke(activity, (Object)null);
Class supportActionBar = mActionBar.getClass();
mHomeAsUpEnabled = supportActionBar.getMethod("setDisplayHomeAsUpEnabled", Boolean.TYPE);
} catch (Throwable t) {
if (ActionBarHelper.DEBUG) {
Log.e(TAG, "Unable to init SetIndicatorInfo for ABS", t);
}
}
}
}
}

View File

@ -1,114 +0,0 @@
package net.simonvt.menudrawer.compat;
import android.app.ActionBar;
import android.app.Activity;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.lang.reflect.Method;
final class ActionBarHelperNative {
private static final String TAG = "ActionBarHelperNative";
private ActionBarHelperNative() {
}
private static final int[] THEME_ATTRS = new int[] {
android.R.attr.homeAsUpIndicator
};
public static void setActionBarUpIndicator(Object info, Activity activity, Drawable drawable, int contentDescRes) {
final SetIndicatorInfo sii = (SetIndicatorInfo) info;
if (sii.setHomeAsUpIndicator != null) {
try {
final ActionBar actionBar = activity.getActionBar();
sii.setHomeAsUpIndicator.invoke(actionBar, drawable);
sii.setHomeActionContentDescription.invoke(actionBar, contentDescRes);
} catch (Throwable t) {
if (ActionBarHelper.DEBUG) Log.e(TAG, "Couldn't set home-as-up indicator via JB-MR2 API", t);
}
} else if (sii.upIndicatorView != null) {
sii.upIndicatorView.setImageDrawable(drawable);
} else {
if (ActionBarHelper.DEBUG) Log.e(TAG, "Couldn't set home-as-up indicator");
}
}
public static void setActionBarDescription(Object info, Activity activity, int contentDescRes) {
final SetIndicatorInfo sii = (SetIndicatorInfo) info;
if (sii.setHomeAsUpIndicator != null) {
try {
final ActionBar actionBar = activity.getActionBar();
sii.setHomeActionContentDescription.invoke(actionBar, contentDescRes);
} catch (Throwable t) {
if (ActionBarHelper.DEBUG) Log.e(TAG, "Couldn't set content description via JB-MR2 API", t);
}
}
}
public static Drawable getThemeUpIndicator(Object info, Activity activity) {
final TypedArray a = activity.obtainStyledAttributes(THEME_ATTRS);
final Drawable result = a.getDrawable(0);
a.recycle();
return result;
}
public static Object getIndicatorInfo(Activity activity) {
return new SetIndicatorInfo(activity);
}
public static void setDisplayHomeAsUpEnabled(Activity activity, boolean b) {
ActionBar actionBar = activity.getActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(b);
}
}
private static class SetIndicatorInfo {
public Method setHomeAsUpIndicator;
public Method setHomeActionContentDescription;
public ImageView upIndicatorView;
SetIndicatorInfo(Activity activity) {
try {
setHomeAsUpIndicator = ActionBar.class.getDeclaredMethod("setHomeAsUpIndicator", Drawable.class);
setHomeActionContentDescription = ActionBar.class.getDeclaredMethod(
"setHomeActionContentDescription", Integer.TYPE);
// If we got the method we won't need the stuff below.
return;
} catch (Throwable t) {
// Oh well. We'll use the other mechanism below instead.
}
final View home = activity.findViewById(android.R.id.home);
if (home == null) {
// Action bar doesn't have a known configuration, an OEM messed with things.
return;
}
final ViewGroup parent = (ViewGroup) home.getParent();
final int childCount = parent.getChildCount();
if (childCount != 2) {
// No idea which one will be the right one, an OEM messed with things.
return;
}
final View first = parent.getChildAt(0);
final View second = parent.getChildAt(1);
final View up = first.getId() == android.R.id.home ? second : first;
if (up instanceof ImageView) {
// Jackpot! (Probably...)
upIndicatorView = (ImageView) up;
}
}
}
}

View File

@ -1,65 +0,0 @@
<resources>
<!-- Reference to a style for the menu drawer. -->
<attr name="menuDrawerStyle" format="reference" />
<!-- Styleables used for styling the menu drawer. -->
<declare-styleable name="MenuDrawer">
<!-- Drawable to use for the background of the content. -->
<attr name="mdContentBackground" format="reference" />
<!-- Drawable to use for the background of the menu. -->
<attr name="mdMenuBackground" format="reference" />
<!-- The size of the menu. -->
<attr name="mdMenuSize" format="dimension" />
<!-- Drawable used as indicator for the active view. -->
<attr name="mdActiveIndicator" format="reference" />
<!-- Defines whether the content will have a dropshadow onto the menu. Default is true. -->
<attr name="mdDropShadowEnabled" format="boolean" />
<!-- The size of the drop shadow. Default is 6dp -->
<attr name="mdDropShadowSize" format="dimension" />
<!-- The color of the drop shadow. Default is #FF000000. -->
<attr name="mdDropShadowColor" format="color" />
<!-- Drawable used for the drop shadow. -->
<attr name="mdDropShadow" format="reference" />
<!-- The touch bezel size. -->
<attr name="mdTouchBezelSize" format="dimension" />
<!-- Whether the indicator should be animated between active views. -->
<attr name="mdAllowIndicatorAnimation" format="boolean" />
<!-- The maximum animation duration -->
<attr name="mdMaxAnimationDuration" format="integer" />
<!-- Drawable that replaces the up indicator -->
<attr name="mdSlideDrawable" format="reference" />
<!-- String to use as the up indicators content description when the drawer is open -->
<attr name="mdDrawerOpenUpContentDescription" format="string" />
<!-- String to use as the up indicators content description when the drawer is closed -->
<attr name="mdDrawerClosedUpContentDescription" format="string" />
<!-- Whether an overlay should be drawn as the drawer is opened and closed -->
<attr name="mdDrawOverlay" format="boolean" />
<!-- The position of the drawer -->
<attr name="mdPosition" format="enum">
<enum name="left" value="0" />
<enum name="top" value="1" />
<enum name="right" value="2" />
<enum name="bottom" value="3" />
<enum name="start" value="4" />
<enum name="end" value="5" />
</attr>
</declare-styleable>
</resources>

View File

@ -1,6 +0,0 @@
<resources>
<!-- The default background of the menu. -->
<color name="md__defaultBackground">#FF555555</color>
</resources>

View File

@ -1,24 +0,0 @@
<resources>
<!-- ID used when defining the content layout in XML. -->
<item name="mdContent" type="id" />
<!-- ID used when defining the menu layout in XML. -->
<item name="mdMenu" type="id" />
<!-- The ID of the content container. -->
<item name="md__content" type="id" />
<!-- The ID of the menu container. -->
<item name="md__menu" type="id" />
<!-- The ID of the drawer. -->
<item name="md__drawer" type="id" />
<!-- Used with View#setTag(int) to specify a position for the active view. -->
<item name="mdActiveViewPosition" type="id" />
<item name="md__translationX" type="id" />
<item name="md__translationY" type="id" />
</resources>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="md__drawerOpenIndicatorDesc" tools:ignore="MissingTranslation">Close drawer</string>
<string name="md__drawerClosedIndicatorDesc" tools:ignore="MissingTranslation">Open drawer</string>
</resources>

View File

@ -1,13 +0,0 @@
<resources>
<style name="Widget" />
<!-- Base theme for the menu drawer. -->
<style name="Widget.MenuDrawer">
<item name="mdMenuBackground">@color/md__defaultBackground</item>
<item name="mdContentBackground">?android:attr/windowBackground</item>
<item name="mdDrawerOpenUpContentDescription">@string/md__drawerOpenIndicatorDesc</item>
<item name="mdDrawerClosedUpContentDescription">@string/md__drawerClosedIndicatorDesc</item>
</style>
</resources>

View File

@ -1,8 +0,0 @@
apply from: bootstrap.androidModule
android {
lintOptions {
baselineFile file("lint-baseline.xml")
abortOnError true
}
}

View File

@ -1,326 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="4" by="lint 2.3.3">
<issue
id="LocaleFolder"
message="The locale folder &quot;`he`&quot; should be called &quot;`iw`&quot; instead; see the `java.util.Locale` documentation">
<location
file="src/main/res/values-he"/>
</issue>
<issue
id="OldTargetApi"
message="Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the `android.os.Build.VERSION_CODES` javadoc for details."
errorLine1=" &lt;uses-sdk android:minSdkVersion=&quot;4&quot; android:targetSdkVersion=&quot;16&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="7"
column="41"/>
</issue>
<issue
id="GradleOverrides"
message="This `minSdkVersion` value (`4`) is not used; it is always overridden by the value specified in the Gradle build script (`14`)"
errorLine1=" &lt;uses-sdk android:minSdkVersion=&quot;4&quot; android:targetSdkVersion=&quot;16&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="7"
column="15"/>
</issue>
<issue
id="GradleOverrides"
message="This `targetSdkVersion` value (`16`) is not used; it is always overridden by the value specified in the Gradle build script (`22`)"
errorLine1=" &lt;uses-sdk android:minSdkVersion=&quot;4&quot; android:targetSdkVersion=&quot;16&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="7"
column="41"/>
</issue>
<issue
id="Deprecated"
message="`android:singleLine` is deprecated: Use `maxLines=&quot;1&quot;` instead"
errorLine1=" android:singleLine=&quot;true&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/pull_to_refresh_header_vertical.xml"
line="45"
column="17"/>
</issue>
<issue
id="Deprecated"
message="`android:singleLine` is deprecated: Use `maxLines=&quot;1&quot;` instead"
errorLine1=" android:singleLine=&quot;true&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/pull_to_refresh_header_vertical.xml"
line="53"
column="17"/>
</issue>
<issue
id="MissingTranslation"
message="&quot;`pull_to_refresh_from_bottom_pull_label`&quot; is not translated in &quot;es&quot; (Spanish), &quot;fr&quot; (French), &quot;pt&quot; (Portuguese), &quot;pt-BR&quot; (Portuguese: Brazil)"
errorLine1=" &lt;string name=&quot;pull_to_refresh_from_bottom_pull_label&quot;>@string/pull_to_refresh_pull_label&lt;/string>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/pull_refresh_strings.xml"
line="9"
column="13"/>
</issue>
<issue
id="MissingTranslation"
message="&quot;`pull_to_refresh_from_bottom_release_label`&quot; is not translated in &quot;es&quot; (Spanish), &quot;fr&quot; (French), &quot;pt&quot; (Portuguese), &quot;pt-BR&quot; (Portuguese: Brazil)"
errorLine1=" &lt;string name=&quot;pull_to_refresh_from_bottom_release_label&quot;>@string/pull_to_refresh_release_label&lt;/string>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/pull_refresh_strings.xml"
line="10"
column="13"/>
</issue>
<issue
id="MissingTranslation"
message="&quot;`pull_to_refresh_from_bottom_refreshing_label`&quot; is not translated in &quot;es&quot; (Spanish), &quot;fr&quot; (French), &quot;pt&quot; (Portuguese), &quot;pt-BR&quot; (Portuguese: Brazil)"
errorLine1=" &lt;string name=&quot;pull_to_refresh_from_bottom_refreshing_label&quot;>@string/pull_to_refresh_refreshing_label&lt;/string>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/pull_refresh_strings.xml"
line="11"
column="13"/>
</issue>
<issue
id="AddJavascriptInterface"
message="`WebView.addJavascriptInterface` should not be called with minSdkVersion &lt; 17 for security reasons: JavaScript can use reflection to manipulate application"
errorLine1=" webView.addJavascriptInterface(mJsCallback, JS_INTERFACE_PKG);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/extras/PullToRefreshWebView2.java"
line="90"
column="11"/>
</issue>
<issue
id="JavascriptInterface"
message="None of the methods in the added interface (JsValueCallback) have been annotated with `@android.webkit.JavascriptInterface`; they will not be visible in API 17"
errorLine1=" webView.addJavascriptInterface(mJsCallback, JS_INTERFACE_PKG);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/extras/PullToRefreshWebView2.java"
line="90"
column="11"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" return VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD &amp;&amp; mOverScrollEnabled"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshBase.java"
line="211"
column="10"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshExpandableListView.java"
line="54"
column="7"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshGridView.java"
line="54"
column="7"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshHorizontalScrollView.java"
line="53"
column="7"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshListView.java"
line="207"
column="7"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshScrollView.java"
line="52"
column="7"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshWebView.java"
line="98"
column="7"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 14"
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/internal/ViewCompat.java"
line="44"
column="7"/>
</issue>
<issue
id="FloatMath"
message="Use `java.lang.Math#floor` instead of `android.util.FloatMath#floor()` since it is faster as of API 8"
errorLine1=" float exactContentHeight = FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale());"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshWebView.java"
line="115"
column="30"/>
</issue>
<issue
id="FloatMath"
message="Use `java.lang.Math#floor` instead of `android.util.FloatMath#floor()` since it is faster as of API 8"
errorLine1=" return (int) Math.max(0, FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale())"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshWebView.java"
line="161"
column="29"/>
</issue>
<issue
id="IconMissingDensityFolder"
message="Missing density variation folders in `src/main/res`: drawable-xxhdpi">
<location
file="src/main/res"/>
</issue>
<issue
id="ViewConstructor"
message="Custom view `RotateLoadingLayout` is missing constructor used by tools: `(Context)` or `(Context,AttributeSet)` or `(Context,AttributeSet,int)`"
errorLine1="public class RotateLoadingLayout extends LoadingLayout {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/internal/RotateLoadingLayout.java"
line="30"
column="14"/>
</issue>
<issue
id="ContentDescription"
message="[Accessibility] Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageView"
errorLine2=" ^">
<location
file="src/main/res/layout/pull_to_refresh_header_horizontal.xml"
line="13"
column="9"/>
</issue>
<issue
id="ContentDescription"
message="[Accessibility] Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageView"
errorLine2=" ^">
<location
file="src/main/res/layout/pull_to_refresh_header_vertical.xml"
line="18"
column="13"/>
</issue>
<issue
id="RtlHardcoded"
message="Use &quot;`Gravity.START`&quot; instead of &quot;`Gravity.LEFT`&quot; to ensure correct behavior in right-to-left locales"
errorLine1=" lp.gravity = scrollDirection == Orientation.VERTICAL ? Gravity.TOP : Gravity.LEFT;"
errorLine2=" ~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/internal/LoadingLayout.java"
line="92"
column="82"/>
</issue>
<issue
id="RtlHardcoded"
message="Use &quot;`Gravity.END`&quot; instead of &quot;`Gravity.RIGHT`&quot; to ensure correct behavior in right-to-left locales"
errorLine1=" lp.gravity = scrollDirection == Orientation.VERTICAL ? Gravity.BOTTOM : Gravity.RIGHT;"
errorLine2=" ~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/internal/LoadingLayout.java"
line="102"
column="85"/>
</issue>
<issue
id="RtlHardcoded"
message="Use &quot;`Gravity.END`&quot; instead of &quot;`Gravity.RIGHT`&quot; to ensure correct behavior in right-to-left locales"
errorLine1=" params.gravity = Gravity.TOP | Gravity.RIGHT;"
errorLine2=" ~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshAdapterViewBase.java"
line="344"
column="43"/>
</issue>
<issue
id="RtlHardcoded"
message="Use &quot;`Gravity.END`&quot; instead of &quot;`Gravity.RIGHT`&quot; to ensure correct behavior in right-to-left locales"
errorLine1=" params.gravity = Gravity.BOTTOM | Gravity.RIGHT;"
errorLine2=" ~~~~~">
<location
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshAdapterViewBase.java"
line="359"
column="46"/>
</issue>
<issue
id="RtlHardcoded"
message="Use &quot;`start`&quot; instead of &quot;`left`&quot; to ensure correct behavior in right-to-left locales"
errorLine1=" android:layout_gravity=&quot;left|center_vertical&quot; >"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/pull_to_refresh_header_vertical.xml"
line="16"
column="37"/>
</issue>
</issues>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.handmark.pulltorefresh.library"
android:versionCode="2110"
android:versionName="2.1.1">
</manifest>

View File

@ -1,57 +0,0 @@
package com.handmark.pulltorefresh.library;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
public interface ILoadingLayout {
/**
* Set the Last Updated Text. This displayed under the main label when
* Pulling
*
* @param label - Label to set
*/
public void setLastUpdatedLabel(CharSequence label);
/**
* Set the drawable used in the loading layout. This is the same as calling
* <code>setLoadingDrawable(drawable, Mode.BOTH)</code>
*
* @param drawable - Drawable to display
*/
public void setLoadingDrawable(Drawable drawable);
/**
* Set Text to show when the Widget is being Pulled
* <code>setPullLabel(releaseLabel, Mode.BOTH)</code>
*
* @param pullLabel - CharSequence to display
*/
public void setPullLabel(CharSequence pullLabel);
/**
* Set Text to show when the Widget is refreshing
* <code>setRefreshingLabel(releaseLabel, Mode.BOTH)</code>
*
* @param refreshingLabel - CharSequence to display
*/
public void setRefreshingLabel(CharSequence refreshingLabel);
/**
* Set Text to show when the Widget is being pulled, and will refresh when
* released. This is the same as calling
* <code>setReleaseLabel(releaseLabel, Mode.BOTH)</code>
*
* @param releaseLabel - CharSequence to display
*/
public void setReleaseLabel(CharSequence releaseLabel);
/**
* Set's the Sets the typeface and style in which the text should be
* displayed. Please see
* {@link android.widget.TextView#setTypeface(Typeface)
* TextView#setTypeface(Typeface)}.
*/
public void setTextTypeface(Typeface tf);
}

View File

@ -1,246 +0,0 @@
/*******************************************************************************
* Copyright 2011, 2012 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.handmark.pulltorefresh.library;
import android.view.View;
import android.view.animation.Interpolator;
import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode;
import com.handmark.pulltorefresh.library.PullToRefreshBase.OnPullEventListener;
import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener;
import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener2;
import com.handmark.pulltorefresh.library.PullToRefreshBase.State;
public interface IPullToRefresh<T extends View> {
/**
* Demos the Pull-to-Refresh functionality to the user so that they are
* aware it is there. This could be useful when the user first opens your
* app, etc. The animation will only happen if the Refresh View (ListView,
* ScrollView, etc) is in a state where a Pull-to-Refresh could occur by a
* user's touch gesture (i.e. scrolled to the top/bottom).
*
* @return true - if the Demo has been started, false if not.
*/
public boolean demo();
/**
* Get the mode that this view is currently in. This is only really useful
* when using <code>Mode.BOTH</code>.
*
* @return Mode that the view is currently in
*/
public Mode getCurrentMode();
/**
* Returns whether the Touch Events are filtered or not. If true is
* returned, then the View will only use touch events where the difference
* in the Y-axis is greater than the difference in the X-axis. This means
* that the View will not interfere when it is used in a horizontal
* scrolling View (such as a ViewPager).
*
* @return boolean - true if the View is filtering Touch Events
*/
public boolean getFilterTouchEvents();
/**
* Returns a proxy object which allows you to call methods on all of the
* LoadingLayouts (the Views which show when Pulling/Refreshing).
* <p />
* You should not keep the result of this method any longer than you need
* it.
*
* @return Object which will proxy any calls you make on it, to all of the
* LoadingLayouts.
*/
public ILoadingLayout getLoadingLayoutProxy();
/**
* Returns a proxy object which allows you to call methods on the
* LoadingLayouts (the Views which show when Pulling/Refreshing). The actual
* LoadingLayout(s) which will be affected, are chosen by the parameters you
* give.
* <p />
* You should not keep the result of this method any longer than you need
* it.
*
* @param includeStart - Whether to include the Start/Header Views
* @param includeEnd - Whether to include the End/Footer Views
* @return Object which will proxy any calls you make on it, to the
* LoadingLayouts included.
*/
public ILoadingLayout getLoadingLayoutProxy(boolean includeStart, boolean includeEnd);
/**
* Get the mode that this view has been set to. If this returns
* <code>Mode.BOTH</code>, you can use <code>getCurrentMode()</code> to
* check which mode the view is currently in
*
* @return Mode that the view has been set to
*/
public Mode getMode();
/**
* Get the Wrapped Refreshable View. Anything returned here has already been
* added to the content view.
*
* @return The View which is currently wrapped
*/
public T getRefreshableView();
/**
* Get whether the 'Refreshing' View should be automatically shown when
* refreshing. Returns true by default.
*
* @return - true if the Refreshing View will be show
*/
public boolean getShowViewWhileRefreshing();
/**
* @return - The state that the View is currently in.
*/
public State getState();
/**
* Whether Pull-to-Refresh is enabled
*
* @return enabled
*/
public boolean isPullToRefreshEnabled();
/**
* Gets whether Overscroll support is enabled. This is different to
* Android's standard Overscroll support (the edge-glow) which is available
* from GINGERBREAD onwards
*
* @return true - if both PullToRefresh-OverScroll and Android's inbuilt
* OverScroll are enabled
*/
public boolean isPullToRefreshOverScrollEnabled();
/**
* Returns whether the Widget is currently in the Refreshing mState
*
* @return true if the Widget is currently refreshing
*/
public boolean isRefreshing();
/**
* Returns whether the widget has enabled scrolling on the Refreshable View
* while refreshing.
*
* @return true if the widget has enabled scrolling while refreshing
*/
public boolean isScrollingWhileRefreshingEnabled();
/**
* Mark the current Refresh as complete. Will Reset the UI and hide the
* Refreshing View
*/
public void onRefreshComplete();
/**
* Set the Touch Events to be filtered or not. If set to true, then the View
* will only use touch events where the difference in the Y-axis is greater
* than the difference in the X-axis. This means that the View will not
* interfere when it is used in a horizontal scrolling View (such as a
* ViewPager), but will restrict which types of finger scrolls will trigger
* the View.
*
* @param filterEvents - true if you want to filter Touch Events. Default is
* true.
*/
public void setFilterTouchEvents(boolean filterEvents);
/**
* Set the mode of Pull-to-Refresh that this view will use.
*
* @param mode - Mode to set the View to
*/
public void setMode(Mode mode);
/**
* Set OnPullEventListener for the Widget
*
* @param listener - Listener to be used when the Widget has a pull event to
* propogate.
*/
public void setOnPullEventListener(OnPullEventListener<T> listener);
/**
* Set OnRefreshListener for the Widget
*
* @param listener - Listener to be used when the Widget is set to Refresh
*/
public void setOnRefreshListener(OnRefreshListener<T> listener);
/**
* Set OnRefreshListener for the Widget
*
* @param listener - Listener to be used when the Widget is set to Refresh
*/
public void setOnRefreshListener(OnRefreshListener2<T> listener);
/**
* Sets whether Overscroll support is enabled. This is different to
* Android's standard Overscroll support (the edge-glow). This setting only
* takes effect when running on device with Android v2.3 or greater.
*
* @param enabled - true if you want Overscroll enabled
*/
public void setPullToRefreshOverScrollEnabled(boolean enabled);
/**
* Sets the Widget to be in the refresh state. The UI will be updated to
* show the 'Refreshing' view, and be scrolled to show such.
*/
public void setRefreshing();
/**
* Sets the Widget to be in the refresh state. The UI will be updated to
* show the 'Refreshing' view.
*
* @param doScroll - true if you want to force a scroll to the Refreshing
* view.
*/
public void setRefreshing(boolean doScroll);
/**
* Sets the Animation Interpolator that is used for animated scrolling.
* Defaults to a DecelerateInterpolator
*
* @param interpolator - Interpolator to use
*/
public void setScrollAnimationInterpolator(Interpolator interpolator);
/**
* By default the Widget disables scrolling on the Refreshable View while
* refreshing. This method can change this behaviour.
*
* @param scrollingWhileRefreshingEnabled - true if you want to enable
* scrolling while refreshing
*/
public void setScrollingWhileRefreshingEnabled(boolean scrollingWhileRefreshingEnabled);
/**
* A mutator to enable/disable whether the 'Refreshing' View should be
* automatically shown when refreshing.
*
* @param showView
*/
public void setShowViewWhileRefreshing(boolean showView);
}

View File

@ -1,73 +0,0 @@
package com.handmark.pulltorefresh.library;
import java.util.HashSet;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import com.handmark.pulltorefresh.library.internal.LoadingLayout;
public class LoadingLayoutProxy implements ILoadingLayout {
private final HashSet<LoadingLayout> mLoadingLayouts;
LoadingLayoutProxy() {
mLoadingLayouts = new HashSet<LoadingLayout>();
}
/**
* This allows you to add extra LoadingLayout instances to this proxy. This
* is only necessary if you keep your own instances, and want to have them
* included in any
* {@link PullToRefreshBase#createLoadingLayoutProxy(boolean, boolean)
* createLoadingLayoutProxy(...)} calls.
*
* @param layout - LoadingLayout to have included.
*/
public void addLayout(LoadingLayout layout) {
if (null != layout) {
mLoadingLayouts.add(layout);
}
}
@Override
public void setLastUpdatedLabel(CharSequence label) {
for (LoadingLayout layout : mLoadingLayouts) {
layout.setLastUpdatedLabel(label);
}
}
@Override
public void setLoadingDrawable(Drawable drawable) {
for (LoadingLayout layout : mLoadingLayouts) {
layout.setLoadingDrawable(drawable);
}
}
@Override
public void setRefreshingLabel(CharSequence refreshingLabel) {
for (LoadingLayout layout : mLoadingLayouts) {
layout.setRefreshingLabel(refreshingLabel);
}
}
@Override
public void setPullLabel(CharSequence label) {
for (LoadingLayout layout : mLoadingLayouts) {
layout.setPullLabel(label);
}
}
@Override
public void setReleaseLabel(CharSequence label) {
for (LoadingLayout layout : mLoadingLayouts) {
layout.setReleaseLabel(label);
}
}
public void setTextTypeface(Typeface tf) {
for (LoadingLayout layout : mLoadingLayouts) {
layout.setTextTypeface(tf);
}
}
}

View File

@ -1,178 +0,0 @@
/*******************************************************************************
* Copyright 2011, 2012 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.handmark.pulltorefresh.library;
import android.annotation.TargetApi;
import android.util.Log;
import android.view.View;
import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode;
import com.handmark.pulltorefresh.library.PullToRefreshBase.State;
@TargetApi(9)
public final class OverscrollHelper {
static final String LOG_TAG = "OverscrollHelper";
static final float DEFAULT_OVERSCROLL_SCALE = 1f;
/**
* Helper method for Overscrolling that encapsulates all of the necessary
* function.
* <p/>
* This should only be used on AdapterView's such as ListView as it just
* calls through to overScrollBy() with the scrollRange = 0. AdapterView's
* do not have a scroll range (i.e. getScrollY() doesn't work).
*
* @param view - PullToRefreshView that is calling this.
* @param deltaX - Change in X in pixels, passed through from from
* overScrollBy call
* @param scrollX - Current X scroll value in pixels before applying deltaY,
* passed through from from overScrollBy call
* @param deltaY - Change in Y in pixels, passed through from from
* overScrollBy call
* @param scrollY - Current Y scroll value in pixels before applying deltaY,
* passed through from from overScrollBy call
* @param isTouchEvent - true if this scroll operation is the result of a
* touch event, passed through from from overScrollBy call
*/
public static void overScrollBy(final PullToRefreshBase<?> view, final int deltaX, final int scrollX,
final int deltaY, final int scrollY, final boolean isTouchEvent) {
overScrollBy(view, deltaX, scrollX, deltaY, scrollY, 0, isTouchEvent);
}
/**
* Helper method for Overscrolling that encapsulates all of the necessary
* function. This version of the call is used for Views that need to specify
* a Scroll Range but scroll back to it's edge correctly.
*
* @param view - PullToRefreshView that is calling this.
* @param deltaX - Change in X in pixels, passed through from from
* overScrollBy call
* @param scrollX - Current X scroll value in pixels before applying deltaY,
* passed through from from overScrollBy call
* @param deltaY - Change in Y in pixels, passed through from from
* overScrollBy call
* @param scrollY - Current Y scroll value in pixels before applying deltaY,
* passed through from from overScrollBy call
* @param scrollRange - Scroll Range of the View, specifically needed for
* ScrollView
* @param isTouchEvent - true if this scroll operation is the result of a
* touch event, passed through from from overScrollBy call
*/
public static void overScrollBy(final PullToRefreshBase<?> view, final int deltaX, final int scrollX,
final int deltaY, final int scrollY, final int scrollRange, final boolean isTouchEvent) {
overScrollBy(view, deltaX, scrollX, deltaY, scrollY, scrollRange, 0, DEFAULT_OVERSCROLL_SCALE, isTouchEvent);
}
/**
* Helper method for Overscrolling that encapsulates all of the necessary
* function. This is the advanced version of the call.
*
* @param view - PullToRefreshView that is calling this.
* @param deltaX - Change in X in pixels, passed through from from
* overScrollBy call
* @param scrollX - Current X scroll value in pixels before applying deltaY,
* passed through from from overScrollBy call
* @param deltaY - Change in Y in pixels, passed through from from
* overScrollBy call
* @param scrollY - Current Y scroll value in pixels before applying deltaY,
* passed through from from overScrollBy call
* @param scrollRange - Scroll Range of the View, specifically needed for
* ScrollView
* @param fuzzyThreshold - Threshold for which the values how fuzzy we
* should treat the other values. Needed for WebView as it
* doesn't always scroll back to it's edge. 0 = no fuzziness.
* @param scaleFactor - Scale Factor for overscroll amount
* @param isTouchEvent - true if this scroll operation is the result of a
* touch event, passed through from from overScrollBy call
*/
public static void overScrollBy(final PullToRefreshBase<?> view, final int deltaX, final int scrollX,
final int deltaY, final int scrollY, final int scrollRange, final int fuzzyThreshold,
final float scaleFactor, final boolean isTouchEvent) {
final int deltaValue, currentScrollValue, scrollValue;
switch (view.getPullToRefreshScrollDirection()) {
case HORIZONTAL:
deltaValue = deltaX;
scrollValue = scrollX;
currentScrollValue = view.getScrollX();
break;
case VERTICAL:
default:
deltaValue = deltaY;
scrollValue = scrollY;
currentScrollValue = view.getScrollY();
break;
}
// Check that OverScroll is enabled and that we're not currently
// refreshing.
if (view.isPullToRefreshOverScrollEnabled() && !view.isRefreshing()) {
final Mode mode = view.getMode();
// Check that Pull-to-Refresh is enabled, and the event isn't from
// touch
if (mode.permitsPullToRefresh() && !isTouchEvent && deltaValue != 0) {
final int newScrollValue = (deltaValue + scrollValue);
if (PullToRefreshBase.DEBUG) {
Log.d(LOG_TAG, "OverScroll. DeltaX: " + deltaX + ", ScrollX: " + scrollX + ", DeltaY: " + deltaY
+ ", ScrollY: " + scrollY + ", NewY: " + newScrollValue + ", ScrollRange: " + scrollRange
+ ", CurrentScroll: " + currentScrollValue);
}
if (newScrollValue < (0 - fuzzyThreshold)) {
// Check the mode supports the overscroll direction, and
// then move scroll
if (mode.showHeaderLoadingLayout()) {
// If we're currently at zero, we're about to start
// overscrolling, so change the state
if (currentScrollValue == 0) {
view.setState(State.OVERSCROLLING);
}
view.setHeaderScroll((int) (scaleFactor * (currentScrollValue + newScrollValue)));
}
} else if (newScrollValue > (scrollRange + fuzzyThreshold)) {
// Check the mode supports the overscroll direction, and
// then move scroll
if (mode.showFooterLoadingLayout()) {
// If we're currently at zero, we're about to start
// overscrolling, so change the state
if (currentScrollValue == 0) {
view.setState(State.OVERSCROLLING);
}
view.setHeaderScroll((int) (scaleFactor * (currentScrollValue + newScrollValue - scrollRange)));
}
} else if (Math.abs(newScrollValue) <= fuzzyThreshold
|| Math.abs(newScrollValue - scrollRange) <= fuzzyThreshold) {
// Means we've stopped overscrolling, so scroll back to 0
view.setState(State.RESET);
}
} else if (isTouchEvent && State.OVERSCROLLING == view.getState()) {
// This condition means that we were overscrolling from a fling,
// but the user has touched the View and is now overscrolling
// from touch instead. We need to just reset.
view.setState(State.RESET);
}
}
}
static boolean isAndroidOverScrollEnabled(View view) {
return view.getOverScrollMode() != View.OVER_SCROLL_NEVER;
}
}

View File

@ -1,475 +0,0 @@
/*******************************************************************************
* Copyright 2011, 2012 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.handmark.pulltorefresh.library;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor;
import com.handmark.pulltorefresh.library.internal.IndicatorLayout;
public abstract class PullToRefreshAdapterViewBase<T extends AbsListView> extends PullToRefreshBase<T> implements
OnScrollListener {
private static FrameLayout.LayoutParams convertEmptyViewLayoutParams(ViewGroup.LayoutParams lp) {
FrameLayout.LayoutParams newLp = null;
if (null != lp) {
newLp = new FrameLayout.LayoutParams(lp);
if (lp instanceof LinearLayout.LayoutParams) {
newLp.gravity = ((LinearLayout.LayoutParams) lp).gravity;
} else {
newLp.gravity = Gravity.CENTER;
}
}
return newLp;
}
private boolean mLastItemVisible;
private OnScrollListener mOnScrollListener;
private OnLastItemVisibleListener mOnLastItemVisibleListener;
private View mEmptyView;
private IndicatorLayout mIndicatorIvTop;
private IndicatorLayout mIndicatorIvBottom;
private boolean mShowIndicator;
private boolean mScrollEmptyView = true;
public PullToRefreshAdapterViewBase(Context context) {
super(context);
mRefreshableView.setOnScrollListener(this);
}
public PullToRefreshAdapterViewBase(Context context, AttributeSet attrs) {
super(context, attrs);
mRefreshableView.setOnScrollListener(this);
}
public PullToRefreshAdapterViewBase(Context context, Mode mode) {
super(context, mode);
mRefreshableView.setOnScrollListener(this);
}
public PullToRefreshAdapterViewBase(Context context, Mode mode, AnimationStyle animStyle) {
super(context, mode, animStyle);
mRefreshableView.setOnScrollListener(this);
}
/**
* Gets whether an indicator graphic should be displayed when the View is in
* a state where a Pull-to-Refresh can happen. An example of this state is
* when the Adapter View is scrolled to the top and the mode is set to
* {@link Mode#PULL_FROM_START}. The default value is <var>true</var> if
* {@link PullToRefreshBase#isPullToRefreshOverScrollEnabled()
* isPullToRefreshOverScrollEnabled()} returns false.
*
* @return true if the indicators will be shown
*/
public boolean getShowIndicator() {
return mShowIndicator;
}
public final void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
final int totalItemCount) {
if (DEBUG) {
Log.d(LOG_TAG, "First Visible: " + firstVisibleItem + ". Visible Count: " + visibleItemCount
+ ". Total Items:" + totalItemCount);
}
/**
* Set whether the Last Item is Visible. lastVisibleItemIndex is a
* zero-based index, so we minus one totalItemCount to check
*/
if (null != mOnLastItemVisibleListener) {
mLastItemVisible = (totalItemCount > 0) && (firstVisibleItem + visibleItemCount >= totalItemCount - 1);
}
// If we're showing the indicator, check positions...
if (getShowIndicatorInternal()) {
updateIndicatorViewsVisibility();
}
// Finally call OnScrollListener if we have one
if (null != mOnScrollListener) {
mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
public final void onScrollStateChanged(final AbsListView view, final int state) {
/**
* Check that the scrolling has stopped, and that the last item is
* visible.
*/
if (state == OnScrollListener.SCROLL_STATE_IDLE && null != mOnLastItemVisibleListener && mLastItemVisible) {
mOnLastItemVisibleListener.onLastItemVisible();
}
if (null != mOnScrollListener) {
mOnScrollListener.onScrollStateChanged(view, state);
}
}
/**
* Pass-through method for {@link PullToRefreshBase#getRefreshableView()
* getRefreshableView()}.
* {@link AdapterView#setAdapter(android.widget.Adapter)}
* setAdapter(adapter)}. This is just for convenience!
*
* @param adapter - Adapter to set
*/
public void setAdapter(ListAdapter adapter) {
((AdapterView<ListAdapter>) mRefreshableView).setAdapter(adapter);
}
/**
* Sets the Empty View to be used by the Adapter View.
* <p/>
* We need it handle it ourselves so that we can Pull-to-Refresh when the
* Empty View is shown.
* <p/>
* Please note, you do <strong>not</strong> usually need to call this method
* yourself. Calling setEmptyView on the AdapterView will automatically call
* this method and set everything up. This includes when the Android
* Framework automatically sets the Empty View based on it's ID.
*
* @param newEmptyView - Empty View to be used
*/
public final void setEmptyView(View newEmptyView) {
FrameLayout refreshableViewWrapper = getRefreshableViewWrapper();
if (null != newEmptyView) {
// New view needs to be clickable so that Android recognizes it as a
// target for Touch Events
newEmptyView.setClickable(true);
ViewParent newEmptyViewParent = newEmptyView.getParent();
if (null != newEmptyViewParent && newEmptyViewParent instanceof ViewGroup) {
((ViewGroup) newEmptyViewParent).removeView(newEmptyView);
}
// We need to convert any LayoutParams so that it works in our
// FrameLayout
FrameLayout.LayoutParams lp = convertEmptyViewLayoutParams(newEmptyView.getLayoutParams());
if (null != lp) {
refreshableViewWrapper.addView(newEmptyView, lp);
} else {
refreshableViewWrapper.addView(newEmptyView);
}
}
if (mRefreshableView instanceof EmptyViewMethodAccessor) {
((EmptyViewMethodAccessor) mRefreshableView).setEmptyViewInternal(newEmptyView);
} else {
mRefreshableView.setEmptyView(newEmptyView);
}
mEmptyView = newEmptyView;
}
/**
* Pass-through method for {@link PullToRefreshBase#getRefreshableView()
* getRefreshableView()}.
* {@link AdapterView#setOnItemClickListener(OnItemClickListener)
* setOnItemClickListener(listener)}. This is just for convenience!
*
* @param listener - OnItemClickListener to use
*/
public void setOnItemClickListener(OnItemClickListener listener) {
mRefreshableView.setOnItemClickListener(listener);
}
public final void setOnLastItemVisibleListener(OnLastItemVisibleListener listener) {
mOnLastItemVisibleListener = listener;
}
public final void setOnScrollListener(OnScrollListener listener) {
mOnScrollListener = listener;
}
public final void setScrollEmptyView(boolean doScroll) {
mScrollEmptyView = doScroll;
}
/**
* Sets whether an indicator graphic should be displayed when the View is in
* a state where a Pull-to-Refresh can happen. An example of this state is
* when the Adapter View is scrolled to the top and the mode is set to
* {@link Mode#PULL_FROM_START}
*
* @param showIndicator - true if the indicators should be shown.
*/
public void setShowIndicator(boolean showIndicator) {
mShowIndicator = showIndicator;
if (getShowIndicatorInternal()) {
// If we're set to Show Indicator, add/update them
addIndicatorViews();
} else {
// If not, then remove then
removeIndicatorViews();
}
}
;
@Override
protected void onPullToRefresh() {
super.onPullToRefresh();
if (getShowIndicatorInternal()) {
switch (getCurrentMode()) {
case PULL_FROM_END:
mIndicatorIvBottom.pullToRefresh();
break;
case PULL_FROM_START:
mIndicatorIvTop.pullToRefresh();
break;
default:
// NO-OP
break;
}
}
}
protected void onRefreshing(boolean doScroll) {
super.onRefreshing(doScroll);
if (getShowIndicatorInternal()) {
updateIndicatorViewsVisibility();
}
}
@Override
protected void onReleaseToRefresh() {
super.onReleaseToRefresh();
if (getShowIndicatorInternal()) {
switch (getCurrentMode()) {
case PULL_FROM_END:
mIndicatorIvBottom.releaseToRefresh();
break;
case PULL_FROM_START:
mIndicatorIvTop.releaseToRefresh();
break;
default:
// NO-OP
break;
}
}
}
@Override
protected void onReset() {
super.onReset();
if (getShowIndicatorInternal()) {
updateIndicatorViewsVisibility();
}
}
@Override
protected void handleStyledAttributes(TypedArray a) {
// Set Show Indicator to the XML value, or default value
mShowIndicator = a.getBoolean(R.styleable.PullToRefresh_ptrShowIndicator, !isPullToRefreshOverScrollEnabled());
}
protected boolean isReadyForPullStart() {
return isFirstItemVisible();
}
protected boolean isReadyForPullEnd() {
return isLastItemVisible();
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (null != mEmptyView && !mScrollEmptyView) {
mEmptyView.scrollTo(-l, -t);
}
}
@Override
protected void updateUIForMode() {
super.updateUIForMode();
// Check Indicator Views consistent with new Mode
if (getShowIndicatorInternal()) {
addIndicatorViews();
} else {
removeIndicatorViews();
}
}
private void addIndicatorViews() {
Mode mode = getMode();
FrameLayout refreshableViewWrapper = getRefreshableViewWrapper();
if (mode.showHeaderLoadingLayout() && null == mIndicatorIvTop) {
// If the mode can pull down, and we don't have one set already
mIndicatorIvTop = new IndicatorLayout(getContext(), Mode.PULL_FROM_START);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.rightMargin = getResources().getDimensionPixelSize(R.dimen.indicator_right_padding);
params.gravity = Gravity.TOP | Gravity.RIGHT;
refreshableViewWrapper.addView(mIndicatorIvTop, params);
} else if (!mode.showHeaderLoadingLayout() && null != mIndicatorIvTop) {
// If we can't pull down, but have a View then remove it
refreshableViewWrapper.removeView(mIndicatorIvTop);
mIndicatorIvTop = null;
}
if (mode.showFooterLoadingLayout() && null == mIndicatorIvBottom) {
// If the mode can pull down, and we don't have one set already
mIndicatorIvBottom = new IndicatorLayout(getContext(), Mode.PULL_FROM_END);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.rightMargin = getResources().getDimensionPixelSize(R.dimen.indicator_right_padding);
params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
refreshableViewWrapper.addView(mIndicatorIvBottom, params);
} else if (!mode.showFooterLoadingLayout() && null != mIndicatorIvBottom) {
// If we can't pull down, but have a View then remove it
refreshableViewWrapper.removeView(mIndicatorIvBottom);
mIndicatorIvBottom = null;
}
}
private boolean getShowIndicatorInternal() {
return mShowIndicator && isPullToRefreshEnabled();
}
private boolean isFirstItemVisible() {
final Adapter adapter = mRefreshableView.getAdapter();
if (null == adapter || adapter.isEmpty()) {
if (DEBUG) {
Log.d(LOG_TAG, "isFirstItemVisible. Empty View.");
}
return true;
} else {
/**
* This check should really just be:
* mRefreshableView.getFirstVisiblePosition() == 0, but PtRListView
* internally use a HeaderView which messes the positions up. For
* now we'll just add one to account for it and rely on the inner
* condition which checks getTop().
*/
if (mRefreshableView.getFirstVisiblePosition() <= 1) {
final View firstVisibleChild = mRefreshableView.getChildAt(0);
if (firstVisibleChild != null) {
return firstVisibleChild.getTop() >= mRefreshableView.getTop();
}
}
}
return false;
}
private boolean isLastItemVisible() {
final Adapter adapter = mRefreshableView.getAdapter();
if (null == adapter || adapter.isEmpty()) {
if (DEBUG) {
Log.d(LOG_TAG, "isLastItemVisible. Empty View.");
}
return true;
} else {
final int lastItemPosition = mRefreshableView.getCount() - 1;
final int lastVisiblePosition = mRefreshableView.getLastVisiblePosition();
if (DEBUG) {
Log.d(LOG_TAG, "isLastItemVisible. Last Item Position: " + lastItemPosition + " Last Visible Pos: "
+ lastVisiblePosition);
}
/**
* This check should really just be: lastVisiblePosition ==
* lastItemPosition, but PtRListView internally uses a FooterView
* which messes the positions up. For me we'll just subtract one to
* account for it and rely on the inner condition which checks
* getBottom().
*/
if (lastVisiblePosition >= lastItemPosition - 1) {
final int childIndex = lastVisiblePosition - mRefreshableView.getFirstVisiblePosition();
final View lastVisibleChild = mRefreshableView.getChildAt(childIndex);
if (lastVisibleChild != null) {
return lastVisibleChild.getBottom() <= mRefreshableView.getBottom();
}
}
}
return false;
}
private void removeIndicatorViews() {
if (null != mIndicatorIvTop) {
getRefreshableViewWrapper().removeView(mIndicatorIvTop);
mIndicatorIvTop = null;
}
if (null != mIndicatorIvBottom) {
getRefreshableViewWrapper().removeView(mIndicatorIvBottom);
mIndicatorIvBottom = null;
}
}
private void updateIndicatorViewsVisibility() {
if (null != mIndicatorIvTop) {
if (!isRefreshing() && isReadyForPullStart()) {
if (!mIndicatorIvTop.isVisible()) {
mIndicatorIvTop.show();
}
} else {
if (mIndicatorIvTop.isVisible()) {
mIndicatorIvTop.hide();
}
}
}
if (null != mIndicatorIvBottom) {
if (!isRefreshing() && isReadyForPullEnd()) {
if (!mIndicatorIvBottom.isVisible()) {
mIndicatorIvBottom.show();
}
} else {
if (mIndicatorIvBottom.isVisible()) {
mIndicatorIvBottom.hide();
}
}
}
}
}

View File

@ -1,103 +0,0 @@
/*******************************************************************************
* Copyright 2011, 2012 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.handmark.pulltorefresh.library;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ExpandableListView;
import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor;
public class PullToRefreshExpandableListView extends PullToRefreshAdapterViewBase<ExpandableListView> {
public PullToRefreshExpandableListView(Context context) {
super(context);
}
public PullToRefreshExpandableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PullToRefreshExpandableListView(Context context, Mode mode) {
super(context, mode);
}
public PullToRefreshExpandableListView(Context context, Mode mode, AnimationStyle style) {
super(context, mode, style);
}
@Override
public final Orientation getPullToRefreshScrollDirection() {
return Orientation.VERTICAL;
}
@Override
protected ExpandableListView createRefreshableView(Context context, AttributeSet attrs) {
final ExpandableListView lv;
if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
lv = new InternalExpandableListViewSDK9(context, attrs);
} else {
lv = new InternalExpandableListView(context, attrs);
}
// Set it to this so it can be used in ListActivity/ListFragment
lv.setId(android.R.id.list);
return lv;
}
class InternalExpandableListView extends ExpandableListView implements EmptyViewMethodAccessor {
public InternalExpandableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setEmptyView(View emptyView) {
PullToRefreshExpandableListView.this.setEmptyView(emptyView);
}
@Override
public void setEmptyViewInternal(View emptyView) {
super.setEmptyView(emptyView);
}
}
@TargetApi(9)
final class InternalExpandableListViewSDK9 extends InternalExpandableListView {
public InternalExpandableListViewSDK9(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
// Does all of the hard work...
OverscrollHelper.overScrollBy(PullToRefreshExpandableListView.this, deltaX, scrollX, deltaY, scrollY,
isTouchEvent);
return returnValue;
}
}
}

View File

@ -1,102 +0,0 @@
/*******************************************************************************
* Copyright 2011, 2012 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.handmark.pulltorefresh.library;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.view.View;
import android.widget.GridView;
import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor;
public class PullToRefreshGridView extends PullToRefreshAdapterViewBase<GridView> {
public PullToRefreshGridView(Context context) {
super(context);
}
public PullToRefreshGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PullToRefreshGridView(Context context, Mode mode) {
super(context, mode);
}
public PullToRefreshGridView(Context context, Mode mode, AnimationStyle style) {
super(context, mode, style);
}
@Override
public final Orientation getPullToRefreshScrollDirection() {
return Orientation.VERTICAL;
}
@Override
protected final GridView createRefreshableView(Context context, AttributeSet attrs) {
final GridView gv;
if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
gv = new InternalGridViewSDK9(context, attrs);
} else {
gv = new InternalGridView(context, attrs);
}
// Use Generated ID (from res/values/ids.xml)
gv.setId(R.id.gridview);
return gv;
}
class InternalGridView extends GridView implements EmptyViewMethodAccessor {
public InternalGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setEmptyView(View emptyView) {
PullToRefreshGridView.this.setEmptyView(emptyView);
}
@Override
public void setEmptyViewInternal(View emptyView) {
super.setEmptyView(emptyView);
}
}
@TargetApi(9)
final class InternalGridViewSDK9 extends InternalGridView {
public InternalGridViewSDK9(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
// Does all of the hard work...
OverscrollHelper.overScrollBy(PullToRefreshGridView.this, deltaX, scrollX, deltaY, scrollY, isTouchEvent);
return returnValue;
}
}
}

View File

@ -1,110 +0,0 @@
/*******************************************************************************
* Copyright 2011, 2012 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.handmark.pulltorefresh.library;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.view.View;
import android.widget.HorizontalScrollView;
public class PullToRefreshHorizontalScrollView extends PullToRefreshBase<HorizontalScrollView> {
public PullToRefreshHorizontalScrollView(Context context) {
super(context);
}
public PullToRefreshHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PullToRefreshHorizontalScrollView(Context context, Mode mode) {
super(context, mode);
}
public PullToRefreshHorizontalScrollView(Context context, Mode mode, AnimationStyle style) {
super(context, mode, style);
}
@Override
public final Orientation getPullToRefreshScrollDirection() {
return Orientation.HORIZONTAL;
}
@Override
protected HorizontalScrollView createRefreshableView(Context context, AttributeSet attrs) {
HorizontalScrollView scrollView;
if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
scrollView = new InternalHorizontalScrollViewSDK9(context, attrs);
} else {
scrollView = new HorizontalScrollView(context, attrs);
}
scrollView.setId(R.id.scrollview);
return scrollView;
}
@Override
protected boolean isReadyForPullStart() {
return mRefreshableView.getScrollX() == 0;
}
@Override
protected boolean isReadyForPullEnd() {
View scrollViewChild = mRefreshableView.getChildAt(0);
if (null != scrollViewChild) {
return mRefreshableView.getScrollX() >= (scrollViewChild.getWidth() - getWidth());
}
return false;
}
@TargetApi(9)
final class InternalHorizontalScrollViewSDK9 extends HorizontalScrollView {
public InternalHorizontalScrollViewSDK9(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
// Does all of the hard work...
OverscrollHelper.overScrollBy(PullToRefreshHorizontalScrollView.this, deltaX, scrollX, deltaY, scrollY,
getScrollRange(), isTouchEvent);
return returnValue;
}
/**
* Taken from the AOSP ScrollView source
*/
private int getScrollRange() {
int scrollRange = 0;
if (getChildCount() > 0) {
View child = getChildAt(0);
scrollRange = Math.max(0, child.getWidth() - (getWidth() - getPaddingLeft() - getPaddingRight()));
}
return scrollRange;
}
}
}

View File

@ -1,337 +0,0 @@
/*******************************************************************************
* Copyright 2011, 2012 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.handmark.pulltorefresh.library;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor;
import com.handmark.pulltorefresh.library.internal.LoadingLayout;
public class PullToRefreshListView extends PullToRefreshAdapterViewBase<ListView> {
private LoadingLayout mHeaderLoadingView;
private LoadingLayout mFooterLoadingView;
private FrameLayout mLvFooterLoadingFrame;
private boolean mListViewExtrasEnabled;
public PullToRefreshListView(Context context) {
super(context);
}
public PullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PullToRefreshListView(Context context, Mode mode) {
super(context, mode);
}
public PullToRefreshListView(Context context, Mode mode, AnimationStyle style) {
super(context, mode, style);
}
@Override
public final Orientation getPullToRefreshScrollDirection() {
return Orientation.VERTICAL;
}
@Override
protected void onRefreshing(final boolean doScroll) {
/**
* If we're not showing the Refreshing view, or the list is empty, the
* the header/footer views won't show so we use the normal method.
*/
ListAdapter adapter = mRefreshableView.getAdapter();
if (!mListViewExtrasEnabled || !getShowViewWhileRefreshing() || null == adapter || adapter.isEmpty()) {
super.onRefreshing(doScroll);
return;
}
super.onRefreshing(false);
final LoadingLayout origLoadingView, listViewLoadingView, oppositeListViewLoadingView;
final int selection, scrollToY;
switch (getCurrentMode()) {
case MANUAL_REFRESH_ONLY:
case PULL_FROM_END:
origLoadingView = getFooterLayout();
listViewLoadingView = mFooterLoadingView;
oppositeListViewLoadingView = mHeaderLoadingView;
selection = mRefreshableView.getCount() - 1;
scrollToY = getScrollY() - getFooterSize();
break;
case PULL_FROM_START:
default:
origLoadingView = getHeaderLayout();
listViewLoadingView = mHeaderLoadingView;
oppositeListViewLoadingView = mFooterLoadingView;
selection = 0;
scrollToY = getScrollY() + getHeaderSize();
break;
}
// Hide our original Loading View
origLoadingView.reset();
origLoadingView.hideAllViews();
// Make sure the opposite end is hidden too
oppositeListViewLoadingView.setVisibility(View.GONE);
// Show the ListView Loading View and set it to refresh.
listViewLoadingView.setVisibility(View.VISIBLE);
listViewLoadingView.refreshing();
if (doScroll) {
// We need to disable the automatic visibility changes for now
disableLoadingLayoutVisibilityChanges();
// We scroll slightly so that the ListView's header/footer is at the
// same Y position as our normal header/footer
setHeaderScroll(scrollToY);
// Make sure the ListView is scrolled to show the loading
// header/footer
mRefreshableView.setSelection(selection);
// Smooth scroll as normal
smoothScrollTo(0);
}
}
@Override
protected void onReset() {
/**
* If the extras are not enabled, just call up to super and return.
*/
if (!mListViewExtrasEnabled) {
super.onReset();
return;
}
final LoadingLayout originalLoadingLayout, listViewLoadingLayout;
final int scrollToHeight, selection;
final boolean scrollLvToEdge;
switch (getCurrentMode()) {
case MANUAL_REFRESH_ONLY:
case PULL_FROM_END:
originalLoadingLayout = getFooterLayout();
listViewLoadingLayout = mFooterLoadingView;
selection = mRefreshableView.getCount() - 1;
scrollToHeight = getFooterSize();
scrollLvToEdge = Math.abs(mRefreshableView.getLastVisiblePosition() - selection) <= 1;
break;
case PULL_FROM_START:
default:
originalLoadingLayout = getHeaderLayout();
listViewLoadingLayout = mHeaderLoadingView;
scrollToHeight = -getHeaderSize();
selection = 0;
scrollLvToEdge = Math.abs(mRefreshableView.getFirstVisiblePosition() - selection) <= 1;
break;
}
// If the ListView header loading layout is showing, then we need to
// flip so that the original one is showing instead
if (listViewLoadingLayout.getVisibility() == View.VISIBLE) {
// Set our Original View to Visible
originalLoadingLayout.showInvisibleViews();
// Hide the ListView Header/Footer
listViewLoadingLayout.setVisibility(View.GONE);
/**
* Scroll so the View is at the same Y as the ListView
* header/footer, but only scroll if: we've pulled to refresh, it's
* positioned correctly
*/
if (scrollLvToEdge && getState() != State.MANUAL_REFRESHING) {
mRefreshableView.setSelection(selection);
setHeaderScroll(scrollToHeight);
}
}
// Finally, call up to super
super.onReset();
}
@Override
protected LoadingLayoutProxy createLoadingLayoutProxy(final boolean includeStart, final boolean includeEnd) {
LoadingLayoutProxy proxy = super.createLoadingLayoutProxy(includeStart, includeEnd);
if (mListViewExtrasEnabled) {
final Mode mode = getMode();
if (includeStart && mode.showHeaderLoadingLayout()) {
proxy.addLayout(mHeaderLoadingView);
}
if (includeEnd && mode.showFooterLoadingLayout()) {
proxy.addLayout(mFooterLoadingView);
}
}
return proxy;
}
protected ListView createListView(Context context, AttributeSet attrs) {
final ListView lv;
if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
lv = new InternalListViewSDK9(context, attrs);
} else {
lv = new InternalListView(context, attrs);
}
return lv;
}
@Override
protected ListView createRefreshableView(Context context, AttributeSet attrs) {
ListView lv = createListView(context, attrs);
// Set it to this so it can be used in ListActivity/ListFragment
lv.setId(android.R.id.list);
return lv;
}
@Override
protected void handleStyledAttributes(TypedArray a) {
super.handleStyledAttributes(a);
mListViewExtrasEnabled = a.getBoolean(R.styleable.PullToRefresh_ptrListViewExtrasEnabled, true);
if (mListViewExtrasEnabled) {
final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL);
// Create Loading Views ready for use later
FrameLayout frame = new FrameLayout(getContext());
mHeaderLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_START, a);
mHeaderLoadingView.setVisibility(View.GONE);
frame.addView(mHeaderLoadingView, lp);
mRefreshableView.addHeaderView(frame, null, false);
mLvFooterLoadingFrame = new FrameLayout(getContext());
mFooterLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_END, a);
mFooterLoadingView.setVisibility(View.GONE);
mLvFooterLoadingFrame.addView(mFooterLoadingView, lp);
/**
* If the value for Scrolling While Refreshing hasn't been
* explicitly set via XML, enable Scrolling While Refreshing.
*/
if (!a.hasValue(R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled)) {
setScrollingWhileRefreshingEnabled(true);
}
}
}
@TargetApi(9)
final class InternalListViewSDK9 extends InternalListView {
public InternalListViewSDK9(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
// Does all of the hard work...
OverscrollHelper.overScrollBy(PullToRefreshListView.this, deltaX, scrollX, deltaY, scrollY, isTouchEvent);
return returnValue;
}
}
protected class InternalListView extends ListView implements EmptyViewMethodAccessor {
private boolean mAddedLvFooter = false;
public InternalListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void dispatchDraw(Canvas canvas) {
/**
* This is a bit hacky, but Samsung's ListView has got a bug in it
* when using Header/Footer Views and the list is empty. This masks
* the issue so that it doesn't cause an FC. See Issue #66.
*/
try {
super.dispatchDraw(canvas);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
/**
* This is a bit hacky, but Samsung's ListView has got a bug in it
* when using Header/Footer Views and the list is empty. This masks
* the issue so that it doesn't cause an FC. See Issue #66.
*/
try {
return super.dispatchTouchEvent(ev);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
return false;
}
}
@Override
public void setAdapter(ListAdapter adapter) {
// Add the Footer View at the last possible moment
if (null != mLvFooterLoadingFrame && !mAddedLvFooter) {
addFooterView(mLvFooterLoadingFrame, null, false);
mAddedLvFooter = true;
}
super.setAdapter(adapter);
}
@Override
public void setEmptyView(View emptyView) {
PullToRefreshListView.this.setEmptyView(emptyView);
}
@Override
public void setEmptyViewInternal(View emptyView) {
super.setEmptyView(emptyView);
}
}
}

View File

@ -1,109 +0,0 @@
/*******************************************************************************
* Copyright 2011, 2012 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.handmark.pulltorefresh.library;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ScrollView;
public class PullToRefreshScrollView extends PullToRefreshBase<ScrollView> {
public PullToRefreshScrollView(Context context) {
super(context);
}
public PullToRefreshScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PullToRefreshScrollView(Context context, Mode mode) {
super(context, mode);
}
public PullToRefreshScrollView(Context context, Mode mode, AnimationStyle style) {
super(context, mode, style);
}
@Override
public final Orientation getPullToRefreshScrollDirection() {
return Orientation.VERTICAL;
}
@Override
protected ScrollView createRefreshableView(Context context, AttributeSet attrs) {
ScrollView scrollView;
if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
scrollView = new InternalScrollViewSDK9(context, attrs);
} else {
scrollView = new ScrollView(context, attrs);
}
scrollView.setId(R.id.scrollview);
return scrollView;
}
@Override
protected boolean isReadyForPullStart() {
return mRefreshableView.getScrollY() == 0;
}
@Override
protected boolean isReadyForPullEnd() {
View scrollViewChild = mRefreshableView.getChildAt(0);
if (null != scrollViewChild) {
return mRefreshableView.getScrollY() >= (scrollViewChild.getHeight() - getHeight());
}
return false;
}
@TargetApi(9)
final class InternalScrollViewSDK9 extends ScrollView {
public InternalScrollViewSDK9(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
// Does all of the hard work...
OverscrollHelper.overScrollBy(PullToRefreshScrollView.this, deltaX, scrollX, deltaY, scrollY,
getScrollRange(), isTouchEvent);
return returnValue;
}
/**
* Taken from the AOSP ScrollView source
*/
private int getScrollRange() {
int scrollRange = 0;
if (getChildCount() > 0) {
View child = getChildAt(0);
scrollRange = Math.max(0, child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop()));
}
return scrollRange;
}
}
}

View File

@ -1,164 +0,0 @@
/*******************************************************************************
* Copyright 2011, 2012 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.handmark.pulltorefresh.library;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.util.AttributeSet;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
public class PullToRefreshWebView extends PullToRefreshBase<WebView> {
private static final OnRefreshListener<WebView> defaultOnRefreshListener = new OnRefreshListener<WebView>() {
@Override
public void onRefresh(PullToRefreshBase<WebView> refreshView) {
refreshView.getRefreshableView().reload();
}
};
private final WebChromeClient defaultWebChromeClient = new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress == 100) {
onRefreshComplete();
}
}
};
public PullToRefreshWebView(Context context) {
super(context);
/**
* Added so that by default, Pull-to-Refresh refreshes the page
*/
setOnRefreshListener(defaultOnRefreshListener);
mRefreshableView.setWebChromeClient(defaultWebChromeClient);
}
public PullToRefreshWebView(Context context, AttributeSet attrs) {
super(context, attrs);
/**
* Added so that by default, Pull-to-Refresh refreshes the page
*/
setOnRefreshListener(defaultOnRefreshListener);
mRefreshableView.setWebChromeClient(defaultWebChromeClient);
}
public PullToRefreshWebView(Context context, Mode mode) {
super(context, mode);
/**
* Added so that by default, Pull-to-Refresh refreshes the page
*/
setOnRefreshListener(defaultOnRefreshListener);
mRefreshableView.setWebChromeClient(defaultWebChromeClient);
}
public PullToRefreshWebView(Context context, Mode mode, AnimationStyle style) {
super(context, mode, style);
/**
* Added so that by default, Pull-to-Refresh refreshes the page
*/
setOnRefreshListener(defaultOnRefreshListener);
mRefreshableView.setWebChromeClient(defaultWebChromeClient);
}
@Override
public final Orientation getPullToRefreshScrollDirection() {
return Orientation.VERTICAL;
}
@Override
protected WebView createRefreshableView(Context context, AttributeSet attrs) {
WebView webView;
if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
webView = new InternalWebViewSDK9(context, attrs);
} else {
webView = new WebView(context, attrs);
}
webView.setId(R.id.webview);
return webView;
}
@Override
protected boolean isReadyForPullStart() {
return mRefreshableView.getScrollY() == 0;
}
@Override
protected boolean isReadyForPullEnd() {
float exactContentHeight = (float) Math.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale());
return mRefreshableView.getScrollY() >= (exactContentHeight - mRefreshableView.getHeight());
}
@Override
protected void onPtrRestoreInstanceState(Bundle savedInstanceState) {
super.onPtrRestoreInstanceState(savedInstanceState);
mRefreshableView.restoreState(savedInstanceState);
}
@Override
protected void onPtrSaveInstanceState(Bundle saveState) {
super.onPtrSaveInstanceState(saveState);
mRefreshableView.saveState(saveState);
}
@TargetApi(9)
final class InternalWebViewSDK9 extends WebView {
// WebView doesn't always scroll back to it's edge so we add some
// fuzziness
static final int OVERSCROLL_FUZZY_THRESHOLD = 2;
// WebView seems quite reluctant to overscroll so we use the scale
// factor to scale it's value
static final float OVERSCROLL_SCALE_FACTOR = 1.5f;
public InternalWebViewSDK9(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
// Does all of the hard work...
OverscrollHelper.overScrollBy(PullToRefreshWebView.this, deltaX, scrollX, deltaY, scrollY,
getScrollRange(), OVERSCROLL_FUZZY_THRESHOLD, OVERSCROLL_SCALE_FACTOR, isTouchEvent);
return returnValue;
}
private int getScrollRange() {
return (int) Math.max(0, Math.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale())
- (getHeight() - getPaddingBottom() - getPaddingTop()));
}
}
}

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