Merge branch 'develop' into folders
This commit is contained in:
commit
f76d3ad09e
@ -45,7 +45,7 @@ workflows:
|
|||||||
destination: app-play-debug.apk
|
destination: app-play-debug.apk
|
||||||
- run:
|
- run:
|
||||||
name: Execute debug unit tests
|
name: Execute debug unit tests
|
||||||
command: ./gradlew :core:testPlayDebugUnitTest -PdisablePreDex
|
command: ./gradlew testPlayDebugUnitTest -PdisablePreDex
|
||||||
- build:
|
- build:
|
||||||
name: Build release
|
name: Build release
|
||||||
build-steps:
|
build-steps:
|
||||||
@ -54,19 +54,19 @@ workflows:
|
|||||||
command: ./gradlew assembleRelease -PdisablePreDex
|
command: ./gradlew assembleRelease -PdisablePreDex
|
||||||
- run:
|
- run:
|
||||||
name: Execute release unit tests
|
name: Execute release unit tests
|
||||||
command: ./gradlew :core:testPlayReleaseUnitTest -PdisablePreDex
|
command: ./gradlew testPlayReleaseUnitTest -PdisablePreDex
|
||||||
- build:
|
- build:
|
||||||
name: Build integration tests
|
name: Build integration tests
|
||||||
build-steps:
|
build-steps:
|
||||||
- run:
|
- run:
|
||||||
name: Build integration tests
|
name: Build integration tests
|
||||||
command: ./gradlew :app:assemblePlayDebugAndroidTest -PdisablePreDex
|
command: ./gradlew assemblePlayDebugAndroidTest -PdisablePreDex
|
||||||
- build:
|
- build:
|
||||||
name: Build free
|
name: Build free
|
||||||
build-steps:
|
build-steps:
|
||||||
- run:
|
- run:
|
||||||
name: Build free (for F-Droid)
|
name: Build free (for F-Droid)
|
||||||
command: ./gradlew assembleFreeRelease -PdisablePreDex -PfreeBuild
|
command: ./gradlew assembleFreeRelease -PdisablePreDex
|
||||||
|
|
||||||
static-analysis:
|
static-analysis:
|
||||||
jobs:
|
jobs:
|
||||||
@ -85,19 +85,8 @@ workflows:
|
|||||||
curl -s -L https://github.com/yangziwen/diff-checkstyle/releases/download/0.0.4/diff-checkstyle.jar > diff-checkstyle.jar
|
curl -s -L https://github.com/yangziwen/diff-checkstyle/releases/download/0.0.4/diff-checkstyle.jar > diff-checkstyle.jar
|
||||||
java -Dconfig_loc=config/checkstyle -jar diff-checkstyle.jar -c config/checkstyle/checkstyle-new-code.xml --git-dir . --base-rev $branchBaseCommit
|
java -Dconfig_loc=config/checkstyle -jar diff-checkstyle.jar -c config/checkstyle/checkstyle-new-code.xml --git-dir . --base-rev $branchBaseCommit
|
||||||
- build:
|
- build:
|
||||||
name: Lint app
|
name: Lint
|
||||||
build-steps:
|
build-steps:
|
||||||
- run:
|
- run:
|
||||||
name: Lint app
|
name: Lint
|
||||||
command: ./gradlew app:lintPlayRelease
|
command: ./gradlew lintPlayRelease
|
||||||
- run:
|
|
||||||
name: Lint core
|
|
||||||
command: ./gradlew core:lintPlayRelease
|
|
||||||
- store_artifacts:
|
|
||||||
name: Uploading app lint reports
|
|
||||||
path: app/build/reports/lint-results-playRelease.html
|
|
||||||
destination: lint-results-app.html
|
|
||||||
- store_artifacts:
|
|
||||||
name: Uploading core lint reports
|
|
||||||
path: core/build/reports/lint-results-playRelease.html
|
|
||||||
destination: lint-results-core.html
|
|
||||||
|
@ -4,6 +4,7 @@ host = https://www.transifex.com
|
|||||||
[antennapod.core-values]
|
[antennapod.core-values]
|
||||||
source_file = core/src/main/res/values/strings.xml
|
source_file = core/src/main/res/values/strings.xml
|
||||||
source_lang = en
|
source_lang = en
|
||||||
|
trans.ar = core/src/main/res/values-ar/strings.xml
|
||||||
trans.br = core/src/main/res/values-br/strings.xml
|
trans.br = core/src/main/res/values-br/strings.xml
|
||||||
trans.ca = core/src/main/res/values-ca/strings.xml
|
trans.ca = core/src/main/res/values-ca/strings.xml
|
||||||
trans.cs_CZ = core/src/main/res/values-cs/strings.xml
|
trans.cs_CZ = core/src/main/res/values-cs/strings.xml
|
||||||
@ -29,6 +30,7 @@ trans.pl_PL = core/src/main/res/values-pl/strings.xml
|
|||||||
trans.pt = core/src/main/res/values-pt/strings.xml
|
trans.pt = core/src/main/res/values-pt/strings.xml
|
||||||
trans.pt_BR = core/src/main/res/values-pt-rBR/strings.xml
|
trans.pt_BR = core/src/main/res/values-pt-rBR/strings.xml
|
||||||
trans.ru_RU = core/src/main/res/values-ru/strings.xml
|
trans.ru_RU = core/src/main/res/values-ru/strings.xml
|
||||||
|
trans.sk = core/src/main/res/values-sk/strings.xml
|
||||||
trans.sv_SE = core/src/main/res/values-sv/strings.xml
|
trans.sv_SE = core/src/main/res/values-sv/strings.xml
|
||||||
trans.tr = core/src/main/res/values-tr/strings.xml
|
trans.tr = core/src/main/res/values-tr/strings.xml
|
||||||
trans.uk_UA = core/src/main/res/values-uk/strings.xml
|
trans.uk_UA = core/src/main/res/values-uk/strings.xml
|
||||||
@ -58,7 +60,6 @@ trans.he_IL = app/src/main/play/listings/iw-IL/full-description.txt
|
|||||||
trans.hu = app/src/main/play/listings/hu-HU/full-description.txt
|
trans.hu = app/src/main/play/listings/hu-HU/full-description.txt
|
||||||
trans.id = app/src/main/play/listings/id/full-description.txt
|
trans.id = app/src/main/play/listings/id/full-description.txt
|
||||||
trans.it_IT = app/src/main/play/listings/it-IT/full-description.txt
|
trans.it_IT = app/src/main/play/listings/it-IT/full-description.txt
|
||||||
trans.iw = app/src/main/play/listings/iw-IL/full-description.txt
|
|
||||||
trans.ja = app/src/main/play/listings/ja-JP/full-description.txt
|
trans.ja = app/src/main/play/listings/ja-JP/full-description.txt
|
||||||
trans.ko = app/src/main/play/listings/ko-KR/full-description.txt
|
trans.ko = app/src/main/play/listings/ko-KR/full-description.txt
|
||||||
trans.lt = app/src/main/play/listings/lt/full-description.txt
|
trans.lt = app/src/main/play/listings/lt/full-description.txt
|
||||||
@ -68,6 +69,7 @@ trans.pt_BR = app/src/main/play/listings/pt-BR/full-description.txt
|
|||||||
trans.pt = app/src/main/play/listings/pt-PT/full-description.txt
|
trans.pt = app/src/main/play/listings/pt-PT/full-description.txt
|
||||||
trans.ro_RO = app/src/main/play/listings/ro/full-description.txt
|
trans.ro_RO = app/src/main/play/listings/ro/full-description.txt
|
||||||
trans.ru_RU = app/src/main/play/listings/ru-RU/full-description.txt
|
trans.ru_RU = app/src/main/play/listings/ru-RU/full-description.txt
|
||||||
|
trans.sk = app/src/main/play/listings/sk/full-description.txt
|
||||||
trans.sl_SI = app/src/main/play/listings/sl/full-description.txt
|
trans.sl_SI = app/src/main/play/listings/sl/full-description.txt
|
||||||
trans.sv_SE = app/src/main/play/listings/sv-SE/full-description.txt
|
trans.sv_SE = app/src/main/play/listings/sv-SE/full-description.txt
|
||||||
trans.tr = app/src/main/play/listings/tr-TR/full-description.txt
|
trans.tr = app/src/main/play/listings/tr-TR/full-description.txt
|
||||||
@ -98,7 +100,6 @@ trans.he_IL = app/src/main/play/listings/iw-IL/short-description.txt
|
|||||||
trans.hu = app/src/main/play/listings/hu-HU/short-description.txt
|
trans.hu = app/src/main/play/listings/hu-HU/short-description.txt
|
||||||
trans.id = app/src/main/play/listings/id/short-description.txt
|
trans.id = app/src/main/play/listings/id/short-description.txt
|
||||||
trans.it_IT = app/src/main/play/listings/it-IT/short-description.txt
|
trans.it_IT = app/src/main/play/listings/it-IT/short-description.txt
|
||||||
trans.iw = app/src/main/play/listings/iw-IL/short-description.txt
|
|
||||||
trans.ja = app/src/main/play/listings/ja-JP/short-description.txt
|
trans.ja = app/src/main/play/listings/ja-JP/short-description.txt
|
||||||
trans.ko = app/src/main/play/listings/ko-KR/short-description.txt
|
trans.ko = app/src/main/play/listings/ko-KR/short-description.txt
|
||||||
trans.lt = app/src/main/play/listings/lt/short-description.txt
|
trans.lt = app/src/main/play/listings/lt/short-description.txt
|
||||||
@ -108,6 +109,7 @@ trans.pt_BR = app/src/main/play/listings/pt-BR/short-description.txt
|
|||||||
trans.pt = app/src/main/play/listings/pt-PT/short-description.txt
|
trans.pt = app/src/main/play/listings/pt-PT/short-description.txt
|
||||||
trans.ro_RO = app/src/main/play/listings/ro/short-description.txt
|
trans.ro_RO = app/src/main/play/listings/ro/short-description.txt
|
||||||
trans.ru_RU = app/src/main/play/listings/ru-RU/short-description.txt
|
trans.ru_RU = app/src/main/play/listings/ru-RU/short-description.txt
|
||||||
|
trans.sk = app/src/main/play/listings/sk/short-description.txt
|
||||||
trans.sl_SI = app/src/main/play/listings/sl/short-description.txt
|
trans.sl_SI = app/src/main/play/listings/sl/short-description.txt
|
||||||
trans.sv_SE = app/src/main/play/listings/sv-SE/short-description.txt
|
trans.sv_SE = app/src/main/play/listings/sv-SE/short-description.txt
|
||||||
trans.tr = app/src/main/play/listings/tr-TR/short-description.txt
|
trans.tr = app/src/main/play/listings/tr-TR/short-description.txt
|
||||||
|
471
CHANGELOG.md
471
CHANGELOG.md
@ -1,471 +0,0 @@
|
|||||||
Change Log
|
|
||||||
==========
|
|
||||||
|
|
||||||
Version 1.8.1
|
|
||||||
-------------
|
|
||||||
* Enabled picture-in-picture for video podcasts by default (by @ByteHamster)
|
|
||||||
* Fixed podcast discovery not showing local trends (by @tonytamsf)
|
|
||||||
* Various bug fixes and improvements (by @ByteHamster)
|
|
||||||
|
|
||||||
Version 1.8.0
|
|
||||||
-------------
|
|
||||||
* Added per-feed playback speed setting (by @spacecowboy)
|
|
||||||
* Support sorting in Podcast screen (by @orionlee)
|
|
||||||
* Option to show stream button rather than download in lists (by @dsmith47)
|
|
||||||
* Option to replace Episode cover with Podcast cover (by @xgouchet)
|
|
||||||
* Transparent widget (by @M-arcel)
|
|
||||||
* User interface tweaks (by @ByteHamster)
|
|
||||||
* Tons of bug fixes and improvements
|
|
||||||
|
|
||||||
Version 1.7.3
|
|
||||||
-------------
|
|
||||||
* Display episode image on widget (by @brad)
|
|
||||||
* Added checkbox to keep queue sorted (by @damoasda)
|
|
||||||
* New UI for "Add podcast" screen (by @ByteHamster)
|
|
||||||
* Added batch editing to the queue (by @ByteHamster)
|
|
||||||
* Added option to adapt remaining time to playback speed (by @CedricCabessa)
|
|
||||||
* Removed broken Flattr integration (by @ByteHamster)
|
|
||||||
* Added filter to "All episodes" list (by @jhunnius)
|
|
||||||
* Tons of bug fixes and performance improvements
|
|
||||||
|
|
||||||
Version 1.7.2
|
|
||||||
-------------
|
|
||||||
* Added configurable behavior of the back button
|
|
||||||
* Added delete option to episode's context menu
|
|
||||||
* New UI for batch edit feature
|
|
||||||
* Set number of columns in subscription list
|
|
||||||
* Lots of bug fixes
|
|
||||||
|
|
||||||
Version 1.7.1
|
|
||||||
-------------
|
|
||||||
|
|
||||||
* Fix for database corruption
|
|
||||||
|
|
||||||
Version 1.7.0
|
|
||||||
-------------
|
|
||||||
|
|
||||||
* NEW ExoPlayer (experimental)
|
|
||||||
* Fix for Bluetooth Forward (Oreo)
|
|
||||||
* Preference redesign + search
|
|
||||||
* Notification improvements
|
|
||||||
* Different screens for feed info and settings
|
|
||||||
* Sort Queue with Random or Smart Shuffle
|
|
||||||
* True Black Theme for AMOLED
|
|
||||||
* Improvements to feed parsing
|
|
||||||
* Fix for app being killed by Android Oreo
|
|
||||||
|
|
||||||
Version 1.6.5
|
|
||||||
-------------
|
|
||||||
|
|
||||||
* Fix database corruption
|
|
||||||
* Improvements to Feed parsing
|
|
||||||
|
|
||||||
Version 1.6.4
|
|
||||||
-------------
|
|
||||||
|
|
||||||
* Fixes issues on Android Oreo
|
|
||||||
* Avoids duplicate chapters
|
|
||||||
* Experimental: Database import & export
|
|
||||||
|
|
||||||
Version 1.6.3
|
|
||||||
-------------
|
|
||||||
|
|
||||||
* New features:
|
|
||||||
* Support for Android Auto
|
|
||||||
* Sort feeds by number of played episodes
|
|
||||||
* Statistics modes
|
|
||||||
* Setting: Enqueue downloaded
|
|
||||||
* Launch screen
|
|
||||||
* Improvements
|
|
||||||
* Chapter duration
|
|
||||||
* Feed title in deletion confirmation
|
|
||||||
* Fixes:
|
|
||||||
* Episodes refresh spinner
|
|
||||||
* Publication date parsing
|
|
||||||
* Unknown mime type
|
|
||||||
|
|
||||||
Version 1.6.2
|
|
||||||
-------------
|
|
||||||
|
|
||||||
* New features:
|
|
||||||
* Integration of fyyd Podcast Search Engine
|
|
||||||
* Export subscriptions as HTML
|
|
||||||
* Rename feeds
|
|
||||||
* Auto-enable sleep timer
|
|
||||||
* "has media" filter
|
|
||||||
* Force gpodder full sync
|
|
||||||
* Improvements:
|
|
||||||
* Better support for Atom feeds, e.g. summary tag
|
|
||||||
* Confirmation dialog on mark all as seen
|
|
||||||
* Number of downloaded episodes in subscription counter
|
|
||||||
* Gpodder sync error optional
|
|
||||||
* Search results
|
|
||||||
* MRSS support
|
|
||||||
* Sanitize HTML from Atom feed
|
|
||||||
* Fixes:
|
|
||||||
* Reset sleep timer on shake to current waiting time
|
|
||||||
* Cast dialog image
|
|
||||||
* Mini player not showing up
|
|
||||||
* Audio player cover fragment
|
|
||||||
* Prevent out of memory and casting crashes
|
|
||||||
|
|
||||||
Version 1.6.0
|
|
||||||
-------------
|
|
||||||
* New features:
|
|
||||||
* Experimental Chromecast support
|
|
||||||
* Subscription overview
|
|
||||||
* Proxy support
|
|
||||||
* Statistics
|
|
||||||
* Manual gpodder.net sync
|
|
||||||
* Fixes:
|
|
||||||
* Audioplayer controls
|
|
||||||
* Audio ducking
|
|
||||||
* Video control fade-out
|
|
||||||
* External media controls
|
|
||||||
* Feed parsing
|
|
||||||
|
|
||||||
Version 1.5.0
|
|
||||||
-------------
|
|
||||||
* Exclude episodes from auto download by keyword
|
|
||||||
* Configure feeds to prevent them from refreshing automatically
|
|
||||||
* Improved audio player
|
|
||||||
* Improved UI
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
Version 1.4.1
|
|
||||||
-------------
|
|
||||||
* Performance improvements
|
|
||||||
* Hardware buttons now ff and rewind instead of skipping
|
|
||||||
* Option to have forward button skip
|
|
||||||
* Option to send crash reports directly to developers
|
|
||||||
* Highlight currently playing episode
|
|
||||||
* Widget improvements
|
|
||||||
|
|
||||||
Version 1.4.0.12
|
|
||||||
----------------
|
|
||||||
* Fix for crash on Huawei devices (media buttons may not work)
|
|
||||||
|
|
||||||
Version 1.4
|
|
||||||
-----------
|
|
||||||
* BLUETOOTH PERMISSION: Needed to be able to resume playback when a Bluetooth device reconnects with your phone
|
|
||||||
* VIBRATE PERMISSION: Used optionally with the sleep timer
|
|
||||||
* Native variable speed playback (experimental via options)
|
|
||||||
* Improved sleep timer
|
|
||||||
* Mark episodes as 'favorite'
|
|
||||||
* Notification can skip episodes
|
|
||||||
* Keep episodes when skipping them
|
|
||||||
* Episode art on lock screen
|
|
||||||
* Flexible episode cleanup
|
|
||||||
* Rewind after pause
|
|
||||||
* Usability improvements
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
Version 1.3
|
|
||||||
-----------
|
|
||||||
* Bulk actions on feed episodes (download, queue, delete)
|
|
||||||
* Reduced space used by images
|
|
||||||
* Automatic refresh at a certain time of day
|
|
||||||
* Customizable indicators and sorting for feeds
|
|
||||||
* Ability to share feeds
|
|
||||||
* Improved auto download
|
|
||||||
* Many fixes and usability improvements
|
|
||||||
|
|
||||||
Version 1.2
|
|
||||||
-----------
|
|
||||||
* Optionally disable swiping and dragging in the queue
|
|
||||||
* Resume playback after phone call
|
|
||||||
* Filter episodes in the Podcast feed
|
|
||||||
* Hide items in the Nav drawer
|
|
||||||
* Customize times for fast forward and rewind
|
|
||||||
* Resolved issues with opening some OPML files
|
|
||||||
* Various bug fixes and usability improvements
|
|
||||||
|
|
||||||
Version 1.1
|
|
||||||
-----------
|
|
||||||
* iTunes podcast integration
|
|
||||||
* Swipe to remove items from the queue
|
|
||||||
* Set the number of parallel downloads
|
|
||||||
* Fix for gpodder.net on old devices
|
|
||||||
* Fixed date problems for some feeds
|
|
||||||
* Display improvements
|
|
||||||
* Usability improvements
|
|
||||||
* Several other bugfixes
|
|
||||||
|
|
||||||
Version 1.0
|
|
||||||
-----------
|
|
||||||
* The queue can now be sorted
|
|
||||||
* Added option to delete episode after playback
|
|
||||||
* Fixed a bug that caused chapters to be displayed multiple times
|
|
||||||
* Several other improvements and bugfixes
|
|
||||||
|
|
||||||
|
|
||||||
Version 0.9.9.6
|
|
||||||
---------------
|
|
||||||
* Fixed problems related to variable playback speed plugins
|
|
||||||
* Fixed automatic feed update problems
|
|
||||||
* Several other bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.9.5
|
|
||||||
---------------
|
|
||||||
* Added support for paged feeds
|
|
||||||
* Improved user interface
|
|
||||||
* Added Japanese and Turkish translations
|
|
||||||
* Fixed more image loading problems
|
|
||||||
* Other bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.9.4
|
|
||||||
---------------
|
|
||||||
* Added option to keep notification and lockscreen controls when playback is paused
|
|
||||||
* Fixed a bug where episode images were not loaded correctly
|
|
||||||
* Fixed battery usage problems
|
|
||||||
|
|
||||||
Version 0.9.9.3
|
|
||||||
---------------
|
|
||||||
* Fixed video playback problems
|
|
||||||
* Improved image loading
|
|
||||||
* Other bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.9.2
|
|
||||||
---------------
|
|
||||||
* Added support for feed discovery if a website URL is entered
|
|
||||||
* Added support for 'next'/'previous' media keys
|
|
||||||
* Improved sleep timer
|
|
||||||
* Timestamps in shownotes can now be used to jump to a specific position
|
|
||||||
* Automatic Flattring is now configurable
|
|
||||||
* Several bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.9.1
|
|
||||||
---------------
|
|
||||||
* Several bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.9.0
|
|
||||||
---------------
|
|
||||||
* New user interface
|
|
||||||
* Failed downloads are now resumed when restarted
|
|
||||||
* Added support for Podlove Alternate Feeds
|
|
||||||
* Added support for "pcast"-protocol
|
|
||||||
* Added backup & restore functionality. This feature has to be enabled in the Android settings in order to work
|
|
||||||
* Several bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.8.3
|
|
||||||
---------------
|
|
||||||
* Added support for password-protected feeds and episodes
|
|
||||||
* Added support for more types of episode images
|
|
||||||
* Added Hebrew translation
|
|
||||||
* Several bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.8.2
|
|
||||||
---------------
|
|
||||||
* Several bugfixes and improvements
|
|
||||||
* Added Korean translation
|
|
||||||
|
|
||||||
Version 0.9.8.1
|
|
||||||
---------------
|
|
||||||
* Added option to flattr an episode automatically after 80 percent of the episode have been played
|
|
||||||
* Added Polish translation
|
|
||||||
* Several bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.8.0
|
|
||||||
---------------
|
|
||||||
* Added access to the gpodder.net directory
|
|
||||||
* Added ability to sync podcast subscriptions with the gpodder.net service
|
|
||||||
* Automatic download can now be turned on or off for specific podcasts
|
|
||||||
* Added option to pause playback when another app is playing sounds
|
|
||||||
* Added Dutch and Hindi translation
|
|
||||||
* Resolved a problem with automatic podcast updates
|
|
||||||
* Resolved a problem with the buttons' visibility in the episode screen
|
|
||||||
* Resolved a problem where episodes would be re-downloaded unnecessarily
|
|
||||||
* Several other bugfixes and usability improvements
|
|
||||||
|
|
||||||
Version 0.9.7.5
|
|
||||||
---------------
|
|
||||||
* Reduced application startup time
|
|
||||||
* Reduced memory usage
|
|
||||||
* Added option to change the playback speed
|
|
||||||
* Added Swedish translation
|
|
||||||
* Several bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.7.4
|
|
||||||
---------------
|
|
||||||
* Episode cache size can now be set to unlimited
|
|
||||||
* Removing an episode in the queue via sliding can now be undone
|
|
||||||
* Added support for Links in MP3 chapters
|
|
||||||
* Added Czech(Czech Republic), Azerbaijani and Portuguese translations
|
|
||||||
* Several bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.7.3
|
|
||||||
---------------
|
|
||||||
* Bluetooth devices now display metadata during playback (requires AVRCP 1.3 or higher)
|
|
||||||
* User interface improvements
|
|
||||||
* Several bugfixes
|
|
||||||
|
|
||||||
Version 0.9.7.2
|
|
||||||
---------------
|
|
||||||
* Automatic download can now be disabled
|
|
||||||
* Added Italian (Italy) translation
|
|
||||||
* Several bugfixes
|
|
||||||
|
|
||||||
Version 0.9.7.1
|
|
||||||
---------------
|
|
||||||
* Added automatic download of new episodes
|
|
||||||
* Added option to specify the number of downloaded episodes to keep on the device
|
|
||||||
* Added support for playback of external media files
|
|
||||||
* Several improvements and bugfixes
|
|
||||||
* Added Catalan translation
|
|
||||||
|
|
||||||
Version 0.9.7
|
|
||||||
-------------
|
|
||||||
* Improved user interface
|
|
||||||
* OPML files can now be imported by selecting them in a file browser
|
|
||||||
* The queue can now be organized via drag & drop
|
|
||||||
* Added expandable notifications (only supported on Android 4.1 and above)
|
|
||||||
* Added Danish, French, Romanian (Romania) and Ukrainian (Ukraine) translation (thanks to all translators!)
|
|
||||||
* Several bugfixes and minor improvements
|
|
||||||
|
|
||||||
Version 0.9.6.4
|
|
||||||
---------------
|
|
||||||
* Added Chinese translation (Thanks tupunco!)
|
|
||||||
* Added Portuguese (Brazil) translation (Thanks mbaltar!)
|
|
||||||
* Several bugfixes
|
|
||||||
|
|
||||||
Version 0.9.6.3
|
|
||||||
---------------
|
|
||||||
* Added the ability change the location of AntennaPod's data folder
|
|
||||||
* Added Spanish translation (Thanks frandavid100!)
|
|
||||||
* Solved problems with several feeds
|
|
||||||
|
|
||||||
Version 0.9.6.2
|
|
||||||
---------------
|
|
||||||
* Fixed import problems with some OPML files
|
|
||||||
* Fixed download problems
|
|
||||||
* AntennaPod now recognizes changes of episode information
|
|
||||||
* Other improvements and bugfixes
|
|
||||||
|
|
||||||
Version 0.9.6.1
|
|
||||||
---------------
|
|
||||||
* Added dark theme
|
|
||||||
* Several bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.6
|
|
||||||
-------------
|
|
||||||
* Added support for VorbisComment chapters
|
|
||||||
* AntennaPod now shows items as 'in progress' when playback has started
|
|
||||||
* Reduced memory usage
|
|
||||||
* Added support for more feed types
|
|
||||||
* Several bugfixes
|
|
||||||
|
|
||||||
|
|
||||||
Version 0.9.5.3
|
|
||||||
---------------
|
|
||||||
* Fixed crash when trying to start playback on some devices
|
|
||||||
* Fixed problems with some feeds
|
|
||||||
* Other bugfixes and improvements
|
|
||||||
|
|
||||||
Version 0.9.5.2
|
|
||||||
---------------
|
|
||||||
* Media player now doesn't use network bandwidth anymore if not in use
|
|
||||||
* Other improvements and bugfixes
|
|
||||||
|
|
||||||
Version 0.9.5.1
|
|
||||||
---------------
|
|
||||||
* Added playback history
|
|
||||||
* Improved behavior of download report notifications
|
|
||||||
* Improved support for headset controls
|
|
||||||
* Bugfixes in the feed parser
|
|
||||||
* Moved 'OPML import' button into the 'add feed' screen and the 'OPML export' button into the settings screen
|
|
||||||
|
|
||||||
Version 0.9.5
|
|
||||||
-------------
|
|
||||||
* Experimental support for MP3 chapters
|
|
||||||
* New menu options for the 'new' list and the queue
|
|
||||||
* Auto-delete feature
|
|
||||||
* Better Download error reports
|
|
||||||
* Several Bugfixes
|
|
||||||
|
|
||||||
Version 0.9.4.6
|
|
||||||
---------------
|
|
||||||
* Enabled support for small-screen devices
|
|
||||||
* Disabling the sleep timer should now work again
|
|
||||||
|
|
||||||
Version 0.9.4.5
|
|
||||||
---------------
|
|
||||||
* Added Russian translation (Thanks older!)
|
|
||||||
* Added German translation
|
|
||||||
* Several bugfixes
|
|
||||||
|
|
||||||
Version 0.9.4.4
|
|
||||||
---------------
|
|
||||||
* Added player controls at the bottom of the main screen and the feedlist screens
|
|
||||||
* Improved media playback
|
|
||||||
|
|
||||||
Version 0.9.4.3
|
|
||||||
---------------
|
|
||||||
* Fixed several bugs in the feed parser
|
|
||||||
* Improved behavior of download reports
|
|
||||||
|
|
||||||
Version 0.9.4.2
|
|
||||||
---------------
|
|
||||||
* Fixed bug in the OPML importer
|
|
||||||
* Reduced memory usage of images
|
|
||||||
* Fixed download problems on some devices
|
|
||||||
|
|
||||||
Version 0.9.4.1
|
|
||||||
---------------
|
|
||||||
* Changed behavior of download notifications
|
|
||||||
|
|
||||||
Version 0.9.4
|
|
||||||
-------------
|
|
||||||
* Faster and more reliable downloads
|
|
||||||
* Added lockscreen player controls for Android 4.x devices
|
|
||||||
* Several bugfixes
|
|
||||||
|
|
||||||
Version 0.9.3.1
|
|
||||||
---------------
|
|
||||||
* Added preference to hide feed items which don't have an episode
|
|
||||||
* Improved image size for some some screen sizes
|
|
||||||
* Added grid view for large screens
|
|
||||||
* Several bugfixes
|
|
||||||
|
|
||||||
Version 0.9.3
|
|
||||||
-------------
|
|
||||||
* MiroGuide integration
|
|
||||||
* Bugfixes in the audio- and videoplayer
|
|
||||||
* Automatically add feeds to the queue when they have been downloaded
|
|
||||||
|
|
||||||
Version 0.9.2
|
|
||||||
-------------
|
|
||||||
* Bugfixes in the user interface
|
|
||||||
* GUID and ID attributes are now recognized by the Feedparser
|
|
||||||
* Stability improvements when adding several feeds at the same time
|
|
||||||
* Fixed bugs that occured when adding certain feeds
|
|
||||||
|
|
||||||
Version 0.9.1.1
|
|
||||||
--------------------
|
|
||||||
* Changed Flattr credentials
|
|
||||||
* Improved layout of Feed information screen
|
|
||||||
* AntennaPod is now open source! The source code is available at https://github.com/danieloeh/AntennaPod
|
|
||||||
|
|
||||||
Version 0.9.1
|
|
||||||
-----------------
|
|
||||||
* Added support for links in SimpleChapters
|
|
||||||
* Bugfix: Current Chapter wasn't always displayed correctly
|
|
||||||
|
|
||||||
Version 0.9
|
|
||||||
--------------
|
|
||||||
|
|
||||||
* OPML export
|
|
||||||
* Flattr integration
|
|
||||||
* Sleep timer
|
|
||||||
|
|
||||||
Version 0.8.2
|
|
||||||
-------------
|
|
||||||
|
|
||||||
* Added search
|
|
||||||
* Improved OPML import experience
|
|
||||||
* More bugfixes
|
|
||||||
|
|
||||||
Version 0.8.1
|
|
||||||
------------
|
|
||||||
|
|
||||||
* Added support for SimpleChapters
|
|
||||||
* OPML import
|
|
@ -35,3 +35,24 @@ Submit a pull request
|
|||||||
- Please do not upgrade dependencies or build tools unless you have a good reason for it. Doing so can easily introduce bugs that are hard to track down.
|
- Please do not upgrade dependencies or build tools unless you have a good reason for it. Doing so can easily introduce bugs that are hard to track down.
|
||||||
- If you plan to do a change that touches many files (10+), please ask beforehand. This usually causes merge conflicts for other developers.
|
- If you plan to do a change that touches many files (10+), please ask beforehand. This usually causes merge conflicts for other developers.
|
||||||
- Please follow our code style. You can use Checkstyle within Android Studio using our [coniguration file](https://github.com/AntennaPod/AntennaPod/blob/develop/config/checkstyle/checkstyle-new-code.xml).
|
- Please follow our code style. You can use Checkstyle within Android Studio using our [coniguration file](https://github.com/AntennaPod/AntennaPod/blob/develop/config/checkstyle/checkstyle-new-code.xml).
|
||||||
|
- Please only change the English string resources. Translations are handled on [Transifex](https://www.transifex.com/antennapod/antennapod/).
|
||||||
|
|
||||||
|
|
||||||
|
Testing and Verifying
|
||||||
|
--------------------------
|
||||||
|
As a developer contributing to AntennaPod, we ask that you test the feature yourself manually and better yet, add unit and functional tests to any feature of bug you fix.
|
||||||
|
|
||||||
|
### Running Unit Tests
|
||||||
|
* `./gradlew :core:testPlayDebugUnitTest`
|
||||||
|
|
||||||
|
### Running Integration Tests
|
||||||
|
|
||||||
|
#### Using Android Studio
|
||||||
|
* Create a configuration via 'Run->Edit Configurations...'
|
||||||
|
|
||||||
|
<img width="768" alt="antennapod-run-tests"
|
||||||
|
src="https://user-images.githubusercontent.com/149837/105122859-e1317180-5a8b-11eb-8d45-d54a3b051a9b.png">
|
||||||
|
|
||||||
|
#### Using the command line
|
||||||
|
* Start an AVD or plug in your phone
|
||||||
|
* `sh .github/workflows/runTests.sh`
|
||||||
|
File diff suppressed because one or more lines are too long
@ -18,7 +18,7 @@ You can use the [AntennaPod Forum](https://forum.antennapod.org/) for discussion
|
|||||||
Bug reports and feature requests can be submitted [here](https://github.com/AntennaPod/AntennaPod/issues) (please read the [instructions](https://github.com/AntennaPod/AntennaPod/blob/master/CONTRIBUTING.md) on how to report a bug and how to submit a feature request first!).
|
Bug reports and feature requests can be submitted [here](https://github.com/AntennaPod/AntennaPod/issues) (please read the [instructions](https://github.com/AntennaPod/AntennaPod/blob/master/CONTRIBUTING.md) on how to report a bug and how to submit a feature request first!).
|
||||||
|
|
||||||
## Help to test AntennaPod
|
## Help to test AntennaPod
|
||||||
AntennaPod has many users and we don't want them to run into trouble when we add a new feature. It's important that we have a significant group test our app, so that we know all possible combinations of phones, Android versions and use cases work as expected. Check out our wiki on how to join our [Alpha and Beta testing programmes](https://github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod)!
|
AntennaPod has many users and we don't want them to run into trouble when we add a new feature. It's important that we have a significant group test our app, so that we know all possible combinations of phones, Android versions and use cases work as expected. Check out our wiki on how to join our [Beta testing program](https://antennapod.org/documentation/general/beta)! If a bug is reported during the beta period, chances are high that it will be fixed before the stable version. If it is reported later, fixing might take another full beta cycle. So definitely let us know if something is not right.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -19,11 +19,10 @@ android {
|
|||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
// Version code schema:
|
// Version code schema:
|
||||||
// "1.2.3-SNAPSHOT" -> 1020300
|
// "1.2.3-beta4" -> 1020304
|
||||||
// "1.2.3-RC4" -> 1020304
|
|
||||||
// "1.2.3" -> 1020395
|
// "1.2.3" -> 1020395
|
||||||
versionCode 2020000
|
versionCode 2020001
|
||||||
versionName "2.2.0"
|
versionName "2.2.0-beta1"
|
||||||
|
|
||||||
multiDexEnabled false
|
multiDexEnabled false
|
||||||
vectorDrawables.useSupportLibrary true
|
vectorDrawables.useSupportLibrary true
|
||||||
@ -156,14 +155,9 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
freeImplementation project(":core")
|
implementation project(":core")
|
||||||
// free build hack: skip some dependencies
|
implementation project(':ui:app-start-intent')
|
||||||
if (!doFreeBuild()) {
|
implementation project(':ui:common')
|
||||||
playImplementation project(":core")
|
|
||||||
implementation 'com.google.android.play:core:1.8.0'
|
|
||||||
} else {
|
|
||||||
System.out.println("app: free build hack, skipping some dependencies")
|
|
||||||
}
|
|
||||||
|
|
||||||
annotationProcessor "androidx.annotation:annotation:$annotationVersion"
|
annotationProcessor "androidx.annotation:annotation:$annotationVersion"
|
||||||
implementation "androidx.appcompat:appcompat:$appcompatVersion"
|
implementation "androidx.appcompat:appcompat:$appcompatVersion"
|
||||||
@ -191,14 +185,15 @@ dependencies {
|
|||||||
|
|
||||||
implementation "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
|
implementation "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
|
||||||
implementation "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
|
implementation "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
|
||||||
implementation 'com.yqritc:recyclerview-flexibledivider:1.4.0'
|
|
||||||
implementation 'com.github.shts:TriangleLabelView:1.1.2'
|
implementation 'com.github.shts:TriangleLabelView:1.1.2'
|
||||||
implementation 'com.leinardi.android:speed-dial:3.1.1'
|
implementation 'com.github.leinardi:FloatingActionButtonSpeedDial:3.1.1'
|
||||||
implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
|
implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
|
||||||
implementation 'com.github.mfietz:fyydlin:v0.5.0'
|
implementation 'com.github.mfietz:fyydlin:v0.5.0'
|
||||||
implementation 'com.github.ByteHamster:SearchPreference:v2.0.0'
|
implementation 'com.github.ByteHamster:SearchPreference:v2.0.0'
|
||||||
implementation 'com.github.skydoves:balloon:1.1.5'
|
implementation 'com.github.skydoves:balloon:1.1.5'
|
||||||
|
|
||||||
|
// Non-free dependencies:
|
||||||
|
playImplementation 'com.google.android.play:core:1.8.0'
|
||||||
compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion"
|
compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion"
|
||||||
|
|
||||||
androidTestImplementation "org.awaitility:awaitility:$awaitilityVersion"
|
androidTestImplementation "org.awaitility:awaitility:$awaitilityVersion"
|
||||||
|
@ -3,8 +3,10 @@ package de.test.antennapod;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import androidx.annotation.IdRes;
|
import androidx.annotation.IdRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.test.espresso.NoMatchingViewException;
|
||||||
import androidx.test.platform.app.InstrumentationRegistry;
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
import androidx.test.espresso.PerformException;
|
import androidx.test.espresso.PerformException;
|
||||||
import androidx.test.espresso.UiController;
|
import androidx.test.espresso.UiController;
|
||||||
@ -15,6 +17,9 @@ import androidx.test.espresso.contrib.RecyclerViewActions;
|
|||||||
import androidx.test.espresso.util.HumanReadables;
|
import androidx.test.espresso.util.HumanReadables;
|
||||||
import androidx.test.espresso.util.TreeIterables;
|
import androidx.test.espresso.util.TreeIterables;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import junit.framework.AssertionFailedError;
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
@ -33,6 +38,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
|
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
|
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
||||||
@ -57,7 +63,7 @@ public class EspressoTestUtils {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return "wait for a specific view for" + millis + " millis.";
|
return "wait for a specific view for " + millis + " millis.";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -87,6 +93,33 @@ public class EspressoTestUtils {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until a certain view becomes visible, but at the longest until the timeout.
|
||||||
|
* Unlike {@link #waitForView(Matcher, long)} it doesn't stick to the initial root view.
|
||||||
|
*
|
||||||
|
* @param viewMatcher The view to wait for.
|
||||||
|
* @param timeoutMillis Maximum waiting period in milliseconds.
|
||||||
|
* @throws Exception Throws an Exception in case of a timeout.
|
||||||
|
*/
|
||||||
|
public static void waitForViewGlobally(@NonNull Matcher<View> viewMatcher, long timeoutMillis) throws Exception {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
long endTime = startTime + timeoutMillis;
|
||||||
|
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
onView(viewMatcher).check(matches(isDisplayed()));
|
||||||
|
// no Exception thrown -> check successful
|
||||||
|
return;
|
||||||
|
} catch (NoMatchingViewException | AssertionFailedError ignore) {
|
||||||
|
// check was not successful "not found" -> continue waiting
|
||||||
|
}
|
||||||
|
//noinspection BusyWait
|
||||||
|
Thread.sleep(50);
|
||||||
|
} while (System.currentTimeMillis() < endTime);
|
||||||
|
|
||||||
|
throw new Exception("Timeout after " + timeoutMillis + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform action of waiting for a specific view id.
|
* Perform action of waiting for a specific view id.
|
||||||
* https://stackoverflow.com/a/30338665/
|
* https://stackoverflow.com/a/30338665/
|
||||||
@ -113,7 +146,7 @@ public class EspressoTestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all app databases
|
* Clear all app databases.
|
||||||
*/
|
*/
|
||||||
public static void clearPreferences() {
|
public static void clearPreferences() {
|
||||||
File root = InstrumentationRegistry.getInstrumentation().getTargetContext().getFilesDir().getParentFile();
|
File root = InstrumentationRegistry.getInstrumentation().getTargetContext().getFilesDir().getParentFile();
|
||||||
|
@ -11,15 +11,11 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||||
import androidx.test.platform.app.InstrumentationRegistry;
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
|
||||||
import de.danoeh.antennapod.core.storage.DBReader;
|
|
||||||
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
||||||
import de.test.antennapod.EspressoTestUtils;
|
import de.test.antennapod.EspressoTestUtils;
|
||||||
import de.test.antennapod.ui.UITestUtils;
|
import de.test.antennapod.ui.UITestUtils;
|
||||||
@ -70,7 +66,6 @@ public class ShareDialogTest {
|
|||||||
onView(withText(R.string.all_episodes_short_label)).perform(click());
|
onView(withText(R.string.all_episodes_short_label)).perform(click());
|
||||||
|
|
||||||
Matcher<View> allEpisodesMatcher;
|
Matcher<View> allEpisodesMatcher;
|
||||||
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
|
|
||||||
allEpisodesMatcher = Matchers.allOf(withId(android.R.id.list), isDisplayed(), hasMinimumChildCount(2));
|
allEpisodesMatcher = Matchers.allOf(withId(android.R.id.list), isDisplayed(), hasMinimumChildCount(2));
|
||||||
onView(isRoot()).perform(waitForView(allEpisodesMatcher, 1000));
|
onView(isRoot()).perform(waitForView(allEpisodesMatcher, 1000));
|
||||||
onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, click()));
|
onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, click()));
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
package de.test.antennapod.feed;
|
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
public class FeedItemTest {
|
|
||||||
private static final String TEXT_LONG = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
|
|
||||||
private static final String TEXT_SHORT = "Lorem ipsum";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If one of `description` or `content:encoded` is null, use the other one.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testShownotesNullValues() throws Exception {
|
|
||||||
testShownotes(null, TEXT_LONG);
|
|
||||||
testShownotes(TEXT_LONG, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If `description` is reasonably longer than `content:encoded`, use `description`.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testShownotesLength() throws Exception {
|
|
||||||
testShownotes(TEXT_SHORT, TEXT_LONG);
|
|
||||||
testShownotes(TEXT_LONG, TEXT_SHORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the shownotes equal TEXT_LONG, using the given `description` and `content:encoded`
|
|
||||||
* @param description Description of the feed item
|
|
||||||
* @param contentEncoded `content:encoded` of the feed item
|
|
||||||
*/
|
|
||||||
private void testShownotes(String description, String contentEncoded) throws Exception {
|
|
||||||
FeedItem item = new FeedItem();
|
|
||||||
item.setDescription(description);
|
|
||||||
item.setContentEncoded(contentEncoded);
|
|
||||||
assertEquals(TEXT_LONG, item.loadShownotes().call());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package de.test.antennapod.handler;
|
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
|
||||||
import de.danoeh.antennapod.core.feed.Feed;
|
|
||||||
import de.test.antennapod.util.syndication.feedgenerator.AtomGenerator;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for Atom feeds in FeedHandler.
|
|
||||||
*/
|
|
||||||
@SmallTest
|
|
||||||
public class AtomParserTest extends FeedParserTestBase {
|
|
||||||
@Test
|
|
||||||
public void testAtomBasic() throws Exception {
|
|
||||||
Feed f1 = createTestFeed(10, true);
|
|
||||||
Feed f2 = runFeedTest(f1, new AtomGenerator(), "UTF-8", 0);
|
|
||||||
feedValid(f1, f2, Feed.TYPE_ATOM1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLogoWithWhitespace() throws Exception {
|
|
||||||
String logo = "https://example.com/image.png";
|
|
||||||
Feed f1 = createTestFeed(0, false);
|
|
||||||
f1.setImageUrl(null);
|
|
||||||
Feed f2 = runFeedTest(f1, new AtomGenerator() {
|
|
||||||
@Override
|
|
||||||
protected void writeAdditionalAttributes(XmlSerializer xml) throws IOException {
|
|
||||||
xml.startTag(null, "logo");
|
|
||||||
xml.text(" " + logo + "\n");
|
|
||||||
xml.endTag(null, "logo");
|
|
||||||
}
|
|
||||||
}, "UTF-8", 0);
|
|
||||||
assertEquals(logo, f2.getImageUrl());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
package de.test.antennapod.handler;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry;
|
|
||||||
import de.danoeh.antennapod.core.feed.Chapter;
|
|
||||||
import de.danoeh.antennapod.core.feed.Feed;
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
|
||||||
import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
|
|
||||||
import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException;
|
|
||||||
import de.test.antennapod.util.syndication.feedgenerator.FeedGenerator;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for FeedHandler.
|
|
||||||
*/
|
|
||||||
public abstract class FeedParserTestBase {
|
|
||||||
private static final String FEEDS_DIR = "testfeeds";
|
|
||||||
|
|
||||||
private File file = null;
|
|
||||||
private OutputStream outputStream = null;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
|
||||||
File destDir = context.getExternalFilesDir(FEEDS_DIR);
|
|
||||||
assertNotNull(destDir);
|
|
||||||
|
|
||||||
file = new File(destDir, "feed.xml");
|
|
||||||
file.delete();
|
|
||||||
|
|
||||||
assertNotNull(file);
|
|
||||||
assertFalse(file.exists());
|
|
||||||
|
|
||||||
outputStream = new FileOutputStream(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() throws Exception {
|
|
||||||
file.delete();
|
|
||||||
file = null;
|
|
||||||
|
|
||||||
outputStream.close();
|
|
||||||
outputStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Feed runFeedTest(Feed feed, FeedGenerator g, String encoding, long flags)
|
|
||||||
throws IOException, UnsupportedFeedtypeException, SAXException, ParserConfigurationException {
|
|
||||||
g.writeFeed(feed, outputStream, encoding, flags);
|
|
||||||
FeedHandler handler = new FeedHandler();
|
|
||||||
Feed parsedFeed = new Feed(feed.getDownload_url(), feed.getLastUpdate());
|
|
||||||
parsedFeed.setFile_url(file.getAbsolutePath());
|
|
||||||
parsedFeed.setDownloaded(true);
|
|
||||||
handler.parseFeed(parsedFeed);
|
|
||||||
return parsedFeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void feedValid(Feed feed, Feed parsedFeed, String feedType) {
|
|
||||||
assertEquals(feed.getTitle(), parsedFeed.getTitle());
|
|
||||||
if (feedType.equals(Feed.TYPE_ATOM1)) {
|
|
||||||
assertEquals(feed.getFeedIdentifier(), parsedFeed.getFeedIdentifier());
|
|
||||||
} else {
|
|
||||||
assertEquals(feed.getLanguage(), parsedFeed.getLanguage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(feed.getLink(), parsedFeed.getLink());
|
|
||||||
assertEquals(feed.getDescription(), parsedFeed.getDescription());
|
|
||||||
assertEquals(feed.getPaymentLink(), parsedFeed.getPaymentLink());
|
|
||||||
assertEquals(feed.getImageUrl(), parsedFeed.getImageUrl());
|
|
||||||
|
|
||||||
if (feed.getItems() != null) {
|
|
||||||
assertNotNull(parsedFeed.getItems());
|
|
||||||
assertEquals(feed.getItems().size(), parsedFeed.getItems().size());
|
|
||||||
|
|
||||||
for (int i = 0; i < feed.getItems().size(); i++) {
|
|
||||||
FeedItem item = feed.getItems().get(i);
|
|
||||||
FeedItem parsedItem = parsedFeed.getItems().get(i);
|
|
||||||
|
|
||||||
if (item.getItemIdentifier() != null) {
|
|
||||||
assertEquals(item.getItemIdentifier(), parsedItem.getItemIdentifier());
|
|
||||||
}
|
|
||||||
assertEquals(item.getTitle(), parsedItem.getTitle());
|
|
||||||
assertEquals(item.getDescription(), parsedItem.getDescription());
|
|
||||||
assertEquals(item.getContentEncoded(), parsedItem.getContentEncoded());
|
|
||||||
assertEquals(item.getLink(), parsedItem.getLink());
|
|
||||||
assertEquals(item.getPubDate().getTime(), parsedItem.getPubDate().getTime());
|
|
||||||
assertEquals(item.getPaymentLink(), parsedItem.getPaymentLink());
|
|
||||||
|
|
||||||
if (item.hasMedia()) {
|
|
||||||
assertTrue(parsedItem.hasMedia());
|
|
||||||
FeedMedia media = item.getMedia();
|
|
||||||
FeedMedia parsedMedia = parsedItem.getMedia();
|
|
||||||
|
|
||||||
assertEquals(media.getDownload_url(), parsedMedia.getDownload_url());
|
|
||||||
assertEquals(media.getSize(), parsedMedia.getSize());
|
|
||||||
assertEquals(media.getMime_type(), parsedMedia.getMime_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(feed.getImageUrl(), item.getImageLocation());
|
|
||||||
|
|
||||||
if (item.getChapters() != null) {
|
|
||||||
assertNotNull(parsedItem.getChapters());
|
|
||||||
assertEquals(item.getChapters().size(), parsedItem.getChapters().size());
|
|
||||||
List<Chapter> chapters = item.getChapters();
|
|
||||||
List<Chapter> parsedChapters = parsedItem.getChapters();
|
|
||||||
for (int j = 0; j < chapters.size(); j++) {
|
|
||||||
Chapter chapter = chapters.get(j);
|
|
||||||
Chapter parsedChapter = parsedChapters.get(j);
|
|
||||||
|
|
||||||
assertEquals(chapter.getTitle(), parsedChapter.getTitle());
|
|
||||||
assertEquals(chapter.getLink(), parsedChapter.getLink());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Feed createTestFeed(int numItems, boolean withFeedMedia) {
|
|
||||||
Feed feed = new Feed(0, null, "title", "http://example.com", "This is the description",
|
|
||||||
"http://example.com/payment", "Daniel", "en", null, "http://example.com/feed",
|
|
||||||
"http://example.com/picture", file.getAbsolutePath(), "http://example.com/feed", true);
|
|
||||||
feed.setItems(new ArrayList<>());
|
|
||||||
|
|
||||||
for (int i = 0; i < numItems; i++) {
|
|
||||||
FeedItem item = new FeedItem(0, "item-" + i, "http://example.com/item-" + i,
|
|
||||||
"http://example.com/items/" + i, new Date(i * 60000), FeedItem.UNPLAYED, feed);
|
|
||||||
feed.getItems().add(item);
|
|
||||||
if (withFeedMedia) {
|
|
||||||
item.setMedia(new FeedMedia(0, item, 4711, 0, 1024 * 1024, "audio/mp3", null,
|
|
||||||
"http://example.com/media-" + i, false, null, 0, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return feed;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
package de.test.antennapod.handler;
|
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
|
||||||
import de.danoeh.antennapod.core.feed.Feed;
|
|
||||||
import de.danoeh.antennapod.core.feed.MediaType;
|
|
||||||
import de.danoeh.antennapod.core.syndication.namespace.NSMedia;
|
|
||||||
import de.test.antennapod.util.syndication.feedgenerator.Rss2Generator;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for RSS feeds in FeedHandler.
|
|
||||||
*/
|
|
||||||
@SmallTest
|
|
||||||
public class RssParserTest extends FeedParserTestBase {
|
|
||||||
@Test
|
|
||||||
public void testRss2Basic() throws Exception {
|
|
||||||
Feed f1 = createTestFeed(10, true);
|
|
||||||
Feed f2 = runFeedTest(f1, new Rss2Generator(), "UTF-8", Rss2Generator.FEATURE_WRITE_GUID);
|
|
||||||
feedValid(f1, f2, Feed.TYPE_RSS2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testImageWithWhitespace() throws Exception {
|
|
||||||
String image = "https://example.com/image.png";
|
|
||||||
Feed f1 = createTestFeed(0, false);
|
|
||||||
f1.setImageUrl(null);
|
|
||||||
Feed f2 = runFeedTest(f1, new Rss2Generator() {
|
|
||||||
@Override
|
|
||||||
protected void writeAdditionalAttributes(XmlSerializer xml) throws IOException {
|
|
||||||
xml.startTag(null, "image");
|
|
||||||
xml.startTag(null, "url");
|
|
||||||
xml.text(" " + image + "\n");
|
|
||||||
xml.endTag(null, "url");
|
|
||||||
xml.endTag(null, "image");
|
|
||||||
}
|
|
||||||
}, "UTF-8", 0);
|
|
||||||
assertEquals(image, f2.getImageUrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMediaContentMime() throws Exception {
|
|
||||||
Feed f1 = createTestFeed(0, false);
|
|
||||||
f1.setImageUrl(null);
|
|
||||||
Feed f2 = runFeedTest(f1, new Rss2Generator() {
|
|
||||||
@Override
|
|
||||||
protected void writeAdditionalAttributes(XmlSerializer xml) throws IOException {
|
|
||||||
xml.setPrefix(NSMedia.NSTAG, NSMedia.NSURI);
|
|
||||||
xml.startTag(null, "item");
|
|
||||||
xml.startTag(NSMedia.NSURI, "content");
|
|
||||||
xml.attribute(null, "url", "https://www.example.com/file.mp4");
|
|
||||||
xml.attribute(null, "medium", "video");
|
|
||||||
xml.endTag(NSMedia.NSURI, "content");
|
|
||||||
xml.endTag(null, "item");
|
|
||||||
}
|
|
||||||
}, "UTF-8", 0);
|
|
||||||
assertEquals(MediaType.VIDEO, f2.getItems().get(0).getMedia().getMediaType());
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,6 +10,7 @@ import androidx.test.filters.LargeTest;
|
|||||||
import androidx.test.platform.app.InstrumentationRegistry;
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
import androidx.test.rule.ActivityTestRule;
|
import androidx.test.rule.ActivityTestRule;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.core.feed.FeedItemFilter;
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
import org.hamcrest.Matcher;
|
import org.hamcrest.Matcher;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
@ -107,7 +108,12 @@ public class PlaybackTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupPlaybackController() {
|
private void setupPlaybackController() {
|
||||||
controller = new PlaybackController(activityTestRule.getActivity());
|
controller = new PlaybackController(activityTestRule.getActivity()) {
|
||||||
|
@Override
|
||||||
|
public void loadMediaInfo() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
};
|
||||||
controller.init();
|
controller.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +258,7 @@ public class PlaybackTest {
|
|||||||
onView(isRoot()).perform(waitForView(withText(R.string.all_episodes_short_label), 1000));
|
onView(isRoot()).perform(waitForView(withText(R.string.all_episodes_short_label), 1000));
|
||||||
onView(withText(R.string.all_episodes_short_label)).perform(click());
|
onView(withText(R.string.all_episodes_short_label)).perform(click());
|
||||||
|
|
||||||
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
|
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10, FeedItemFilter.unfiltered());
|
||||||
Matcher<View> allEpisodesMatcher = allOf(withId(android.R.id.list), isDisplayed(), hasMinimumChildCount(2));
|
Matcher<View> allEpisodesMatcher = allOf(withId(android.R.id.list), isDisplayed(), hasMinimumChildCount(2));
|
||||||
onView(isRoot()).perform(waitForView(allEpisodesMatcher, 1000));
|
onView(isRoot()).perform(waitForView(allEpisodesMatcher, 1000));
|
||||||
onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.secondaryActionButton)));
|
onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.secondaryActionButton)));
|
||||||
@ -287,7 +293,7 @@ public class PlaybackTest {
|
|||||||
uiTestUtils.addLocalFeedData(true);
|
uiTestUtils.addLocalFeedData(true);
|
||||||
DBWriter.clearQueue().get();
|
DBWriter.clearQueue().get();
|
||||||
activityTestRule.launchActivity(new Intent());
|
activityTestRule.launchActivity(new Intent());
|
||||||
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
|
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10, FeedItemFilter.unfiltered());
|
||||||
|
|
||||||
startLocalPlayback();
|
startLocalPlayback();
|
||||||
FeedMedia media = episodes.get(0).getMedia();
|
FeedMedia media = episodes.get(0).getMedia();
|
||||||
|
@ -6,6 +6,7 @@ import androidx.test.annotation.UiThreadTest;
|
|||||||
import androidx.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
|
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
|
||||||
|
import de.danoeh.antennapod.core.widget.WidgetUpdater;
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
@ -187,8 +188,8 @@ public class PlaybackServiceTaskManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWidgetUpdaterTick() {
|
public WidgetUpdater.WidgetState requestWidgetState() {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -248,8 +249,9 @@ public class PlaybackServiceTaskManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWidgetUpdaterTick() {
|
public WidgetUpdater.WidgetState requestWidgetState() {
|
||||||
countDownLatch.countDown();
|
countDownLatch.countDown();
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -348,8 +350,8 @@ public class PlaybackServiceTaskManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWidgetUpdaterTick() {
|
public WidgetUpdater.WidgetState requestWidgetState() {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -391,8 +393,8 @@ public class PlaybackServiceTaskManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWidgetUpdaterTick() {
|
public WidgetUpdater.WidgetState requestWidgetState() {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -449,8 +451,8 @@ public class PlaybackServiceTaskManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWidgetUpdaterTick() {
|
public WidgetUpdater.WidgetState requestWidgetState() {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3,13 +3,13 @@ package de.test.antennapod.storage;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import de.danoeh.antennapod.core.ClientConfig;
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||||
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm;
|
import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm;
|
||||||
import de.danoeh.antennapod.core.storage.DBReader;
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||||
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
|
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
|
||||||
import de.test.antennapod.EspressoTestUtils;
|
import de.test.antennapod.EspressoTestUtils;
|
||||||
import de.test.antennapod.ui.UITestUtils;
|
import de.test.antennapod.ui.UITestUtils;
|
||||||
@ -29,8 +29,7 @@ public class AutoDownloadTest {
|
|||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
private UITestUtils stubFeedsServer;
|
private UITestUtils stubFeedsServer;
|
||||||
|
private StubDownloadAlgorithm stubDownloadAlgorithm;
|
||||||
private AutomaticDownloadAlgorithm automaticDownloadAlgorithmOrig;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
@ -39,16 +38,19 @@ public class AutoDownloadTest {
|
|||||||
stubFeedsServer = new UITestUtils(context);
|
stubFeedsServer = new UITestUtils(context);
|
||||||
stubFeedsServer.setup();
|
stubFeedsServer.setup();
|
||||||
|
|
||||||
automaticDownloadAlgorithmOrig = ClientConfig.automaticDownloadAlgorithm;
|
|
||||||
|
|
||||||
EspressoTestUtils.clearPreferences();
|
EspressoTestUtils.clearPreferences();
|
||||||
EspressoTestUtils.clearDatabase();
|
EspressoTestUtils.clearDatabase();
|
||||||
UserPreferences.setAllowMobileStreaming(true);
|
UserPreferences.setAllowMobileStreaming(true);
|
||||||
|
|
||||||
|
// Setup: enable automatic download
|
||||||
|
// it is not needed, as the actual automatic download is stubbed.
|
||||||
|
stubDownloadAlgorithm = new StubDownloadAlgorithm();
|
||||||
|
DBTasks.setDownloadAlgorithm(stubDownloadAlgorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
ClientConfig.automaticDownloadAlgorithm = automaticDownloadAlgorithmOrig;
|
DBTasks.setDownloadAlgorithm(new AutomaticDownloadAlgorithm());
|
||||||
EspressoTestUtils.tryKillPlaybackService();
|
EspressoTestUtils.tryKillPlaybackService();
|
||||||
stubFeedsServer.tearDown();
|
stubFeedsServer.tearDown();
|
||||||
}
|
}
|
||||||
@ -74,11 +76,6 @@ public class AutoDownloadTest {
|
|||||||
FeedItem item0 = queue.get(0);
|
FeedItem item0 = queue.get(0);
|
||||||
FeedItem item1 = queue.get(1);
|
FeedItem item1 = queue.get(1);
|
||||||
|
|
||||||
// Setup: enable automatic download
|
|
||||||
// it is not needed, as the actual automatic download is stubbed.
|
|
||||||
StubDownloadAlgorithm stubDownloadAlgorithm = new StubDownloadAlgorithm();
|
|
||||||
ClientConfig.automaticDownloadAlgorithm = stubDownloadAlgorithm;
|
|
||||||
|
|
||||||
// Actual test
|
// Actual test
|
||||||
// Play the first one in the queue
|
// Play the first one in the queue
|
||||||
playEpisode(item0);
|
playEpisode(item0);
|
||||||
@ -92,11 +89,10 @@ public class AutoDownloadTest {
|
|||||||
} catch (ConditionTimeoutException cte) {
|
} catch (ConditionTimeoutException cte) {
|
||||||
long actual = stubDownloadAlgorithm.getCurrentlyPlayingAtDownload();
|
long actual = stubDownloadAlgorithm.getCurrentlyPlayingAtDownload();
|
||||||
fail("when auto download is triggered, the next episode should be playing: ("
|
fail("when auto download is triggered, the next episode should be playing: ("
|
||||||
+ item1.getId() + ", " + item1.getTitle() + ") . "
|
+ item1.getId() + ", " + item1.getTitle() + ") . "
|
||||||
+ "Actual playing: (" + actual + ")"
|
+ "Actual playing: (" + actual + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void playEpisode(@NonNull FeedItem item) {
|
private void playEpisode(@NonNull FeedItem item) {
|
||||||
@ -111,7 +107,7 @@ public class AutoDownloadTest {
|
|||||||
.until(() -> item.getMedia().getId() == PlaybackPreferences.getCurrentlyPlayingFeedMediaId());
|
.until(() -> item.getMedia().getId() == PlaybackPreferences.getCurrentlyPlayingFeedMediaId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class StubDownloadAlgorithm implements AutomaticDownloadAlgorithm {
|
private static class StubDownloadAlgorithm extends AutomaticDownloadAlgorithm {
|
||||||
private long currentlyPlaying = -1;
|
private long currentlyPlaying = -1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -2,16 +2,14 @@ package de.test.antennapod.ui;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import androidx.test.platform.app.InstrumentationRegistry;
|
|
||||||
import androidx.test.espresso.Espresso;
|
import androidx.test.espresso.Espresso;
|
||||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
|
||||||
import com.robotium.solo.Solo;
|
import com.robotium.solo.Solo;
|
||||||
import de.danoeh.antennapod.R;
|
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
|
||||||
import de.danoeh.antennapod.core.feed.Feed;
|
|
||||||
import de.danoeh.antennapod.core.storage.PodDBAdapter;
|
|
||||||
import de.test.antennapod.EspressoTestUtils;
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
@ -20,6 +18,12 @@ import org.junit.runner.RunWith;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
|
import de.danoeh.antennapod.core.feed.Feed;
|
||||||
|
import de.danoeh.antennapod.core.storage.PodDBAdapter;
|
||||||
|
import de.test.antennapod.EspressoTestUtils;
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||||
@ -28,18 +32,17 @@ import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
|||||||
import static androidx.test.espresso.contrib.ActivityResultMatchers.hasResultCode;
|
import static androidx.test.espresso.contrib.ActivityResultMatchers.hasResultCode;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
|
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
import static de.test.antennapod.EspressoTestUtils.clickPreference;
|
import static de.test.antennapod.EspressoTestUtils.clickPreference;
|
||||||
import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
|
import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
|
||||||
import static de.test.antennapod.EspressoTestUtils.waitForView;
|
import static de.test.antennapod.EspressoTestUtils.waitForViewGlobally;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User interface tests for MainActivity
|
* User interface tests for MainActivity.
|
||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class MainActivityTest {
|
public class MainActivityTest {
|
||||||
@ -48,19 +51,19 @@ public class MainActivityTest {
|
|||||||
private UITestUtils uiTestUtils;
|
private UITestUtils uiTestUtils;
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public IntentsTestRule<MainActivity> mActivityRule = new IntentsTestRule<>(MainActivity.class, false, false);
|
public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(MainActivity.class, false, false);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws IOException {
|
public void setUp() throws IOException {
|
||||||
EspressoTestUtils.clearPreferences();
|
EspressoTestUtils.clearPreferences();
|
||||||
EspressoTestUtils.clearDatabase();
|
EspressoTestUtils.clearDatabase();
|
||||||
|
|
||||||
mActivityRule.launchActivity(new Intent());
|
activityRule.launchActivity(new Intent());
|
||||||
|
|
||||||
uiTestUtils = new UITestUtils(InstrumentationRegistry.getInstrumentation().getTargetContext());
|
uiTestUtils = new UITestUtils(InstrumentationRegistry.getInstrumentation().getTargetContext());
|
||||||
uiTestUtils.setup();
|
uiTestUtils.setup();
|
||||||
|
|
||||||
solo = new Solo(InstrumentationRegistry.getInstrumentation(), mActivityRule.getActivity());
|
solo = new Solo(InstrumentationRegistry.getInstrumentation(), activityRule.getActivity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -71,6 +74,7 @@ public class MainActivityTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddFeed() throws Exception {
|
public void testAddFeed() throws Exception {
|
||||||
|
// connect to podcast feed
|
||||||
uiTestUtils.addHostedFeedData();
|
uiTestUtils.addHostedFeedData();
|
||||||
final Feed feed = uiTestUtils.hostedFeeds.get(0);
|
final Feed feed = uiTestUtils.hostedFeeds.get(0);
|
||||||
openNavDrawer();
|
openNavDrawer();
|
||||||
@ -78,9 +82,14 @@ public class MainActivityTest {
|
|||||||
onView(withId(R.id.addViaUrlButton)).perform(scrollTo(), click());
|
onView(withId(R.id.addViaUrlButton)).perform(scrollTo(), click());
|
||||||
onView(withId(R.id.urlEditText)).perform(replaceText(feed.getDownload_url()));
|
onView(withId(R.id.urlEditText)).perform(replaceText(feed.getDownload_url()));
|
||||||
onView(withText(R.string.confirm_label)).perform(scrollTo(), click());
|
onView(withText(R.string.confirm_label)).perform(scrollTo(), click());
|
||||||
|
|
||||||
|
// subscribe podcast
|
||||||
Espresso.closeSoftKeyboard();
|
Espresso.closeSoftKeyboard();
|
||||||
|
waitForViewGlobally(withText(R.string.subscribe_label), 15000);
|
||||||
onView(withText(R.string.subscribe_label)).perform(click());
|
onView(withText(R.string.subscribe_label)).perform(click());
|
||||||
onView(isRoot()).perform(waitForView(withId(R.id.butShowSettings), 5000));
|
|
||||||
|
// wait for podcast feed item list
|
||||||
|
waitForViewGlobally(withId(R.id.butShowSettings), 15000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -100,7 +109,7 @@ public class MainActivityTest {
|
|||||||
onView(allOf(withId(R.id.toolbar), isDisplayed())).check(
|
onView(allOf(withId(R.id.toolbar), isDisplayed())).check(
|
||||||
matches(hasDescendant(withText(R.string.subscriptions_label))));
|
matches(hasDescendant(withText(R.string.subscriptions_label))));
|
||||||
solo.goBack();
|
solo.goBack();
|
||||||
assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
|
assertThat(activityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -113,7 +122,7 @@ public class MainActivityTest {
|
|||||||
solo.goBackToActivity(MainActivity.class.getSimpleName());
|
solo.goBackToActivity(MainActivity.class.getSimpleName());
|
||||||
solo.goBack();
|
solo.goBack();
|
||||||
solo.goBack();
|
solo.goBack();
|
||||||
assertTrue(((MainActivity)solo.getCurrentActivity()).isDrawerOpen());
|
assertTrue(((MainActivity) solo.getCurrentActivity()).isDrawerOpen());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -127,7 +136,7 @@ public class MainActivityTest {
|
|||||||
solo.goBack();
|
solo.goBack();
|
||||||
solo.goBack();
|
solo.goBack();
|
||||||
solo.goBack();
|
solo.goBack();
|
||||||
assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
|
assertThat(activityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -142,7 +151,7 @@ public class MainActivityTest {
|
|||||||
solo.goBack();
|
solo.goBack();
|
||||||
onView(withText(R.string.yes)).perform(click());
|
onView(withText(R.string.yes)).perform(click());
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
|
assertThat(activityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -155,6 +164,6 @@ public class MainActivityTest {
|
|||||||
solo.goBackToActivity(MainActivity.class.getSimpleName());
|
solo.goBackToActivity(MainActivity.class.getSimpleName());
|
||||||
solo.goBack();
|
solo.goBack();
|
||||||
solo.goBack();
|
solo.goBack();
|
||||||
assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
|
assertThat(activityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
|
|||||||
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
|
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
|
||||||
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
|
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
|
||||||
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
|
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
|
||||||
|
import de.danoeh.antennapod.core.storage.ExceptFavoriteCleanupAlgorithm;
|
||||||
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
||||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||||
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
||||||
@ -371,6 +372,17 @@ public class PreferencesTest {
|
|||||||
.until(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery());
|
.until(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEpisodeCleanupFavoriteOnly() {
|
||||||
|
clickPreference(R.string.network_pref);
|
||||||
|
onView(withText(R.string.pref_automatic_download_title)).perform(click());
|
||||||
|
onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
|
||||||
|
onView(isRoot()).perform(waitForView(withText(R.string.episode_cleanup_except_favorite_removal), 1000));
|
||||||
|
onView(withText(R.string.episode_cleanup_except_favorite_removal)).perform(click());
|
||||||
|
Awaitility.await().atMost(1000, MILLISECONDS)
|
||||||
|
.until(() -> UserPreferences.getEpisodeCleanupAlgorithm() instanceof ExceptFavoriteCleanupAlgorithm);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEpisodeCleanupQueueOnly() {
|
public void testEpisodeCleanupQueueOnly() {
|
||||||
clickPreference(R.string.network_pref);
|
clickPreference(R.string.network_pref);
|
||||||
|
@ -15,6 +15,7 @@ import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
|||||||
import de.danoeh.antennapod.core.storage.DBReader;
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||||
|
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
|
||||||
import de.test.antennapod.EspressoTestUtils;
|
import de.test.antennapod.EspressoTestUtils;
|
||||||
import de.test.antennapod.IgnoreOnCi;
|
import de.test.antennapod.IgnoreOnCi;
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
@ -71,8 +72,13 @@ public class SpeedChangeTest {
|
|||||||
UserPreferences.setPlaybackSpeedArray(Arrays.asList(1.0f, 2.0f, 3.0f));
|
UserPreferences.setPlaybackSpeedArray(Arrays.asList(1.0f, 2.0f, 3.0f));
|
||||||
|
|
||||||
EspressoTestUtils.tryKillPlaybackService();
|
EspressoTestUtils.tryKillPlaybackService();
|
||||||
activityRule.launchActivity(new Intent().putExtra(MainActivity.EXTRA_OPEN_PLAYER, true));
|
activityRule.launchActivity(new Intent().putExtra(MainActivityStarter.EXTRA_OPEN_PLAYER, true));
|
||||||
controller = new PlaybackController(activityRule.getActivity());
|
controller = new PlaybackController(activityRule.getActivity()) {
|
||||||
|
@Override
|
||||||
|
public void loadMediaInfo() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
};
|
||||||
controller.init();
|
controller.init();
|
||||||
controller.getMedia(); // To load media
|
controller.getMedia(); // To load media
|
||||||
}
|
}
|
||||||
|
@ -1,131 +0,0 @@
|
|||||||
package de.test.antennapod.util.syndication.feedgenerator;
|
|
||||||
|
|
||||||
import android.util.Xml;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.feed.Feed;
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
|
||||||
import de.danoeh.antennapod.core.util.DateUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates Atom feeds. See FeedGenerator for more information.
|
|
||||||
*/
|
|
||||||
public class AtomGenerator implements FeedGenerator {
|
|
||||||
|
|
||||||
private static final String NS_ATOM = "http://www.w3.org/2005/Atom";
|
|
||||||
|
|
||||||
private static final long FEATURE_USE_RFC3339LOCAL = 1;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeFeed(Feed feed, OutputStream outputStream, String encoding, long flags) throws IOException {
|
|
||||||
if (feed == null) throw new IllegalArgumentException("feed = null");
|
|
||||||
if (outputStream == null) throw new IllegalArgumentException("outputStream = null");
|
|
||||||
if (encoding == null) throw new IllegalArgumentException("encoding = null");
|
|
||||||
|
|
||||||
XmlSerializer xml = Xml.newSerializer();
|
|
||||||
xml.setOutput(outputStream, encoding);
|
|
||||||
xml.startDocument(encoding, null);
|
|
||||||
|
|
||||||
xml.startTag(null, "feed");
|
|
||||||
xml.attribute(null, "xmlns", NS_ATOM);
|
|
||||||
|
|
||||||
// Write Feed data
|
|
||||||
if (feed.getIdentifyingValue() != null) {
|
|
||||||
xml.startTag(null, "id");
|
|
||||||
xml.text(feed.getIdentifyingValue());
|
|
||||||
xml.endTag(null, "id");
|
|
||||||
}
|
|
||||||
if (feed.getTitle() != null) {
|
|
||||||
xml.startTag(null, "title");
|
|
||||||
xml.text(feed.getTitle());
|
|
||||||
xml.endTag(null, "title");
|
|
||||||
}
|
|
||||||
if (feed.getLink() != null) {
|
|
||||||
xml.startTag(null, "link");
|
|
||||||
xml.attribute(null, "rel", "alternate");
|
|
||||||
xml.attribute(null, "href", feed.getLink());
|
|
||||||
xml.endTag(null, "link");
|
|
||||||
}
|
|
||||||
if (feed.getDescription() != null) {
|
|
||||||
xml.startTag(null, "subtitle");
|
|
||||||
xml.text(feed.getDescription());
|
|
||||||
xml.endTag(null, "subtitle");
|
|
||||||
}
|
|
||||||
if (feed.getImageUrl() != null) {
|
|
||||||
xml.startTag(null, "logo");
|
|
||||||
xml.text(feed.getImageUrl());
|
|
||||||
xml.endTag(null, "logo");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (feed.getPaymentLink() != null) {
|
|
||||||
GeneratorUtil.addPaymentLink(xml, feed.getPaymentLink(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write FeedItem data
|
|
||||||
if (feed.getItems() != null) {
|
|
||||||
for (FeedItem item : feed.getItems()) {
|
|
||||||
xml.startTag(null, "entry");
|
|
||||||
|
|
||||||
if (item.getIdentifyingValue() != null) {
|
|
||||||
xml.startTag(null, "id");
|
|
||||||
xml.text(item.getIdentifyingValue());
|
|
||||||
xml.endTag(null, "id");
|
|
||||||
}
|
|
||||||
if (item.getTitle() != null) {
|
|
||||||
xml.startTag(null, "title");
|
|
||||||
xml.text(item.getTitle());
|
|
||||||
xml.endTag(null, "title");
|
|
||||||
}
|
|
||||||
if (item.getLink() != null) {
|
|
||||||
xml.startTag(null, "link");
|
|
||||||
xml.attribute(null, "rel", "alternate");
|
|
||||||
xml.attribute(null, "href", item.getLink());
|
|
||||||
xml.endTag(null, "link");
|
|
||||||
}
|
|
||||||
if (item.getPubDate() != null) {
|
|
||||||
xml.startTag(null, "published");
|
|
||||||
if ((flags & FEATURE_USE_RFC3339LOCAL) != 0) {
|
|
||||||
xml.text(DateUtils.formatRFC3339Local(item.getPubDate()));
|
|
||||||
} else {
|
|
||||||
xml.text(DateUtils.formatRFC3339UTC(item.getPubDate()));
|
|
||||||
}
|
|
||||||
xml.endTag(null, "published");
|
|
||||||
}
|
|
||||||
if (item.getDescription() != null) {
|
|
||||||
xml.startTag(null, "content");
|
|
||||||
xml.text(item.getDescription());
|
|
||||||
xml.endTag(null, "content");
|
|
||||||
}
|
|
||||||
if (item.getMedia() != null) {
|
|
||||||
FeedMedia media = item.getMedia();
|
|
||||||
xml.startTag(null, "link");
|
|
||||||
xml.attribute(null, "rel", "enclosure");
|
|
||||||
xml.attribute(null, "href", media.getDownload_url());
|
|
||||||
xml.attribute(null, "type", media.getMime_type());
|
|
||||||
xml.attribute(null, "length", String.valueOf(media.getSize()));
|
|
||||||
xml.endTag(null, "link");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.getPaymentLink() != null) {
|
|
||||||
GeneratorUtil.addPaymentLink(xml, item.getPaymentLink(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
xml.endTag(null, "entry");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeAdditionalAttributes(xml);
|
|
||||||
|
|
||||||
xml.endTag(null, "feed");
|
|
||||||
xml.endDocument();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeAdditionalAttributes(XmlSerializer xml) throws IOException {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -60,7 +60,8 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".activity.SplashActivity"
|
android:name=".activity.SplashActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize">
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
@ -83,11 +84,39 @@
|
|||||||
android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|density|uiMode|keyboard|navigation"
|
android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|density|uiMode|keyboard|navigation"
|
||||||
android:windowSoftInputMode="stateAlwaysHidden"
|
android:windowSoftInputMode="stateAlwaysHidden"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="antennapod.org"
|
||||||
|
android:pathPrefix="/deeplink/main"
|
||||||
|
android:scheme="https" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="antennapod.org"
|
||||||
|
android:pathPrefix="/deeplink/search"
|
||||||
|
android:scheme="https" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="de.danoeh.antennapod.intents.MAIN_ACTIVITY" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.DownloadAuthenticationActivity"
|
android:name=".activity.DownloadAuthenticationActivity"
|
||||||
|
android:theme="@style/Theme.AntennaPod.Dark.Translucent"
|
||||||
android:launchMode="singleInstance"/>
|
android:launchMode="singleInstance"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
@ -101,7 +130,8 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.WidgetConfigActivity"
|
android:name=".activity.WidgetConfigActivity"
|
||||||
android:label="@string/widget_settings">
|
android:label="@string/widget_settings"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGUR"/>
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGUR"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@ -114,21 +144,21 @@
|
|||||||
android:exported="false">
|
android:exported="false">
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<receiver android:name=".core.receiver.PlayerWidget">
|
<receiver
|
||||||
|
android:name=".core.receiver.PlayerWidget"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="de.danoeh.antennapod.FORCE_WIDGET_UPDATE"/>
|
<action android:name="de.danoeh.antennapod.FORCE_WIDGET_UPDATE"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="android.appwidget.provider"
|
|
||||||
android:resource="@xml/player_widget_info"/>
|
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="de.danoeh.antennapod.STOP_WIDGET_UPDATE"/>
|
<action android:name="de.danoeh.antennapod.STOP_WIDGET_UPDATE"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/player_widget_info"/>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<activity android:name=".activity.StorageErrorActivity">
|
<activity android:name=".activity.StorageErrorActivity">
|
||||||
@ -136,7 +166,8 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".activity.OpmlImportActivity"
|
android:name=".activity.OpmlImportActivity"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:label="@string/opml_import_label">
|
android:label="@string/opml_import_label"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
@ -185,18 +216,14 @@
|
|||||||
android:name=".activity.VideoplayerActivity"
|
android:name=".activity.VideoplayerActivity"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:screenOrientation="sensorLandscape">
|
android:screenOrientation="sensorLandscape"
|
||||||
|
android:exported="false">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="de.danoeh.antennapod.activity.MainActivity"/>
|
android:value="de.danoeh.antennapod.activity.MainActivity"/>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="de.danoeh.antennapod.intents.VIDEO_PLAYER" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
|
||||||
|
|
||||||
<data android:scheme="file"/>
|
|
||||||
<data android:mimeType="video/*"/>
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
@ -204,7 +231,8 @@
|
|||||||
android:name=".activity.OnlineFeedViewActivity"
|
android:name=".activity.OnlineFeedViewActivity"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:theme="@style/Theme.AntennaPod.Dark.Translucent"
|
android:theme="@style/Theme.AntennaPod.Dark.Translucent"
|
||||||
android:label="@string/add_feed_label">
|
android:label="@string/add_feed_label"
|
||||||
|
android:exported="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="de.danoeh.antennapod.activity.MainActivity"/>
|
android:value="de.danoeh.antennapod.activity.MainActivity"/>
|
||||||
@ -292,33 +320,26 @@
|
|||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<receiver
|
||||||
android:name=".activity.gpoddernet.GpodnetAuthenticationActivity"
|
android:name=".receiver.ConnectivityActionReceiver"
|
||||||
android:configChanges="orientation"
|
android:exported="true">
|
||||||
android:label="@string/gpodnet_auth_label">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name=".activity.gpoddernet.GpodnetAuthenticationActivity"/>
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
|
||||||
android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<receiver android:name=".receiver.ConnectivityActionReceiver">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
|
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.PowerConnectionReceiver">
|
<receiver
|
||||||
|
android:name=".receiver.PowerConnectionReceiver"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
|
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
|
||||||
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
|
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.SPAReceiver">
|
<receiver
|
||||||
|
android:name=".receiver.SPAReceiver"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS_RESPONSE"/>
|
<action android:name="de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS_RESPONSE"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@ -333,6 +354,10 @@
|
|||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/provider_paths"/>
|
android:resource="@xml/provider_paths"/>
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.actions"
|
||||||
|
android:resource="@xml/actions" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -4,14 +4,14 @@ mfietz;6860662;Maintainer (retired)
|
|||||||
TomHennen;5216560;Maintainer (retired)
|
TomHennen;5216560;Maintainer (retired)
|
||||||
orionlee;250644;Contributor
|
orionlee;250644;Contributor
|
||||||
domingos86;9538859;Contributor
|
domingos86;9538859;Contributor
|
||||||
|
damoasda;46045854;Contributor
|
||||||
tonytamsf;149837;Contributor
|
tonytamsf;149837;Contributor
|
||||||
andersonvom;69922;Contributor
|
andersonvom;69922;Contributor
|
||||||
damoasda;46045854;Contributor
|
|
||||||
TacoTheDank;32376686;Contributor
|
TacoTheDank;32376686;Contributor
|
||||||
shortspider;5712543;Contributor
|
shortspider;5712543;Contributor
|
||||||
|
spacecowboy;223655;Contributor
|
||||||
ebraminio;833473;Contributor
|
ebraminio;833473;Contributor
|
||||||
asdoi;36813904;Contributor
|
asdoi;36813904;Contributor
|
||||||
spacecowboy;223655;Contributor
|
|
||||||
patheticpat;16046;Contributor
|
patheticpat;16046;Contributor
|
||||||
brad;1614;Contributor
|
brad;1614;Contributor
|
||||||
Cj-Malone;10121513;Contributor
|
Cj-Malone;10121513;Contributor
|
||||||
@ -44,6 +44,7 @@ mchelen;30691;Contributor
|
|||||||
dethstar;1239177;Contributor
|
dethstar;1239177;Contributor
|
||||||
drabux;10663142;Contributor
|
drabux;10663142;Contributor
|
||||||
saqura;1935380;Contributor
|
saqura;1935380;Contributor
|
||||||
|
binarytoto;75904760;Contributor
|
||||||
bibz;5141956;Contributor
|
bibz;5141956;Contributor
|
||||||
hzulla;1705654;Contributor
|
hzulla;1705654;Contributor
|
||||||
deandreamatias;21011641;Contributor
|
deandreamatias;21011641;Contributor
|
||||||
@ -65,6 +66,7 @@ HrBDev;25826502;Contributor
|
|||||||
HolgerJeromin;2410353;Contributor
|
HolgerJeromin;2410353;Contributor
|
||||||
xisberto;1914956;Contributor
|
xisberto;1914956;Contributor
|
||||||
jmue;898577;Contributor
|
jmue;898577;Contributor
|
||||||
|
jonasburian;15125616;Contributor
|
||||||
katrinleinweber;9948149;Contributor
|
katrinleinweber;9948149;Contributor
|
||||||
LatinSuD;451487;Contributor
|
LatinSuD;451487;Contributor
|
||||||
24hours;650407;Contributor
|
24hours;650407;Contributor
|
||||||
@ -74,25 +76,27 @@ archibishop;36948493;Contributor
|
|||||||
alifeflow;24603829;Contributor
|
alifeflow;24603829;Contributor
|
||||||
avirajrsingh;69088913;Contributor
|
avirajrsingh;69088913;Contributor
|
||||||
toggles;14695;Contributor
|
toggles;14695;Contributor
|
||||||
|
connectety;26038710;Contributor
|
||||||
matdb;48329535;Contributor
|
matdb;48329535;Contributor
|
||||||
damlayildiz;56313500;Contributor
|
damlayildiz;56313500;Contributor
|
||||||
kingargyle;177042;Contributor
|
kingargyle;177042;Contributor
|
||||||
dsmith47;14109426;Contributor
|
dsmith47;14109426;Contributor
|
||||||
hannesaa2;18496079;Contributor
|
hannesaa2;18496079;Contributor
|
||||||
jhunnius;9149031;Contributor
|
jhunnius;9149031;Contributor
|
||||||
|
a1291762;327162;Contributor
|
||||||
ShadowIce;59123;Contributor
|
ShadowIce;59123;Contributor
|
||||||
Niffler;8172446;Contributor
|
Niffler;8172446;Contributor
|
||||||
raghulj;57007;Contributor
|
raghulj;57007;Contributor
|
||||||
raghulrm;5362986;Contributor
|
raghulrm;5362986;Contributor
|
||||||
mamehacker;16738348;Contributor
|
mamehacker;16738348;Contributor
|
||||||
skitt;2128935;Contributor
|
skitt;2128935;Contributor
|
||||||
|
Thom-Merrilin;76849828;Contributor
|
||||||
wseemann;2296196;Contributor
|
wseemann;2296196;Contributor
|
||||||
markamaze;17114678;Contributor
|
markamaze;17114678;Contributor
|
||||||
mohitshah3111999;42018918;Contributor
|
mohitshah3111999;42018918;Contributor
|
||||||
moralesg;14352147;Contributor
|
moralesg;14352147;Contributor
|
||||||
mr-intj;6268767;Contributor
|
mr-intj;6268767;Contributor
|
||||||
tuxayo;2678215;Contributor
|
tuxayo;2678215;Contributor
|
||||||
schlch;56929215;Contributor
|
|
||||||
alimemonzx;44647595;Contributor
|
alimemonzx;44647595;Contributor
|
||||||
dev-darrell;52300159;Contributor
|
dev-darrell;52300159;Contributor
|
||||||
jmdouglas;10855634;Contributor
|
jmdouglas;10855634;Contributor
|
||||||
@ -106,24 +110,27 @@ arantius;84729;Contributor
|
|||||||
BoJacobs;25435640;Contributor
|
BoJacobs;25435640;Contributor
|
||||||
chetan882777;36985543;Contributor
|
chetan882777;36985543;Contributor
|
||||||
chrissicool;232590;Contributor
|
chrissicool;232590;Contributor
|
||||||
|
britiger;2057760;Contributor
|
||||||
cszucko;1810383;Contributor
|
cszucko;1810383;Contributor
|
||||||
CWftw;1498303;Contributor
|
CWftw;1498303;Contributor
|
||||||
connectety;26038710;Contributor
|
|
||||||
danielm5;66779;Contributor
|
danielm5;66779;Contributor
|
||||||
ariedov;958646;Contributor
|
ariedov;958646;Contributor
|
||||||
brettle;118192;Contributor
|
brettle;118192;Contributor
|
||||||
edwinhere;19705425;Contributor
|
edwinhere;19705425;Contributor
|
||||||
eirikv;4076243;Contributor
|
eirikv;4076243;Contributor
|
||||||
eerden;277513;Contributor
|
eerden;277513;Contributor
|
||||||
|
Geist5000;37940313;Contributor
|
||||||
jklippel;8657220;Contributor
|
jklippel;8657220;Contributor
|
||||||
jannic;232606;Contributor
|
jannic;232606;Contributor
|
||||||
Foso;5015532;Contributor
|
Foso;5015532;Contributor
|
||||||
Kaligule;3586246;Contributor
|
Kaligule;3586246;Contributor
|
||||||
kvithayathil;1056073;Contributor
|
kvithayathil;1056073;Contributor
|
||||||
luiscruz;1080714;Contributor
|
luiscruz;1080714;Contributor
|
||||||
|
MStrecke;5202211;Contributor
|
||||||
mlasson;5814258;Contributor
|
mlasson;5814258;Contributor
|
||||||
schwedenmut;9077622;Contributor
|
schwedenmut;9077622;Contributor
|
||||||
M-arcel;56698158;Contributor
|
M-arcel;56698158;Contributor
|
||||||
|
mgborowiec;29843126;Contributor
|
||||||
msoose;30473690;Contributor
|
msoose;30473690;Contributor
|
||||||
mo;7117;Contributor
|
mo;7117;Contributor
|
||||||
mdeveloper20;2319126;Contributor
|
mdeveloper20;2319126;Contributor
|
||||||
@ -138,6 +145,7 @@ ortylp;470439;Contributor
|
|||||||
ramzan;55637406;Contributor
|
ramzan;55637406;Contributor
|
||||||
iamrichR;44210678;Contributor
|
iamrichR;44210678;Contributor
|
||||||
SamWhited;512573;Contributor
|
SamWhited;512573;Contributor
|
||||||
|
SebiderSushi;23618858;Contributor
|
||||||
selivan;1208989;Contributor
|
selivan;1208989;Contributor
|
||||||
sonnayasomnambula;7716779;Contributor
|
sonnayasomnambula;7716779;Contributor
|
||||||
sethoscope;534043;Contributor
|
sethoscope;534043;Contributor
|
||||||
@ -148,14 +156,17 @@ vimsick;20211590;Contributor
|
|||||||
lyallemma;25173082;Contributor
|
lyallemma;25173082;Contributor
|
||||||
edent;837136;Contributor
|
edent;837136;Contributor
|
||||||
atrus6;357881;Contributor
|
atrus6;357881;Contributor
|
||||||
|
timakro;8438790;Contributor
|
||||||
heyyviv;56256802;Contributor
|
heyyviv;56256802;Contributor
|
||||||
waylife;3348620;Contributor
|
waylife;3348620;Contributor
|
||||||
|
yarons;406826;Contributor
|
||||||
amhokies;3124968;Contributor
|
amhokies;3124968;Contributor
|
||||||
andrewc1;19559401;Contributor
|
andrewc1;19559401;Contributor
|
||||||
axq;5077221;Contributor
|
axq;5077221;Contributor
|
||||||
binarytoto;75904760;Contributor
|
|
||||||
chrk2205;44704035;Contributor
|
chrk2205;44704035;Contributor
|
||||||
fossterer;4236021;Contributor
|
fossterer;4236021;Contributor
|
||||||
lightonflux;1377943;Contributor
|
lightonflux;1377943;Contributor
|
||||||
minusf;3632883;Contributor
|
minusf;3632883;Contributor
|
||||||
|
s3lph;5564491;Contributor
|
||||||
|
tamizh138;26201258;Contributor
|
||||||
zawad2221;32180355;Contributor
|
zawad2221;32180355;Contributor
|
||||||
|
|
@ -90,12 +90,6 @@
|
|||||||
website="https://github.com/square/okio"
|
website="https://github.com/square/okio"
|
||||||
license="Apache 2.0"
|
license="Apache 2.0"
|
||||||
licenseText="LICENSE_APACHE-2.0.txt" />
|
licenseText="LICENSE_APACHE-2.0.txt" />
|
||||||
<library
|
|
||||||
name="RecyclerView-FlexibleDivider"
|
|
||||||
author="yqritc"
|
|
||||||
website="https://github.com/yqritc/RecyclerView-FlexibleDivider"
|
|
||||||
license="Apache 2.0"
|
|
||||||
licenseText="LICENSE_APACHE-2.0.txt" />
|
|
||||||
<library
|
<library
|
||||||
name="RxAndroid"
|
name="RxAndroid"
|
||||||
author="ReactiveX"
|
author="ReactiveX"
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
Arabic;abuzar3.khalid, badarotti, keunes, nabilMaghura, rex07, shubbar
|
Arabic;abuzar3.khalid, badarotti, keunes, MustafaAlgurabi, nabilMaghura, rex07, shubbar
|
||||||
Asturian (ast_ES);enolp
|
Asturian (ast_ES);enolp
|
||||||
Basque;gaztainalde, keunes, Osoitz, pospolos
|
Basque;gaztainalde, keunes, Osoitz, pospolos
|
||||||
Breton;Belvar, keunes
|
Breton;Belvar, keunes
|
||||||
Bulgarian;keunes, solusitor
|
Bulgarian;keunes, ma4ko, solusitor
|
||||||
Catalan;carles.llacer, dvd1985, exort12, IvanAmarante, javiercoll, keunes, Kintu, lambdani, marcmetallextrem, xc70
|
Catalan;carles.llacer, dvd1985, exort12, IvanAmarante, javiercoll, keunes, Kintu, lambdani, marcmetallextrem, xc70
|
||||||
Chinese (zh_CN);brnme, cyril3, Felix2yu, gaohongyuan, Guaidaodl, Huck0, iconteral, jhxie, jxj2zzz79pfp9bpo, keunes, kyleehee, molisiye, owen8877, RainSlide, RangerNJU, Sak94664, spice2wolf, tupunco, wongsyrone, yangyang, yiqiok
|
Chinese (zh_CN);brnme, cyril3, Felix2yu, gaohongyuan, Guaidaodl, Huck0, iconteral, jhxie, jxj2zzz79pfp9bpo, keunes, kyleehee, molisiye, owen8877, RainSlide, RangerNJU, Sak94664, spice2wolf, tupunco, wongsyrone, yangyang, yiqiok
|
||||||
Chinese (zh_TW);bobchao, ijliao, keunes, mapobi, pggdt, ymhuang0808
|
Chinese (zh_TW);bobchao, ijliao, keunes, mapobi, pggdt, ymhuang0808
|
||||||
Czech (cs_CZ);anotheranonymoususer, elich, Hanzmeister, svetlemodry, Thomaash
|
Czech (cs_CZ);anotheranonymoususer, elich, Hanzmeister, md.share, svetlemodry, Thomaash
|
||||||
Danish;JFreak, jhertel, keunes, SebastianKiwiDk, twikedk
|
Danish;JFreak, jhertel, keunes, petterbejo, SebastianKiwiDk
|
||||||
Dutch;e2jk, keunes, rwv, Vistaus
|
Dutch;e2jk, keunes, rwv, Vistaus
|
||||||
Estonian;Eraser, keunes, mahfiaz
|
Estonian;Eraser, keunes, mahfiaz
|
||||||
Finnish;Ban3, keunes, Sahtor
|
Finnish;Ban3, keunes, Sahtor
|
||||||
French;ChaoticMind, clombion, Cornegidouille, e2jk, keunes, lacouture, LouFex, Matth78, Poussinou, sterylmreep
|
French;ChaoticMind, clombion, Cornegidouille, e2jk, keunes, lacouture, LouFex, Matth78, petterbejo, Poussinou, RomainTT, sterylmreep
|
||||||
Galician;antiparvos, pikamoku, Raichely
|
Galician;antiparvos, pikamoku, Raichely
|
||||||
German;_Er, ByteHamster, ceving, dadosch, DerSilly, elkangaroo, enz, f_grubm, finsterwalder, hbilke, HolgerJeromin, JoeMcFly, kalei, keunes, mfietz, pudeeh, Quiss42, repat, tomte, tweimer, Willhelm, ypid
|
German;_Er, ByteHamster, ceving, dadosch, DerSilly, elkangaroo, enz, f_grubm, finsterwalder, forght, hbilke, HolgerJeromin, JoeMcFly, kalei, keunes, max.wittig, mfietz, Michael_Strecke, petterbejo, pudeeh, Quiss42, repat, toaskoas, tomte, tweimer, Willhelm, ypid
|
||||||
Modern Greek (1453-);AnimaRain, antonist, keunes, pavlosv
|
Modern Greek (1453-);AnimaRain, antonist, keunes, pavlosv
|
||||||
Hebrew (he_IL);amir.dafnyman, E1i9, mongoose4004, pinkasey, rellieberman, Yaron
|
Hebrew (he_IL);amir.dafnyman, E1i9, mongoose4004, pinkasey, rellieberman, Yaron
|
||||||
Hindi (hi_IN);keunes, purple.coder, siddhusengar, thelazyoxymoron
|
Hindi (hi_IN);keunes, purple.coder, siddhusengar, thelazyoxymoron
|
||||||
@ -24,20 +24,20 @@ Italian (it_IT);aalex70, allin, alvami, Bonnee, dontknowcris, giuseppep, Guybrus
|
|||||||
Japanese;keunes, KotaKato, Naofumi, sh3llc4t, TranslatorG
|
Japanese;keunes, KotaKato, Naofumi, sh3llc4t, TranslatorG
|
||||||
Kannada (kn_IN);chiraag.nataraj, keunes, thejeshgn
|
Kannada (kn_IN);chiraag.nataraj, keunes, thejeshgn
|
||||||
Ko;changwoo, keunes, libliboom
|
Ko;changwoo, keunes, libliboom
|
||||||
Lithuanian;keunes, naglis
|
Lithuanian;keunes, naglis, Sharper
|
||||||
Macedonian;krisfremen
|
Macedonian;krisfremen
|
||||||
Malayalam;joice, keunes, rashivkp
|
Malayalam;joice, keunes, rashivkp
|
||||||
Norwegian Bokmål (nb_NO);abstrakct, ahysing, bablecopherye, corkie, forteller, heraldo, jakobkg, keunes, kongk, sevenmaster, timbast
|
Norwegian Bokmål (nb_NO);abstrakct, ahysing, bablecopherye, corkie, forteller, heraldo, jakobkg, keunes, kongk, sevenmaster, timbast
|
||||||
Persian;ahangarha, danialbehzadi, ebadi, ebraminio, F7D, hamidrezabayat76, keunes, sinamoghaddas
|
Persian;ahangarha, danialbehzadi, ebadi, ebraminio, F7D, hamidrezabayat76, keunes, sinamoghaddas
|
||||||
Polish (pl_PL);befeleme, hiro2020, Iwangelion, keunes, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
|
Polish (pl_PL);befeleme, hiro2020, Iwangelion, kamila.miodek1991, keunes, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
|
||||||
Portuguese;emansije, keunes, smarquespt
|
Portuguese;emansije, keunes, smarquespt
|
||||||
Portuguese (pt_BR);alexupits, alysonborges, andersonvom, arua, caioau, carlo_valente, castrors, edman, keunes, lipefire, mbaltar, olivoto, rogervezaro, RubeensVinicius, SamWilliam
|
Portuguese (pt_BR);alexupits, alysonborges, andersonvom, aracnus, arua, bandreghetti, caioau, carlo_valente, castrors, edman, keunes, lipefire, mbaltar, olivoto, rogervezaro, RubeensVinicius, SamWilliam
|
||||||
Romanian (ro_RO);corneliu.e, fuzzmz, keunes, ralienpp
|
Romanian (ro_RO);corneliu.e, fuzzmz, keunes, ralienpp
|
||||||
Russian (ru_RU);ashed, btimofeev, Duke_Raven, gammja, homocomputeris, IgorPolyakov, keunes, mercutiy, null, overmind88, Platun0v, PtilopsisLeucotis, s.chebotar, un_logic, Vladryyu, whereisthetea
|
Russian (ru_RU);ashed, btimofeev, Duke_Raven, gammja, homocomputeris, IgorPolyakov, keunes, mercutiy, null, overmind88, Platun0v, PtilopsisLeucotis, s.chebotar, tepxd, un_logic, Vladryyu, whereisthetea
|
||||||
Slovak;ati3, keunes, marulinko, tiborepcek
|
Slovak;ati3, jose1711, keunes, marulinko, tiborepcek
|
||||||
Slovenian (sl_SI);keunes, panter23
|
Slovenian (sl_SI);asovic, keunes, panter23, trus2
|
||||||
Spanish;AleksSyntek, andersonvom, andrespelaezp, deandreamatias, dvd1985, elojodepajaro, Fitoschido, frandavid100, hard_ware, javiercoll, keunes, LatinSuD, leogrignafini, rafael.osuna, tres.14159, vfmatzkin, wakutiteo
|
Spanish;AleksSyntek, andersonvom, andrespelaezp, Atreyu94, CaeM0R, deandreamatias, dvd1985, elojodepajaro, Fitoschido, frandavid100, hard_ware, javiercoll, keunes, LatinSuD, leogrignafini, rafael.osuna, tres.14159, vfmatzkin, wakutiteo
|
||||||
Swahili (macrolanguage);keunes, kmtra
|
Swahili (macrolanguage);1silvester, keunes, kmtra
|
||||||
Swedish (sv_SE);bpnilsson, keunes, nilso, TwoD
|
Swedish (sv_SE);bpnilsson, keunes, nilso, TwoD
|
||||||
Telugu;keunes, veeven
|
Telugu;keunes, veeven
|
||||||
Turkish;AhmedDuran, brsata, Erdy, keunes, overbite, Slsdem
|
Turkish;AhmedDuran, brsata, Erdy, keunes, overbite, Slsdem
|
||||||
|
|
@ -1,95 +1,76 @@
|
|||||||
package de.danoeh.antennapod.activity;
|
package de.danoeh.antennapod.activity;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||||
|
import de.danoeh.antennapod.core.feed.FeedPreferences;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||||
|
import de.danoeh.antennapod.dialog.AuthenticationDialog;
|
||||||
|
import io.reactivex.Completable;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a username and a password text field.
|
* Shows a username and a password text field.
|
||||||
* The activity MUST be started with the ARG_DOWNlOAD_REQUEST argument set to a non-null value.
|
* The activity MUST be started with the ARG_DOWNlOAD_REQUEST argument set to a non-null value.
|
||||||
* Other arguments are optional.
|
|
||||||
* The activity's result will be the same DownloadRequest with the entered username and password.
|
|
||||||
*/
|
*/
|
||||||
public class DownloadAuthenticationActivity extends AppCompatActivity {
|
public class DownloadAuthenticationActivity extends AppCompatActivity {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The download request object that contains information about the resource that requires a username and a password
|
* The download request object that contains information about the resource that requires a username and a password.
|
||||||
*/
|
*/
|
||||||
public static final String ARG_DOWNLOAD_REQUEST = "request";
|
public static final String ARG_DOWNLOAD_REQUEST = "request";
|
||||||
/**
|
|
||||||
* True if the request should be sent to the DownloadRequester when this activity is finished, false otherwise.
|
|
||||||
* The default value is false.
|
|
||||||
*/
|
|
||||||
public static final String ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL = "send_to_downloadrequester";
|
|
||||||
|
|
||||||
private static final String RESULT_REQUEST = "request";
|
|
||||||
|
|
||||||
private EditText etxtUsername;
|
|
||||||
private EditText etxtPassword;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
setTheme(UserPreferences.getNoTitleTheme());
|
setTheme(UserPreferences.getTranslucentTheme());
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setContentView(R.layout.download_authentication_activity);
|
|
||||||
TextView txtvDescription = findViewById(R.id.txtvDescription);
|
|
||||||
etxtUsername = findViewById(R.id.etxtUsername);
|
|
||||||
etxtPassword = findViewById(R.id.etxtPassword);
|
|
||||||
Button butConfirm = findViewById(R.id.butConfirm);
|
|
||||||
Button butCancel = findViewById(R.id.butCancel);
|
|
||||||
|
|
||||||
Validate.isTrue(getIntent().hasExtra(ARG_DOWNLOAD_REQUEST), "Download request missing");
|
Validate.isTrue(getIntent().hasExtra(ARG_DOWNLOAD_REQUEST), "Download request missing");
|
||||||
DownloadRequest request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST);
|
DownloadRequest request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST);
|
||||||
boolean sendToDownloadRequester = getIntent().getBooleanExtra(ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, false);
|
|
||||||
|
|
||||||
String newDescription = txtvDescription.getText() + ":\n\n" + request.getTitle();
|
new AuthenticationDialog(this, R.string.authentication_label, true, "", "") {
|
||||||
txtvDescription.setText(newDescription);
|
@Override
|
||||||
|
protected void onConfirmed(String username, String password) {
|
||||||
|
Completable.fromAction(
|
||||||
|
() -> {
|
||||||
|
request.setUsername(username);
|
||||||
|
request.setPassword(password);
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||||
etxtUsername.setText(savedInstanceState.getString("username"));
|
long mediaId = request.getFeedfileId();
|
||||||
etxtPassword.setText(savedInstanceState.getString("password"));
|
FeedMedia media = DBReader.getFeedMedia(mediaId);
|
||||||
}
|
if (media != null) {
|
||||||
|
FeedPreferences preferences = media.getItem().getFeed().getPreferences();
|
||||||
butConfirm.setOnClickListener(v -> {
|
if (TextUtils.isEmpty(preferences.getPassword())
|
||||||
String username = etxtUsername.getText().toString();
|
|| TextUtils.isEmpty(preferences.getUsername())) {
|
||||||
String password = etxtPassword.getText().toString();
|
preferences.setUsername(username);
|
||||||
request.setUsername(username);
|
preferences.setPassword(password);
|
||||||
request.setPassword(password);
|
DBWriter.setFeedPreferences(preferences);
|
||||||
Intent result = new Intent();
|
}
|
||||||
result.putExtra(RESULT_REQUEST, request);
|
}
|
||||||
setResult(Activity.RESULT_OK, result);
|
}
|
||||||
|
})
|
||||||
if (sendToDownloadRequester) {
|
.subscribeOn(Schedulers.io())
|
||||||
DownloadRequester.getInstance().download(DownloadAuthenticationActivity.this, request);
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(() -> {
|
||||||
|
DownloadRequester.getInstance().download(DownloadAuthenticationActivity.this, request);
|
||||||
|
finish();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
finish();
|
|
||||||
});
|
|
||||||
|
|
||||||
butCancel.setOnClickListener(v -> {
|
@Override
|
||||||
setResult(Activity.RESULT_CANCELED);
|
protected void onCancelled() {
|
||||||
finish();
|
finish();
|
||||||
});
|
}
|
||||||
|
}.show();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putString("username", etxtUsername.getText().toString());
|
|
||||||
outState.putString("password", etxtPassword.getText().toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import android.content.Intent;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -40,7 +41,7 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
|
|||||||
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
|
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||||
import de.danoeh.antennapod.core.util.StorageUtils;
|
import de.danoeh.antennapod.core.util.StorageUtils;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||||
import de.danoeh.antennapod.dialog.RatingDialog;
|
import de.danoeh.antennapod.dialog.RatingDialog;
|
||||||
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
||||||
@ -51,9 +52,11 @@ import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
|||||||
import de.danoeh.antennapod.fragment.NavDrawerFragment;
|
import de.danoeh.antennapod.fragment.NavDrawerFragment;
|
||||||
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
|
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
|
||||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||||
|
import de.danoeh.antennapod.fragment.SearchFragment;
|
||||||
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
||||||
import de.danoeh.antennapod.fragment.TransitionEffect;
|
import de.danoeh.antennapod.fragment.TransitionEffect;
|
||||||
import de.danoeh.antennapod.preferences.PreferenceUpgrader;
|
import de.danoeh.antennapod.preferences.PreferenceUpgrader;
|
||||||
|
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
|
||||||
import de.danoeh.antennapod.view.LockableBottomSheetBehavior;
|
import de.danoeh.antennapod.view.LockableBottomSheetBehavior;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
@ -75,7 +78,6 @@ public class MainActivity extends CastEnabledActivity {
|
|||||||
public static final String EXTRA_FRAGMENT_TAG = "fragment_tag";
|
public static final String EXTRA_FRAGMENT_TAG = "fragment_tag";
|
||||||
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
|
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
|
||||||
public static final String EXTRA_FEED_ID = "fragment_feed_id";
|
public static final String EXTRA_FEED_ID = "fragment_feed_id";
|
||||||
public static final String EXTRA_OPEN_PLAYER = "open_player";
|
|
||||||
public static final String EXTRA_REFRESH_ON_START = "refresh_on_start";
|
public static final String EXTRA_REFRESH_ON_START = "refresh_on_start";
|
||||||
public static final String EXTRA_STARTED_FROM_SEARCH = "started_from_search";
|
public static final String EXTRA_STARTED_FROM_SEARCH = "started_from_search";
|
||||||
public static final String KEY_GENERATED_VIEW_ID = "generated_view_id";
|
public static final String KEY_GENERATED_VIEW_ID = "generated_view_id";
|
||||||
@ -184,16 +186,16 @@ public class MainActivity extends CastEnabledActivity {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public void setupToolbarToggle(@Nullable Toolbar toolbar) {
|
public void setupToolbarToggle(@NonNull Toolbar toolbar, boolean displayUpArrow) {
|
||||||
if (drawerLayout != null) { // Tablet layout does not have a drawer
|
if (drawerLayout != null) { // Tablet layout does not have a drawer
|
||||||
drawerLayout.removeDrawerListener(drawerToggle);
|
drawerLayout.removeDrawerListener(drawerToggle);
|
||||||
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar,
|
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar,
|
||||||
R.string.drawer_open, R.string.drawer_close);
|
R.string.drawer_open, R.string.drawer_close);
|
||||||
drawerLayout.addDrawerListener(drawerToggle);
|
drawerLayout.addDrawerListener(drawerToggle);
|
||||||
drawerToggle.syncState();
|
drawerToggle.syncState();
|
||||||
drawerToggle.setDrawerIndicatorEnabled(getSupportFragmentManager().getBackStackEntryCount() == 0);
|
drawerToggle.setDrawerIndicatorEnabled(!displayUpArrow);
|
||||||
drawerToggle.setToolbarNavigationClickListener(v -> getSupportFragmentManager().popBackStack());
|
drawerToggle.setToolbarNavigationClickListener(v -> getSupportFragmentManager().popBackStack());
|
||||||
} else if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
|
} else if (!displayUpArrow) {
|
||||||
toolbar.setNavigationIcon(null);
|
toolbar.setNavigationIcon(null);
|
||||||
} else {
|
} else {
|
||||||
toolbar.setNavigationIcon(ThemeUtils.getDrawableFromAttr(this, R.attr.homeAsUpIndicator));
|
toolbar.setNavigationIcon(ThemeUtils.getDrawableFromAttr(this, R.attr.homeAsUpIndicator));
|
||||||
@ -508,9 +510,11 @@ public class MainActivity extends CastEnabledActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||||
} else if (intent.getBooleanExtra(EXTRA_OPEN_PLAYER, false)) {
|
} else if (intent.getBooleanExtra(MainActivityStarter.EXTRA_OPEN_PLAYER, false)) {
|
||||||
sheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
sheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||||
bottomSheetCallback.onSlide(null, 1.0f);
|
bottomSheetCallback.onSlide(null, 1.0f);
|
||||||
|
} else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
|
||||||
|
handleDeeplink(intent.getData());
|
||||||
}
|
}
|
||||||
// to avoid handling the intent twice when the configuration changes
|
// to avoid handling the intent twice when the configuration changes
|
||||||
setIntent(new Intent(MainActivity.this, MainActivity.class));
|
setIntent(new Intent(MainActivity.this, MainActivity.class));
|
||||||
@ -520,6 +524,7 @@ public class MainActivity extends CastEnabledActivity {
|
|||||||
protected void onNewIntent(Intent intent) {
|
protected void onNewIntent(Intent intent) {
|
||||||
super.onNewIntent(intent);
|
super.onNewIntent(intent);
|
||||||
setIntent(intent);
|
setIntent(intent);
|
||||||
|
handleNavIntent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Snackbar showSnackbarAbovePlayer(CharSequence text, int duration) {
|
public Snackbar showSnackbarAbovePlayer(CharSequence text, int duration) {
|
||||||
@ -540,6 +545,59 @@ public class MainActivity extends CastEnabledActivity {
|
|||||||
return showSnackbarAbovePlayer(getResources().getText(text), duration);
|
return showSnackbarAbovePlayer(getResources().getText(text), duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the deep link incoming via App Actions.
|
||||||
|
* Performs an in-app search or opens the relevant feature of the app
|
||||||
|
* depending on the query.
|
||||||
|
*
|
||||||
|
* @param uri incoming deep link
|
||||||
|
*/
|
||||||
|
private void handleDeeplink(Uri uri) {
|
||||||
|
if (uri == null || uri.getPath() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Handling deeplink: " + uri.toString());
|
||||||
|
switch (uri.getPath()) {
|
||||||
|
case "/deeplink/search":
|
||||||
|
String query = uri.getQueryParameter("query");
|
||||||
|
if (query == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadChildFragment(SearchFragment.newInstance(query));
|
||||||
|
break;
|
||||||
|
case "/deeplink/main":
|
||||||
|
String feature = uri.getQueryParameter("page");
|
||||||
|
if (feature == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (feature) {
|
||||||
|
case "DOWNLOADS":
|
||||||
|
loadFragment(DownloadsFragment.TAG, null);
|
||||||
|
break;
|
||||||
|
case "HISTORY":
|
||||||
|
loadFragment(PlaybackHistoryFragment.TAG, null);
|
||||||
|
break;
|
||||||
|
case "EPISODES":
|
||||||
|
loadFragment(EpisodesFragment.TAG, null);
|
||||||
|
break;
|
||||||
|
case "QUEUE":
|
||||||
|
loadFragment(QueueFragment.TAG, null);
|
||||||
|
break;
|
||||||
|
case "SUBSCRIPTIONS":
|
||||||
|
loadFragment(SubscriptionFragment.TAG, null);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
showSnackbarAbovePlayer(getString(R.string.app_action_not_found, feature),
|
||||||
|
Snackbar.LENGTH_LONG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Hardware keyboard support
|
//Hardware keyboard support
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
@ -592,5 +650,4 @@ public class MainActivity extends CastEnabledActivity {
|
|||||||
}
|
}
|
||||||
return super.onKeyUp(keyCode, event);
|
return super.onKeyUp(keyCode, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
package de.danoeh.antennapod.activity;
|
package de.danoeh.antennapod.activity;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -17,7 +15,6 @@ import android.widget.ImageButton;
|
|||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
|
|
||||||
@ -28,17 +25,16 @@ import org.greenrobot.eventbus.ThreadMode;
|
|||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.cardview.widget.CardView;
|
||||||
import androidx.core.app.ActivityOptionsCompat;
|
import androidx.core.app.ActivityOptionsCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||||
import de.danoeh.antennapod.core.feed.MediaType;
|
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||||
import de.danoeh.antennapod.core.storage.DBReader;
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
@ -50,11 +46,9 @@ import de.danoeh.antennapod.core.util.ShareUtils;
|
|||||||
import de.danoeh.antennapod.core.util.StorageUtils;
|
import de.danoeh.antennapod.core.util.StorageUtils;
|
||||||
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
|
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
|
||||||
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
|
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
|
||||||
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
|
|
||||||
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
|
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
|
||||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||||
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
|
|
||||||
import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
|
import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
|
||||||
import de.danoeh.antennapod.dialog.ShareDialog;
|
import de.danoeh.antennapod.dialog.ShareDialog;
|
||||||
import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
|
import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
|
||||||
@ -64,7 +58,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
|
|||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides general features which are both needed for playing audio and video
|
* Provides general features which are both needed for playing audio and video
|
||||||
* files.
|
* files.
|
||||||
@ -72,9 +65,6 @@ import io.reactivex.schedulers.Schedulers;
|
|||||||
public abstract class MediaplayerActivity extends CastEnabledActivity implements OnSeekBarChangeListener {
|
public abstract class MediaplayerActivity extends CastEnabledActivity implements OnSeekBarChangeListener {
|
||||||
private static final String TAG = "MediaplayerActivity";
|
private static final String TAG = "MediaplayerActivity";
|
||||||
private static final String PREFS = "MediaPlayerActivityPreferences";
|
private static final String PREFS = "MediaPlayerActivityPreferences";
|
||||||
private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
|
|
||||||
private static final int REQUEST_CODE_STORAGE_PLAY_VIDEO = 42;
|
|
||||||
private static final int REQUEST_CODE_STORAGE_PLAY_AUDIO = 43;
|
|
||||||
|
|
||||||
PlaybackController controller;
|
PlaybackController controller;
|
||||||
|
|
||||||
@ -87,6 +77,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||||||
private ImageButton butFF;
|
private ImageButton butFF;
|
||||||
private TextView txtvFF;
|
private TextView txtvFF;
|
||||||
private ImageButton butSkip;
|
private ImageButton butSkip;
|
||||||
|
private CardView cardViewSeek;
|
||||||
|
private TextView txtvSeek;
|
||||||
|
|
||||||
private boolean showTimeLeft = false;
|
private boolean showTimeLeft = false;
|
||||||
|
|
||||||
@ -96,12 +88,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||||||
|
|
||||||
private PlaybackController newPlaybackController() {
|
private PlaybackController newPlaybackController() {
|
||||||
return new PlaybackController(this) {
|
return new PlaybackController(this) {
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setupGUI() {
|
|
||||||
MediaplayerActivity.this.setupGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPositionObserverUpdate() {
|
public void onPositionObserverUpdate() {
|
||||||
MediaplayerActivity.this.onPositionObserverUpdate();
|
MediaplayerActivity.this.onPositionObserverUpdate();
|
||||||
@ -143,8 +129,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean loadMediaInfo() {
|
public void loadMediaInfo() {
|
||||||
return MediaplayerActivity.this.loadMediaInfo();
|
MediaplayerActivity.this.loadMediaInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -467,17 +453,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||||||
* to the PlaybackService to ensure that the activity has the right
|
* to the PlaybackService to ensure that the activity has the right
|
||||||
* FeedMedia object.
|
* FeedMedia object.
|
||||||
*/
|
*/
|
||||||
boolean loadMediaInfo() {
|
void loadMediaInfo() {
|
||||||
Log.d(TAG, "loadMediaInfo()");
|
Log.d(TAG, "loadMediaInfo()");
|
||||||
if(controller == null || controller.getMedia() == null) {
|
if (controller == null || controller.getMedia() == null) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
|
showTimeLeft = UserPreferences.shouldShowRemainingTime();
|
||||||
showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
|
|
||||||
onPositionObserverUpdate();
|
onPositionObserverUpdate();
|
||||||
checkFavorite();
|
checkFavorite();
|
||||||
updatePlaybackSpeedButton();
|
updatePlaybackSpeedButton();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updatePlaybackSpeedButton() {
|
void updatePlaybackSpeedButton() {
|
||||||
@ -492,9 +476,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||||||
setContentView(getContentViewResourceId());
|
setContentView(getContentViewResourceId());
|
||||||
sbPosition = findViewById(R.id.sbPosition);
|
sbPosition = findViewById(R.id.sbPosition);
|
||||||
txtvPosition = findViewById(R.id.txtvPosition);
|
txtvPosition = findViewById(R.id.txtvPosition);
|
||||||
|
cardViewSeek = findViewById(R.id.cardViewSeek);
|
||||||
|
txtvSeek = findViewById(R.id.txtvSeek);
|
||||||
|
|
||||||
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
|
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
|
||||||
showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
|
showTimeLeft = UserPreferences.shouldShowRemainingTime();
|
||||||
Log.d("timeleft", showTimeLeft ? "true" : "false");
|
Log.d("timeleft", showTimeLeft ? "true" : "false");
|
||||||
txtvLength = findViewById(R.id.txtvLength);
|
txtvLength = findViewById(R.id.txtvLength);
|
||||||
if (txtvLength != null) {
|
if (txtvLength != null) {
|
||||||
@ -518,9 +504,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||||||
}
|
}
|
||||||
txtvLength.setText(length);
|
txtvLength.setText(length);
|
||||||
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
UserPreferences.setShowRemainTimeSetting(showTimeLeft);
|
||||||
editor.putBoolean(PREF_SHOW_TIME_LEFT, showTimeLeft);
|
|
||||||
editor.apply();
|
|
||||||
Log.d("timeleft on click", showTimeLeft ? "true" : "false");
|
Log.d("timeleft on click", showTimeLeft ? "true" : "false");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -618,21 +602,21 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||||||
}
|
}
|
||||||
if (fromUser) {
|
if (fromUser) {
|
||||||
prog = progress / ((float) seekBar.getMax());
|
prog = progress / ((float) seekBar.getMax());
|
||||||
int duration = controller.getDuration();
|
|
||||||
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
|
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
|
||||||
int position = converter.convert((int) (prog * duration));
|
int position = converter.convert((int) (prog * controller.getDuration()));
|
||||||
txtvPosition.setText(Converter.getDurationStringLong(position));
|
txtvSeek.setText(Converter.getDurationStringLong(position));
|
||||||
|
|
||||||
if (showTimeLeft) {
|
|
||||||
int timeLeft = converter.convert(duration - (int) (prog * duration));
|
|
||||||
txtvLength.setText("-" + Converter.getDurationStringLong(timeLeft));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
cardViewSeek.setScaleX(.8f);
|
||||||
|
cardViewSeek.setScaleY(.8f);
|
||||||
|
cardViewSeek.animate()
|
||||||
|
.setInterpolator(new FastOutSlowInInterpolator())
|
||||||
|
.alpha(1f).scaleX(1f).scaleY(1f)
|
||||||
|
.setDuration(200)
|
||||||
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -640,6 +624,13 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
controller.seekTo((int) (prog * controller.getDuration()));
|
controller.seekTo((int) (prog * controller.getDuration()));
|
||||||
}
|
}
|
||||||
|
cardViewSeek.setScaleX(1f);
|
||||||
|
cardViewSeek.setScaleY(1f);
|
||||||
|
cardViewSeek.animate()
|
||||||
|
.setInterpolator(new FastOutSlowInInterpolator())
|
||||||
|
.alpha(0f).scaleX(.8f).scaleY(.8f)
|
||||||
|
.setDuration(200)
|
||||||
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkFavorite() {
|
private void checkFavorite() {
|
||||||
@ -663,50 +654,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void playExternalMedia(Intent intent, MediaType type) {
|
|
||||||
if (intent == null || intent.getData() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Build.VERSION.SDK_INT >= 23
|
|
||||||
&& ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
|
||||||
!= PackageManager.PERMISSION_GRANTED) {
|
|
||||||
|
|
||||||
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
|
|
||||||
Toast.makeText(this, R.string.needs_storage_permission, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
int code = REQUEST_CODE_STORAGE_PLAY_AUDIO;
|
|
||||||
if (type == MediaType.VIDEO) {
|
|
||||||
code = REQUEST_CODE_STORAGE_PLAY_VIDEO;
|
|
||||||
}
|
|
||||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, code);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
|
|
||||||
ExternalMedia media = new ExternalMedia(intent.getData().getPath(), type);
|
|
||||||
|
|
||||||
new PlaybackServiceStarter(this, media)
|
|
||||||
.callEvenIfRunning(true)
|
|
||||||
.startWhenPrepared(true)
|
|
||||||
.shouldStream(false)
|
|
||||||
.prepareImmediately(true)
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, int[] grantResults) {
|
|
||||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
if (requestCode == REQUEST_CODE_STORAGE_PLAY_AUDIO) {
|
|
||||||
playExternalMedia(getIntent(), MediaType.AUDIO);
|
|
||||||
} else if (requestCode == REQUEST_CODE_STORAGE_PLAY_VIDEO) {
|
|
||||||
playExternalMedia(getIntent(), MediaType.VIDEO);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, R.string.needs_storage_permission, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static FeedItem getFeedItem(@Nullable Playable playable) {
|
private static FeedItem getFeedItem(@Nullable Playable playable) {
|
||||||
if (playable instanceof FeedMedia) {
|
if (playable instanceof FeedMedia) {
|
||||||
|
@ -4,10 +4,14 @@ import android.app.Dialog;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.LightingColorFilter;
|
import android.graphics.LightingColorFilter;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -15,6 +19,7 @@ import android.widget.AdapterView;
|
|||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.UiThread;
|
import androidx.annotation.UiThread;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
@ -28,7 +33,6 @@ import de.danoeh.antennapod.core.event.DownloadEvent;
|
|||||||
import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
|
import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
|
||||||
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
|
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
|
||||||
import de.danoeh.antennapod.core.feed.Feed;
|
import de.danoeh.antennapod.core.feed.Feed;
|
||||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedPreferences;
|
import de.danoeh.antennapod.core.feed.FeedPreferences;
|
||||||
import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
|
import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
|
||||||
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
||||||
@ -41,6 +45,7 @@ import de.danoeh.antennapod.core.service.download.Downloader;
|
|||||||
import de.danoeh.antennapod.core.service.download.HttpDownloader;
|
import de.danoeh.antennapod.core.service.download.HttpDownloader;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||||
import de.danoeh.antennapod.core.storage.DBReader;
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||||
import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
|
import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
|
||||||
@ -49,7 +54,6 @@ import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeExceptio
|
|||||||
import de.danoeh.antennapod.core.util.DownloadError;
|
import de.danoeh.antennapod.core.util.DownloadError;
|
||||||
import de.danoeh.antennapod.core.util.FileNameGenerator;
|
import de.danoeh.antennapod.core.util.FileNameGenerator;
|
||||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||||
import de.danoeh.antennapod.core.util.Optional;
|
|
||||||
import de.danoeh.antennapod.core.util.StorageUtils;
|
import de.danoeh.antennapod.core.util.StorageUtils;
|
||||||
import de.danoeh.antennapod.core.util.URLChecker;
|
import de.danoeh.antennapod.core.util.URLChecker;
|
||||||
import de.danoeh.antennapod.core.util.playback.RemoteMedia;
|
import de.danoeh.antennapod.core.util.playback.RemoteMedia;
|
||||||
@ -58,9 +62,11 @@ import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
|
|||||||
import de.danoeh.antennapod.databinding.OnlinefeedviewActivityBinding;
|
import de.danoeh.antennapod.databinding.OnlinefeedviewActivityBinding;
|
||||||
import de.danoeh.antennapod.dialog.AuthenticationDialog;
|
import de.danoeh.antennapod.dialog.AuthenticationDialog;
|
||||||
import de.danoeh.antennapod.discovery.PodcastSearcherRegistry;
|
import de.danoeh.antennapod.discovery.PodcastSearcherRegistry;
|
||||||
|
import io.reactivex.Maybe;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.observers.DisposableMaybeObserver;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
@ -87,6 +93,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||||||
// Optional argument: specify a title for the actionbar.
|
// Optional argument: specify a title for the actionbar.
|
||||||
private static final int RESULT_ERROR = 2;
|
private static final int RESULT_ERROR = 2;
|
||||||
private static final String TAG = "OnlineFeedViewActivity";
|
private static final String TAG = "OnlineFeedViewActivity";
|
||||||
|
private static final String PREFS = "OnlineFeedViewActivityPreferences";
|
||||||
|
private static final String PREF_LAST_AUTO_DOWNLOAD = "lastAutoDownload";
|
||||||
|
|
||||||
private volatile List<Feed> feeds;
|
private volatile List<Feed> feeds;
|
||||||
private Feed feed;
|
private Feed feed;
|
||||||
private String selectedDownloadUrl;
|
private String selectedDownloadUrl;
|
||||||
@ -248,7 +257,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||||||
url = URLChecker.prepareURL(url);
|
url = URLChecker.prepareURL(url);
|
||||||
feed = new Feed(url, null);
|
feed = new Feed(url, null);
|
||||||
if (username != null && password != null) {
|
if (username != null && password != null) {
|
||||||
feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password));
|
feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL,
|
||||||
|
VolumeAdaptionSetting.OFF, username, password));
|
||||||
}
|
}
|
||||||
String fileUrl = new File(getExternalCacheDir(),
|
String fileUrl = new File(getExternalCacheDir(),
|
||||||
FileNameGenerator.generateFileName(feed.getDownload_url())).toString();
|
FileNameGenerator.generateFileName(feed.getDownload_url())).toString();
|
||||||
@ -283,11 +293,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String errorMsg = status.getReason().getErrorString(OnlineFeedViewActivity.this);
|
showErrorDialog(status.getReason().getErrorString(OnlineFeedViewActivity.this), status.getReasonDetailed());
|
||||||
if (status.getReasonDetailed() != null) {
|
|
||||||
errorMsg += " (" + status.getReasonDetailed() + ")";
|
|
||||||
}
|
|
||||||
showErrorDialog(errorMsg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,37 +322,47 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
Log.d(TAG, "Parsing feed");
|
Log.d(TAG, "Parsing feed");
|
||||||
|
|
||||||
parser = Observable.fromCallable(this::doParseFeed)
|
parser = Maybe.fromCallable(this::doParseFeed)
|
||||||
.subscribeOn(Schedulers.computation())
|
.subscribeOn(Schedulers.computation())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(optionalResult -> {
|
.subscribeWith(new DisposableMaybeObserver<FeedHandlerResult>() {
|
||||||
if(optionalResult.isPresent()) {
|
@Override
|
||||||
FeedHandlerResult result = optionalResult.get();
|
public void onSuccess(@NonNull FeedHandlerResult result) {
|
||||||
beforeShowFeedInformation(result.feed);
|
|
||||||
showFeedInformation(result.feed, result.alternateFeedUrls);
|
showFeedInformation(result.feed, result.alternateFeedUrls);
|
||||||
}
|
}
|
||||||
}, error -> {
|
|
||||||
String errorMsg = DownloadError.ERROR_PARSER_EXCEPTION.getErrorString(
|
@Override
|
||||||
OnlineFeedViewActivity.this) + " (" + error.getMessage() + ")";
|
public void onComplete() {
|
||||||
showErrorDialog(errorMsg);
|
// Ignore null result: We showed the discovery dialog.
|
||||||
Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error));
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(@NonNull Throwable error) {
|
||||||
|
showErrorDialog(error.getMessage(), "");
|
||||||
|
Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
/**
|
||||||
private Optional<FeedHandlerResult> doParseFeed() throws Exception {
|
* Try to parse the feed.
|
||||||
|
* @return The FeedHandlerResult if successful.
|
||||||
|
* Null if unsuccessful but we started another attempt.
|
||||||
|
* @throws Exception If unsuccessful but we do not know a resolution.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private FeedHandlerResult doParseFeed() throws Exception {
|
||||||
FeedHandler handler = new FeedHandler();
|
FeedHandler handler = new FeedHandler();
|
||||||
try {
|
try {
|
||||||
return Optional.of(handler.parseFeed(feed));
|
return handler.parseFeed(feed);
|
||||||
} catch (UnsupportedFeedtypeException e) {
|
} catch (UnsupportedFeedtypeException e) {
|
||||||
Log.d(TAG, "Unsupported feed type detected");
|
Log.d(TAG, "Unsupported feed type detected");
|
||||||
if ("html".equalsIgnoreCase(e.getRootElement())) {
|
if ("html".equalsIgnoreCase(e.getRootElement())) {
|
||||||
boolean dialogShown = showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url());
|
boolean dialogShown = showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url());
|
||||||
if (dialogShown) {
|
if (dialogShown) {
|
||||||
return Optional.empty();
|
return null; // Should not display an error message
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "Supplied feed is an HTML web page that has no references to any feed");
|
throw new UnsupportedFeedtypeException(getString(R.string.download_error_unsupported_type_html));
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
@ -360,23 +376,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after the feed has been downloaded and parsed and before showFeedInformation is called.
|
|
||||||
* This method is executed on a background thread
|
|
||||||
*/
|
|
||||||
private void beforeShowFeedInformation(Feed feed) {
|
|
||||||
Log.d(TAG, "Removing HTML from feed description");
|
|
||||||
|
|
||||||
feed.setDescription(HtmlToPlainText.getPlainText(feed.getDescription()));
|
|
||||||
|
|
||||||
Log.d(TAG, "Removing HTML from shownotes");
|
|
||||||
if (feed.getItems() != null) {
|
|
||||||
for (FeedItem item : feed.getItems()) {
|
|
||||||
item.setDescription(HtmlToPlainText.getPlainText(item.getDescription()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when feed parsed successfully.
|
* Called when feed parsed successfully.
|
||||||
* This method is executed on the GUI thread.
|
* This method is executed on the GUI thread.
|
||||||
@ -420,7 +419,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
viewBinding.titleLabel.setText(feed.getTitle());
|
viewBinding.titleLabel.setText(feed.getTitle());
|
||||||
viewBinding.authorLabel.setText(feed.getAuthor());
|
viewBinding.authorLabel.setText(feed.getAuthor());
|
||||||
description.setText(feed.getDescription());
|
description.setText(HtmlToPlainText.getPlainText(feed.getDescription()));
|
||||||
|
|
||||||
viewBinding.subscribeButton.setOnClickListener(v -> {
|
viewBinding.subscribeButton.setOnClickListener(v -> {
|
||||||
if (feedInFeedlist(feed)) {
|
if (feedInFeedlist(feed)) {
|
||||||
@ -445,6 +444,11 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||||||
IntentUtils.sendLocalBroadcast(this, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE);
|
IntentUtils.sendLocalBroadcast(this, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (UserPreferences.isEnableAutodownload()) {
|
||||||
|
SharedPreferences preferences = getSharedPreferences(PREFS, MODE_PRIVATE);
|
||||||
|
viewBinding.autoDownloadCheckBox.setChecked(preferences.getBoolean(PREF_LAST_AUTO_DOWNLOAD, true));
|
||||||
|
}
|
||||||
|
|
||||||
final int MAX_LINES_COLLAPSED = 10;
|
final int MAX_LINES_COLLAPSED = 10;
|
||||||
description.setMaxLines(MAX_LINES_COLLAPSED);
|
description.setMaxLines(MAX_LINES_COLLAPSED);
|
||||||
description.setOnClickListener(v -> {
|
description.setOnClickListener(v -> {
|
||||||
@ -511,10 +515,17 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||||||
if (didPressSubscribe) {
|
if (didPressSubscribe) {
|
||||||
didPressSubscribe = false;
|
didPressSubscribe = false;
|
||||||
if (UserPreferences.isEnableAutodownload()) {
|
if (UserPreferences.isEnableAutodownload()) {
|
||||||
|
boolean autoDownload = viewBinding.autoDownloadCheckBox.isChecked();
|
||||||
|
|
||||||
Feed feed1 = DBReader.getFeed(getFeedId(feed));
|
Feed feed1 = DBReader.getFeed(getFeedId(feed));
|
||||||
FeedPreferences feedPreferences = feed1.getPreferences();
|
FeedPreferences feedPreferences = feed1.getPreferences();
|
||||||
feedPreferences.setAutoDownload(viewBinding.autoDownloadCheckBox.isChecked());
|
feedPreferences.setAutoDownload(autoDownload);
|
||||||
feed1.savePreferences();
|
DBWriter.setFeedPreferences(feedPreferences);
|
||||||
|
|
||||||
|
SharedPreferences preferences = getSharedPreferences(PREFS, MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = preferences.edit();
|
||||||
|
editor.putBoolean(PREF_LAST_AUTO_DOWNLOAD, autoDownload);
|
||||||
|
editor.apply();
|
||||||
}
|
}
|
||||||
openFeed();
|
openFeed();
|
||||||
}
|
}
|
||||||
@ -553,12 +564,16 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void showErrorDialog(String errorMsg) {
|
private void showErrorDialog(String errorMsg, String details) {
|
||||||
if (!isFinishing() && !isPaused) {
|
if (!isFinishing() && !isPaused) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
builder.setTitle(R.string.error_label);
|
builder.setTitle(R.string.error_label);
|
||||||
if (errorMsg != null) {
|
if (errorMsg != null) {
|
||||||
builder.setMessage(errorMsg);
|
String total = errorMsg + "\n\n" + details;
|
||||||
|
SpannableString errorMessage = new SpannableString(total);
|
||||||
|
errorMessage.setSpan(new ForegroundColorSpan(0x88888888),
|
||||||
|
errorMsg.length(), total.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
builder.setMessage(errorMessage);
|
||||||
} else {
|
} else {
|
||||||
builder.setMessage(R.string.download_error_error_unknown);
|
builder.setMessage(R.string.download_error_error_unknown);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import android.widget.ImageView;
|
|||||||
|
|
||||||
import androidx.core.view.WindowCompat;
|
import androidx.core.view.WindowCompat;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@ -37,12 +36,12 @@ import java.lang.ref.WeakReference;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.core.feed.MediaType;
|
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
||||||
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
|
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
|
||||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||||
|
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
|
||||||
import de.danoeh.antennapod.view.AspectRatioVideoView;
|
import de.danoeh.antennapod.view.AspectRatioVideoView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,9 +87,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
|
if (PlaybackService.isCasting()) {
|
||||||
playExternalMedia(getIntent(), MediaType.VIDEO);
|
|
||||||
} else if (PlaybackService.isCasting()) {
|
|
||||||
Intent intent = PlaybackService.getPlayerActivityIntent(this);
|
Intent intent = PlaybackService.getPlayerActivityIntent(this);
|
||||||
if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) {
|
if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) {
|
||||||
destroyingDueToReload = true;
|
destroyingDueToReload = true;
|
||||||
@ -135,17 +132,13 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean loadMediaInfo() {
|
protected void loadMediaInfo() {
|
||||||
if (!super.loadMediaInfo() || controller == null) {
|
super.loadMediaInfo();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Playable media = controller.getMedia();
|
Playable media = controller.getMedia();
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
getSupportActionBar().setSubtitle(media.getEpisodeTitle());
|
getSupportActionBar().setSubtitle(media.getEpisodeTitle());
|
||||||
getSupportActionBar().setTitle(media.getFeedTitle());
|
getSupportActionBar().setTitle(media.getFeedTitle());
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -347,7 +340,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
|||||||
Log.d(TAG, "ReloadNotification received, switching to Castplayer now");
|
Log.d(TAG, "ReloadNotification received, switching to Castplayer now");
|
||||||
destroyingDueToReload = true;
|
destroyingDueToReload = true;
|
||||||
finish();
|
finish();
|
||||||
startActivity(new Intent(this, MainActivity.class).putExtra(MainActivity.EXTRA_OPEN_PLAYER, true));
|
new MainActivityStarter(this).withOpenPlayer().start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,33 +2,34 @@ package de.danoeh.antennapod.activity;
|
|||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.WallpaperManager;
|
import android.app.WallpaperManager;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
|
|
||||||
import android.appwidget.AppWidgetManager;
|
import android.appwidget.AppWidgetManager;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.receiver.PlayerWidget;
|
import de.danoeh.antennapod.core.receiver.PlayerWidget;
|
||||||
import de.danoeh.antennapod.core.service.PlayerWidgetJobService;
|
import de.danoeh.antennapod.core.widget.WidgetUpdaterJobService;
|
||||||
|
|
||||||
public class WidgetConfigActivity extends AppCompatActivity {
|
public class WidgetConfigActivity extends AppCompatActivity {
|
||||||
private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
|
private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
|
||||||
|
|
||||||
private SeekBar opacitySeekBar;
|
private SeekBar opacitySeekBar;
|
||||||
private TextView opacityTextView;
|
private TextView opacityTextView;
|
||||||
private RelativeLayout widgetPreview;
|
private View widgetPreview;
|
||||||
|
private CheckBox ckRewind;
|
||||||
|
private CheckBox ckFastForward;
|
||||||
|
private CheckBox ckSkip;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -73,6 +74,32 @@ public class WidgetConfigActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
widgetPreview.findViewById(R.id.txtNoPlaying).setVisibility(View.GONE);
|
||||||
|
TextView title = widgetPreview.findViewById(R.id.txtvTitle);
|
||||||
|
title.setVisibility(View.VISIBLE);
|
||||||
|
title.setText(R.string.app_name);
|
||||||
|
TextView progress = widgetPreview.findViewById(R.id.txtvProgress);
|
||||||
|
progress.setVisibility(View.VISIBLE);
|
||||||
|
progress.setText(R.string.position_default_label);
|
||||||
|
|
||||||
|
ckRewind = findViewById(R.id.ckRewind);
|
||||||
|
ckRewind.setOnClickListener(v -> displayPreviewPanel());
|
||||||
|
ckFastForward = findViewById(R.id.ckFastForward);
|
||||||
|
ckFastForward.setOnClickListener(v -> displayPreviewPanel());
|
||||||
|
ckSkip = findViewById(R.id.ckSkip);
|
||||||
|
ckSkip.setOnClickListener(v -> displayPreviewPanel());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayPreviewPanel() {
|
||||||
|
boolean showExtendedPreview = ckRewind.isChecked() || ckFastForward.isChecked() || ckSkip.isChecked();
|
||||||
|
widgetPreview.findViewById(R.id.extendedButtonsContainer)
|
||||||
|
.setVisibility(showExtendedPreview ? View.VISIBLE : View.GONE);
|
||||||
|
widgetPreview.findViewById(R.id.butPlay).setVisibility(showExtendedPreview ? View.GONE : View.VISIBLE);
|
||||||
|
widgetPreview.findViewById(R.id.butFastForward)
|
||||||
|
.setVisibility(ckFastForward.isChecked() ? View.VISIBLE : View.GONE);
|
||||||
|
widgetPreview.findViewById(R.id.butSkip).setVisibility(ckSkip.isChecked() ? View.VISIBLE : View.GONE);
|
||||||
|
widgetPreview.findViewById(R.id.butRew).setVisibility(ckRewind.isChecked() ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayDeviceBackground() {
|
private void displayDeviceBackground() {
|
||||||
@ -91,13 +118,16 @@ public class WidgetConfigActivity extends AppCompatActivity {
|
|||||||
SharedPreferences prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE);
|
SharedPreferences prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE);
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor);
|
editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor);
|
||||||
|
editor.putBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, ckSkip.isChecked());
|
||||||
|
editor.putBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, ckRewind.isChecked());
|
||||||
|
editor.putBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, ckFastForward.isChecked());
|
||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
Intent resultValue = new Intent();
|
Intent resultValue = new Intent();
|
||||||
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
||||||
setResult(RESULT_OK, resultValue);
|
setResult(RESULT_OK, resultValue);
|
||||||
finish();
|
finish();
|
||||||
PlayerWidgetJobService.updateWidget(this);
|
WidgetUpdaterJobService.performBackgroundUpdate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getColorWithAlpha(int color, int opacity) {
|
private int getColorWithAlpha(int color, int opacity) {
|
||||||
|
@ -1,395 +0,0 @@
|
|||||||
package de.danoeh.antennapod.activity.gpoddernet;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.inputmethod.EditorInfo;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.ViewFlipper;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.BuildConfig;
|
|
||||||
import de.danoeh.antennapod.R;
|
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
|
||||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
|
||||||
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
|
||||||
import de.danoeh.antennapod.core.sync.SyncService;
|
|
||||||
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
|
|
||||||
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
|
|
||||||
import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetDevice;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Guides the user through the authentication process
|
|
||||||
* Step 1: Request username and password from user
|
|
||||||
* Step 2: Choose device from a list of available devices or create a new one
|
|
||||||
* Step 3: Choose from a list of actions
|
|
||||||
*/
|
|
||||||
public class GpodnetAuthenticationActivity extends AppCompatActivity {
|
|
||||||
private static final String TAG = "GpodnetAuthActivity";
|
|
||||||
|
|
||||||
private ViewFlipper viewFlipper;
|
|
||||||
|
|
||||||
private static final int STEP_DEFAULT = -1;
|
|
||||||
private static final int STEP_LOGIN = 0;
|
|
||||||
private static final int STEP_DEVICE = 1;
|
|
||||||
private static final int STEP_FINISH = 2;
|
|
||||||
|
|
||||||
private int currentStep = -1;
|
|
||||||
|
|
||||||
private GpodnetService service;
|
|
||||||
private volatile String username;
|
|
||||||
private volatile String password;
|
|
||||||
private volatile GpodnetDevice selectedDevice;
|
|
||||||
|
|
||||||
private View[] views;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
setTheme(UserPreferences.getTheme());
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
|
|
||||||
setContentView(R.layout.gpodnetauth_activity);
|
|
||||||
service = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetPreferences.getHostname());
|
|
||||||
|
|
||||||
viewFlipper = findViewById(R.id.viewflipper);
|
|
||||||
LayoutInflater inflater = (LayoutInflater)
|
|
||||||
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
views = new View[]{
|
|
||||||
inflater.inflate(R.layout.gpodnetauth_credentials, viewFlipper, false),
|
|
||||||
inflater.inflate(R.layout.gpodnetauth_device, viewFlipper, false),
|
|
||||||
inflater.inflate(R.layout.gpodnetauth_finish, viewFlipper, false)
|
|
||||||
};
|
|
||||||
for (View view : views) {
|
|
||||||
viewFlipper.addView(view);
|
|
||||||
}
|
|
||||||
advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (item.getItemId() == android.R.id.home) {
|
|
||||||
finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupLoginView(View view) {
|
|
||||||
final EditText username = view.findViewById(R.id.etxtUsername);
|
|
||||||
final EditText password = view.findViewById(R.id.etxtPassword);
|
|
||||||
final Button login = view.findViewById(R.id.butLogin);
|
|
||||||
final TextView txtvError = view.findViewById(R.id.txtvError);
|
|
||||||
final ProgressBar progressBar = view.findViewById(R.id.progBarLogin);
|
|
||||||
|
|
||||||
password.setOnEditorActionListener((v, actionID, event) ->
|
|
||||||
actionID == EditorInfo.IME_ACTION_GO && login.performClick());
|
|
||||||
|
|
||||||
login.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
|
|
||||||
final String usernameStr = username.getText().toString();
|
|
||||||
final String passwordStr = password.getText().toString();
|
|
||||||
|
|
||||||
if (usernameHasUnwantedChars(usernameStr)) {
|
|
||||||
txtvError.setText(R.string.gpodnetsync_username_characters_error);
|
|
||||||
txtvError.setVisibility(View.VISIBLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (BuildConfig.DEBUG) Log.d(TAG, "Checking login credentials");
|
|
||||||
AsyncTask<GpodnetService, Void, Void> authTask = new AsyncTask<GpodnetService, Void, Void>() {
|
|
||||||
|
|
||||||
volatile Exception exception;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
login.setEnabled(false);
|
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
|
||||||
txtvError.setVisibility(View.GONE);
|
|
||||||
// hide the keyboard
|
|
||||||
InputMethodManager inputManager = (InputMethodManager)
|
|
||||||
getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
inputManager.hideSoftInputFromWindow(login.getWindowToken(),
|
|
||||||
InputMethodManager.HIDE_NOT_ALWAYS);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void aVoid) {
|
|
||||||
super.onPostExecute(aVoid);
|
|
||||||
login.setEnabled(true);
|
|
||||||
progressBar.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (exception == null) {
|
|
||||||
advance();
|
|
||||||
} else {
|
|
||||||
txtvError.setText(exception.getCause().getMessage());
|
|
||||||
txtvError.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(GpodnetService... params) {
|
|
||||||
try {
|
|
||||||
params[0].authenticate(usernameStr, passwordStr);
|
|
||||||
GpodnetAuthenticationActivity.this.username = usernameStr;
|
|
||||||
GpodnetAuthenticationActivity.this.password = passwordStr;
|
|
||||||
} catch (GpodnetServiceException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
exception = e;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
authTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, service);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupDeviceView(View view) {
|
|
||||||
final EditText deviceID = view.findViewById(R.id.etxtDeviceID);
|
|
||||||
final EditText caption = view.findViewById(R.id.etxtCaption);
|
|
||||||
final Button createNewDevice = view.findViewById(R.id.butCreateNewDevice);
|
|
||||||
final Button chooseDevice = view.findViewById(R.id.butChooseExistingDevice);
|
|
||||||
final TextView txtvError = view.findViewById(R.id.txtvError);
|
|
||||||
final ProgressBar progBarCreateDevice = view.findViewById(R.id.progbarCreateDevice);
|
|
||||||
final Spinner spinnerDevices = view.findViewById(R.id.spinnerChooseDevice);
|
|
||||||
|
|
||||||
|
|
||||||
// load device list
|
|
||||||
final AtomicReference<List<GpodnetDevice>> devices = new AtomicReference<>();
|
|
||||||
new AsyncTask<GpodnetService, Void, List<GpodnetDevice>>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
chooseDevice.setEnabled(false);
|
|
||||||
spinnerDevices.setEnabled(false);
|
|
||||||
createNewDevice.setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(List<GpodnetDevice> gpodnetDevices) {
|
|
||||||
super.onPostExecute(gpodnetDevices);
|
|
||||||
if (gpodnetDevices != null) {
|
|
||||||
List<String> deviceNames = new ArrayList<>();
|
|
||||||
for (GpodnetDevice device : gpodnetDevices) {
|
|
||||||
deviceNames.add(device.getCaption());
|
|
||||||
}
|
|
||||||
spinnerDevices.setAdapter(new ArrayAdapter<>(GpodnetAuthenticationActivity.this,
|
|
||||||
android.R.layout.simple_spinner_dropdown_item, deviceNames));
|
|
||||||
spinnerDevices.setEnabled(true);
|
|
||||||
if (!deviceNames.isEmpty()) {
|
|
||||||
chooseDevice.setEnabled(true);
|
|
||||||
}
|
|
||||||
devices.set(gpodnetDevices);
|
|
||||||
deviceID.setText(generateDeviceID(gpodnetDevices));
|
|
||||||
createNewDevice.setEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<GpodnetDevice> doInBackground(GpodnetService... params) {
|
|
||||||
try {
|
|
||||||
return params[0].getDevices();
|
|
||||||
} catch (GpodnetServiceException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.execute(service);
|
|
||||||
|
|
||||||
|
|
||||||
createNewDevice.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (checkDeviceIDText(deviceID, caption, txtvError, devices.get())) {
|
|
||||||
final String deviceStr = deviceID.getText().toString();
|
|
||||||
final String captionStr = caption.getText().toString();
|
|
||||||
|
|
||||||
new AsyncTask<GpodnetService, Void, GpodnetDevice>() {
|
|
||||||
|
|
||||||
private volatile Exception exception;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
createNewDevice.setEnabled(false);
|
|
||||||
chooseDevice.setEnabled(false);
|
|
||||||
progBarCreateDevice.setVisibility(View.VISIBLE);
|
|
||||||
txtvError.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(GpodnetDevice result) {
|
|
||||||
super.onPostExecute(result);
|
|
||||||
createNewDevice.setEnabled(true);
|
|
||||||
chooseDevice.setEnabled(true);
|
|
||||||
progBarCreateDevice.setVisibility(View.GONE);
|
|
||||||
if (exception == null) {
|
|
||||||
selectedDevice = result;
|
|
||||||
advance();
|
|
||||||
} else {
|
|
||||||
txtvError.setText(exception.getMessage());
|
|
||||||
txtvError.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected GpodnetDevice doInBackground(GpodnetService... params) {
|
|
||||||
try {
|
|
||||||
params[0].configureDevice(deviceStr, captionStr, GpodnetDevice.DeviceType.MOBILE);
|
|
||||||
return new GpodnetDevice(deviceStr, captionStr, GpodnetDevice.DeviceType.MOBILE.toString(), 0);
|
|
||||||
} catch (GpodnetServiceException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
exception = e;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}.execute(service);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
chooseDevice.setOnClickListener(v -> {
|
|
||||||
final int position = spinnerDevices.getSelectedItemPosition();
|
|
||||||
if (position != AdapterView.INVALID_POSITION) {
|
|
||||||
selectedDevice = devices.get().get(position);
|
|
||||||
advance();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String generateDeviceID(List<GpodnetDevice> gpodnetDevices) {
|
|
||||||
// devices names must be of a certain form:
|
|
||||||
// https://gpoddernet.readthedocs.org/en/latest/api/reference/general.html#devices
|
|
||||||
// This is more restrictive than needed, but I think it makes for more readable names.
|
|
||||||
String baseId = Build.MODEL.replaceAll("\\W", "");
|
|
||||||
String id = baseId;
|
|
||||||
int num = 0;
|
|
||||||
|
|
||||||
while (isDeviceWithIdInList(id, gpodnetDevices)) {
|
|
||||||
id = baseId + "_" + num;
|
|
||||||
num++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isDeviceWithIdInList(String id, List<GpodnetDevice> gpodnetDevices) {
|
|
||||||
if (gpodnetDevices == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (GpodnetDevice device : gpodnetDevices) {
|
|
||||||
if (device.getId().equals(id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkDeviceIDText(EditText deviceID, EditText caption, TextView txtvError, List<GpodnetDevice> devices) {
|
|
||||||
String text = deviceID.getText().toString();
|
|
||||||
if (text.length() == 0) {
|
|
||||||
txtvError.setText(R.string.gpodnetauth_device_errorEmpty);
|
|
||||||
txtvError.setVisibility(View.VISIBLE);
|
|
||||||
return false;
|
|
||||||
} else if (caption.length() == 0) {
|
|
||||||
txtvError.setText(R.string.gpodnetauth_device_caption_errorEmpty);
|
|
||||||
txtvError.setVisibility(View.VISIBLE);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
if (devices != null) {
|
|
||||||
if (isDeviceWithIdInList(text, devices)) {
|
|
||||||
txtvError.setText(R.string.gpodnetauth_device_errorAlreadyUsed);
|
|
||||||
txtvError.setVisibility(View.VISIBLE);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
txtvError.setVisibility(View.GONE);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupFinishView(View view) {
|
|
||||||
final Button sync = view.findViewById(R.id.butSyncNow);
|
|
||||||
final Button back = view.findViewById(R.id.butGoMainscreen);
|
|
||||||
|
|
||||||
sync.setOnClickListener(v -> {
|
|
||||||
finish();
|
|
||||||
SyncService.sync(getApplicationContext());
|
|
||||||
});
|
|
||||||
back.setOnClickListener(v -> {
|
|
||||||
Intent intent = new Intent(GpodnetAuthenticationActivity.this, MainActivity.class);
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeLoginCredentials() {
|
|
||||||
if (BuildConfig.DEBUG) Log.d(TAG, "Writing login credentials");
|
|
||||||
GpodnetPreferences.setUsername(username);
|
|
||||||
GpodnetPreferences.setPassword(password);
|
|
||||||
GpodnetPreferences.setDeviceID(selectedDevice.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void advance() {
|
|
||||||
if (currentStep < STEP_FINISH) {
|
|
||||||
|
|
||||||
View view = views[currentStep + 1];
|
|
||||||
if (currentStep == STEP_DEFAULT) {
|
|
||||||
setupLoginView(view);
|
|
||||||
} else if (currentStep == STEP_LOGIN) {
|
|
||||||
if (username == null || password == null) {
|
|
||||||
throw new IllegalStateException("Username and password must not be null here");
|
|
||||||
} else {
|
|
||||||
setupDeviceView(view);
|
|
||||||
}
|
|
||||||
} else if (currentStep == STEP_DEVICE) {
|
|
||||||
if (selectedDevice == null) {
|
|
||||||
throw new IllegalStateException("Device must not be null here");
|
|
||||||
} else {
|
|
||||||
writeLoginCredentials();
|
|
||||||
setupFinishView(view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (currentStep != STEP_DEFAULT) {
|
|
||||||
viewFlipper.showNext();
|
|
||||||
}
|
|
||||||
currentStep++;
|
|
||||||
} else {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean usernameHasUnwantedChars(String username) {
|
|
||||||
Pattern special = Pattern.compile("[!@#$%&*()+=|<>?{}\\[\\]~]");
|
|
||||||
Matcher containsUnwantedChars = special.matcher(username);
|
|
||||||
return containsUnwantedChars.find();
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,9 +20,9 @@ import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
|||||||
import de.danoeh.antennapod.core.util.Converter;
|
import de.danoeh.antennapod.core.util.Converter;
|
||||||
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
|
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
|
||||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||||
import de.danoeh.antennapod.view.CircularProgressBar;
|
import de.danoeh.antennapod.ui.common.CircularProgressBar;
|
||||||
|
|
||||||
public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapter.ChapterHolder> {
|
public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapter.ChapterHolder> {
|
||||||
private Playable media;
|
private Playable media;
|
||||||
@ -42,7 +42,7 @@ public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapte
|
|||||||
hasImages = false;
|
hasImages = false;
|
||||||
if (media.getChapters() != null) {
|
if (media.getChapters() != null) {
|
||||||
for (Chapter chapter : media.getChapters()) {
|
for (Chapter chapter : media.getChapters()) {
|
||||||
if (!ignoreChapter(chapter) && !TextUtils.isEmpty(chapter.getImageUrl())) {
|
if (!TextUtils.isEmpty(chapter.getImageUrl())) {
|
||||||
hasImages = true;
|
hasImages = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,14 +125,7 @@ public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapte
|
|||||||
if (media == null || media.getChapters() == null) {
|
if (media == null || media.getChapters() == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// ignore invalid chapters
|
return media.getChapters().size();
|
||||||
int counter = 0;
|
|
||||||
for (Chapter chapter : media.getChapters()) {
|
|
||||||
if (!ignoreChapter(chapter)) {
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return counter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ChapterHolder extends RecyclerView.ViewHolder {
|
static class ChapterHolder extends RecyclerView.ViewHolder {
|
||||||
@ -171,22 +164,8 @@ public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapte
|
|||||||
notifyItemChanged(currentChapterIndex, "foo");
|
notifyItemChanged(currentChapterIndex, "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean ignoreChapter(Chapter c) {
|
|
||||||
return media.getDuration() > 0 && media.getDuration() < c.getStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Chapter getItem(int position) {
|
public Chapter getItem(int position) {
|
||||||
int i = 0;
|
return media.getChapters().get(position);
|
||||||
for (Chapter chapter : media.getChapters()) {
|
|
||||||
if (!ignoreChapter(chapter)) {
|
|
||||||
if (i == position) {
|
|
||||||
return chapter;
|
|
||||||
} else {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
|
@ -20,7 +20,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
|
|||||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
import de.danoeh.antennapod.view.viewholder.DownloadItemViewHolder;
|
import de.danoeh.antennapod.view.viewholder.DownloadItemViewHolder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,16 +68,14 @@ public class DownloadLogAdapter extends BaseAdapter {
|
|||||||
holder.icon.setContentDescription(context.getString(R.string.download_successful));
|
holder.icon.setContentDescription(context.getString(R.string.download_successful));
|
||||||
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
|
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
|
||||||
holder.reason.setVisibility(View.GONE);
|
holder.reason.setVisibility(View.GONE);
|
||||||
|
holder.tapForDetails.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
holder.icon.setTextColor(ContextCompat.getColor(context, R.color.download_failed_red));
|
holder.icon.setTextColor(ContextCompat.getColor(context, R.color.download_failed_red));
|
||||||
holder.icon.setText("{fa-times-circle}");
|
holder.icon.setText("{fa-times-circle}");
|
||||||
holder.icon.setContentDescription(context.getString(R.string.error_label));
|
holder.icon.setContentDescription(context.getString(R.string.error_label));
|
||||||
String reasonText = status.getReason().getErrorString(context);
|
holder.reason.setText(status.getReason().getErrorString(context));
|
||||||
if (status.getReasonDetailed() != null) {
|
|
||||||
reasonText += ": " + status.getReasonDetailed();
|
|
||||||
}
|
|
||||||
holder.reason.setText(reasonText);
|
|
||||||
holder.reason.setVisibility(View.VISIBLE);
|
holder.reason.setVisibility(View.VISIBLE);
|
||||||
|
holder.tapForDetails.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
if (newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
|
if (newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
|
||||||
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
|
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
|
||||||
|
@ -15,8 +15,8 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
|
|||||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
import de.danoeh.antennapod.view.CircularProgressBar;
|
import de.danoeh.antennapod.ui.common.CircularProgressBar;
|
||||||
|
|
||||||
public class DownloadlistAdapter extends BaseAdapter {
|
public class DownloadlistAdapter extends BaseAdapter {
|
||||||
|
|
||||||
@ -68,8 +68,8 @@ public class DownloadlistAdapter extends BaseAdapter {
|
|||||||
holder.secondaryActionButton.setContentDescription(context.getString(R.string.cancel_download_label));
|
holder.secondaryActionButton.setContentDescription(context.getString(R.string.cancel_download_label));
|
||||||
holder.secondaryActionButton.setTag(downloader);
|
holder.secondaryActionButton.setTag(downloader);
|
||||||
holder.secondaryActionButton.setOnClickListener(butSecondaryListener);
|
holder.secondaryActionButton.setOnClickListener(butSecondaryListener);
|
||||||
holder.secondaryActionProgress.setPercentage(0, request);
|
|
||||||
|
|
||||||
|
boolean percentageWasSet = false;
|
||||||
String status = "";
|
String status = "";
|
||||||
if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
|
if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
|
||||||
status += context.getString(R.string.download_type_feed);
|
status += context.getString(R.string.download_type_feed);
|
||||||
@ -85,8 +85,12 @@ public class DownloadlistAdapter extends BaseAdapter {
|
|||||||
status += " / " + Formatter.formatShortFileSize(context, request.getSize());
|
status += " / " + Formatter.formatShortFileSize(context, request.getSize());
|
||||||
holder.secondaryActionProgress.setPercentage(
|
holder.secondaryActionProgress.setPercentage(
|
||||||
0.01f * Math.max(1, request.getProgressPercent()), request);
|
0.01f * Math.max(1, request.getProgressPercent()), request);
|
||||||
|
percentageWasSet = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!percentageWasSet) {
|
||||||
|
holder.secondaryActionProgress.setPercentage(0, request);
|
||||||
|
}
|
||||||
holder.status.setText(status);
|
holder.status.setText(status);
|
||||||
|
|
||||||
return convertView;
|
return convertView;
|
||||||
|
@ -20,6 +20,7 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
|||||||
import de.danoeh.antennapod.core.util.DateUtils;
|
import de.danoeh.antennapod.core.util.DateUtils;
|
||||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||||
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
|
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
|
||||||
|
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
|
||||||
import de.danoeh.antennapod.dialog.StreamingConfirmationDialog;
|
import de.danoeh.antennapod.dialog.StreamingConfirmationDialog;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -59,7 +60,7 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
|
|||||||
holder.title.setText(item.getTitle());
|
holder.title.setText(item.getTitle());
|
||||||
holder.pubDate.setText(DateUtils.formatAbbrev(getContext(), item.getPubDate()));
|
holder.pubDate.setText(DateUtils.formatAbbrev(getContext(), item.getPubDate()));
|
||||||
if (item.getDescription() != null) {
|
if (item.getDescription() != null) {
|
||||||
String description = item.getDescription()
|
String description = HtmlToPlainText.getPlainText(item.getDescription())
|
||||||
.replaceAll("\n", " ")
|
.replaceAll("\n", " ")
|
||||||
.replaceAll("\\s+", " ")
|
.replaceAll("\\s+", " ")
|
||||||
.trim();
|
.trim();
|
||||||
|
@ -10,7 +10,7 @@ import de.danoeh.antennapod.R;
|
|||||||
import de.danoeh.antennapod.activity.MainActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.core.feed.Feed;
|
import de.danoeh.antennapod.core.feed.Feed;
|
||||||
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
||||||
import de.danoeh.antennapod.view.SquareImageView;
|
import de.danoeh.antennapod.ui.common.SquareImageView;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -24,7 +24,6 @@ import de.danoeh.antennapod.core.feed.Feed;
|
|||||||
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.storage.NavDrawerData;
|
import de.danoeh.antennapod.core.storage.NavDrawerData;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
|
||||||
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
||||||
import de.danoeh.antennapod.fragment.DownloadsFragment;
|
import de.danoeh.antennapod.fragment.DownloadsFragment;
|
||||||
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
||||||
@ -32,6 +31,7 @@ import de.danoeh.antennapod.fragment.NavDrawerFragment;
|
|||||||
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
|
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
|
||||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||||
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
||||||
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
@ -76,7 +76,7 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
if (key.equals(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)) {
|
if (UserPreferences.PREF_HIDDEN_DRAWER_ITEMS.equals(key)) {
|
||||||
loadItems();
|
loadItems();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,7 +314,7 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Glide.with(context)
|
Glide.with(context)
|
||||||
.load(feed.getImageLocation())
|
.load(feed.getImageUrl())
|
||||||
.apply(new RequestOptions()
|
.apply(new RequestOptions()
|
||||||
.placeholder(R.color.light_gray)
|
.placeholder(R.color.light_gray)
|
||||||
.error(R.color.light_gray)
|
.error(R.color.light_gray)
|
||||||
|
@ -66,7 +66,7 @@ public abstract class StatisticsListAdapter extends RecyclerView.Adapter<Recycle
|
|||||||
StatisticsHolder holder = (StatisticsHolder) h;
|
StatisticsHolder holder = (StatisticsHolder) h;
|
||||||
StatisticsItem statsItem = statisticsData.get(position - 1);
|
StatisticsItem statsItem = statisticsData.get(position - 1);
|
||||||
Glide.with(context)
|
Glide.with(context)
|
||||||
.load(statsItem.feed.getImageLocation())
|
.load(statsItem.feed.getImageUrl())
|
||||||
.apply(new RequestOptions()
|
.apply(new RequestOptions()
|
||||||
.placeholder(R.color.light_gray)
|
.placeholder(R.color.light_gray)
|
||||||
.error(R.color.light_gray)
|
.error(R.color.light_gray)
|
||||||
|
@ -22,8 +22,8 @@ import de.danoeh.antennapod.activity.MainActivity;
|
|||||||
import de.danoeh.antennapod.core.feed.Feed;
|
import de.danoeh.antennapod.core.feed.Feed;
|
||||||
import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
|
import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
|
||||||
import de.danoeh.antennapod.core.storage.NavDrawerData;
|
import de.danoeh.antennapod.core.storage.NavDrawerData;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
|
||||||
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
||||||
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
import jp.shts.android.library.TriangleLabelView;
|
import jp.shts.android.library.TriangleLabelView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,7 +112,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
|
|||||||
boolean textAndImageCombined = feed.isLocalFeed()
|
boolean textAndImageCombined = feed.isLocalFeed()
|
||||||
&& LocalFeedUpdater.getDefaultIconUrl(convertView.getContext()).equals(feed.getImageUrl());
|
&& LocalFeedUpdater.getDefaultIconUrl(convertView.getContext()).equals(feed.getImageUrl());
|
||||||
new CoverLoader(mainActivityRef.get())
|
new CoverLoader(mainActivityRef.get())
|
||||||
.withUri(feed.getImageLocation())
|
.withUri(feed.getImageUrl())
|
||||||
.withPlaceholderView(holder.feedTitle, textAndImageCombined)
|
.withPlaceholderView(holder.feedTitle, textAndImageCombined)
|
||||||
.withCoverView(holder.imageView)
|
.withCoverView(holder.imageView)
|
||||||
.load();
|
.load();
|
||||||
@ -123,7 +123,6 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
|
|||||||
.withCoverView(holder.imageView)
|
.withCoverView(holder.imageView)
|
||||||
.load();
|
.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
return convertView;
|
return convertView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,21 +2,19 @@ package de.danoeh.antennapod.config;
|
|||||||
|
|
||||||
import de.danoeh.antennapod.BuildConfig;
|
import de.danoeh.antennapod.BuildConfig;
|
||||||
import de.danoeh.antennapod.core.ClientConfig;
|
import de.danoeh.antennapod.core.ClientConfig;
|
||||||
import de.danoeh.antennapod.core.storage.APDownloadAlgorithm;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the ClientConfig class of the core package.
|
* Configures the ClientConfig class of the core package.
|
||||||
*/
|
*/
|
||||||
class ClientConfigurator {
|
class ClientConfigurator {
|
||||||
|
|
||||||
private ClientConfigurator(){}
|
private ClientConfigurator() {
|
||||||
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ClientConfig.USER_AGENT = "AntennaPod/" + BuildConfig.VERSION_NAME;
|
ClientConfig.USER_AGENT = "AntennaPod/" + BuildConfig.VERSION_NAME;
|
||||||
ClientConfig.applicationCallbacks = new ApplicationCallbacksImpl();
|
ClientConfig.applicationCallbacks = new ApplicationCallbacksImpl();
|
||||||
ClientConfig.downloadServiceCallbacks = new DownloadServiceCallbacksImpl();
|
ClientConfig.downloadServiceCallbacks = new DownloadServiceCallbacksImpl();
|
||||||
ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl();
|
|
||||||
ClientConfig.automaticDownloadAlgorithm = new APDownloadAlgorithm();
|
|
||||||
ClientConfig.castCallbacks = new CastCallbackImpl();
|
ClientConfig.castCallbacks = new CastCallbackImpl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,8 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks {
|
|||||||
@Override
|
@Override
|
||||||
public PendingIntent getAuthentificationNotificationContentIntent(Context context, DownloadRequest request) {
|
public PendingIntent getAuthentificationNotificationContentIntent(Context context, DownloadRequest request) {
|
||||||
final Intent activityIntent = new Intent(context.getApplicationContext(), DownloadAuthenticationActivity.class);
|
final Intent activityIntent = new Intent(context.getApplicationContext(), DownloadAuthenticationActivity.class);
|
||||||
|
activityIntent.setAction("request" + request.getFeedfileId());
|
||||||
activityIntent.putExtra(DownloadAuthenticationActivity.ARG_DOWNLOAD_REQUEST, request);
|
activityIntent.putExtra(DownloadAuthenticationActivity.ARG_DOWNLOAD_REQUEST, request);
|
||||||
activityIntent.putExtra(DownloadAuthenticationActivity.ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, true);
|
|
||||||
return PendingIntent.getActivity(context.getApplicationContext(),
|
return PendingIntent.getActivity(context.getApplicationContext(),
|
||||||
R.id.pending_intent_download_service_auth, activityIntent, PendingIntent.FLAG_ONE_SHOT);
|
R.id.pending_intent_download_service_auth, activityIntent, PendingIntent.FLAG_ONE_SHOT);
|
||||||
}
|
}
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
package de.danoeh.antennapod.config;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Build;
|
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
|
||||||
import de.danoeh.antennapod.activity.VideoplayerActivity;
|
|
||||||
import de.danoeh.antennapod.core.PlaybackServiceCallbacks;
|
|
||||||
import de.danoeh.antennapod.core.feed.MediaType;
|
|
||||||
|
|
||||||
public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
|
|
||||||
@Override
|
|
||||||
public Intent getPlayerActivityIntent(Context context, MediaType mediaType, boolean remotePlayback) {
|
|
||||||
if (mediaType == MediaType.AUDIO || remotePlayback) {
|
|
||||||
return new Intent(context, MainActivity.class).putExtra(MainActivity.EXTRA_OPEN_PLAYER, true);
|
|
||||||
} else {
|
|
||||||
Intent i = new Intent(context, VideoplayerActivity.class);
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +1,50 @@
|
|||||||
package de.danoeh.antennapod.dialog;
|
package de.danoeh.antennapod.dialog;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.View;
|
import android.text.method.HideReturnsTransformationMethod;
|
||||||
import android.widget.EditText;
|
import android.text.method.PasswordTransformationMethod;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.databinding.AuthenticationDialogBinding;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a dialog with a username and password text field and an optional checkbox to save username and preferences.
|
* Displays a dialog with a username and password text field and an optional checkbox to save username and preferences.
|
||||||
*/
|
*/
|
||||||
public abstract class AuthenticationDialog extends AlertDialog.Builder {
|
public abstract class AuthenticationDialog extends AlertDialog.Builder {
|
||||||
|
boolean passwordHidden = true;
|
||||||
|
|
||||||
public AuthenticationDialog(Context context, int titleRes, boolean enableUsernameField,
|
public AuthenticationDialog(Context context, int titleRes, boolean enableUsernameField,
|
||||||
String usernameInitialValue, String passwordInitialValue) {
|
String usernameInitialValue, String passwordInitialValue) {
|
||||||
super(context);
|
super(context);
|
||||||
setTitle(titleRes);
|
setTitle(titleRes);
|
||||||
View rootView = View.inflate(context, R.layout.authentication_dialog, null);
|
AuthenticationDialogBinding viewBinding = AuthenticationDialogBinding.inflate(LayoutInflater.from(context));
|
||||||
setView(rootView);
|
setView(viewBinding.getRoot());
|
||||||
|
|
||||||
final EditText etxtUsername = rootView.findViewById(R.id.etxtUsername);
|
viewBinding.usernameEditText.setEnabled(enableUsernameField);
|
||||||
final EditText etxtPassword = rootView.findViewById(R.id.etxtPassword);
|
|
||||||
|
|
||||||
etxtUsername.setEnabled(enableUsernameField);
|
|
||||||
if (usernameInitialValue != null) {
|
if (usernameInitialValue != null) {
|
||||||
etxtUsername.setText(usernameInitialValue);
|
viewBinding.usernameEditText.setText(usernameInitialValue);
|
||||||
}
|
}
|
||||||
if (passwordInitialValue != null) {
|
if (passwordInitialValue != null) {
|
||||||
etxtPassword.setText(passwordInitialValue);
|
viewBinding.passwordEditText.setText(passwordInitialValue);
|
||||||
}
|
}
|
||||||
|
viewBinding.showPasswordButton.setOnClickListener(v -> {
|
||||||
|
if (passwordHidden) {
|
||||||
|
viewBinding.passwordEditText.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
|
||||||
|
viewBinding.showPasswordButton.setAlpha(1.0f);
|
||||||
|
} else {
|
||||||
|
viewBinding.passwordEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
|
||||||
|
viewBinding.showPasswordButton.setAlpha(0.6f);
|
||||||
|
}
|
||||||
|
passwordHidden = !passwordHidden;
|
||||||
|
});
|
||||||
|
|
||||||
setOnCancelListener(dialog -> onCancelled());
|
setOnCancelListener(dialog -> onCancelled());
|
||||||
|
setOnDismissListener(dialog -> onCancelled());
|
||||||
setNegativeButton(R.string.cancel_label, null);
|
setNegativeButton(R.string.cancel_label, null);
|
||||||
setPositiveButton(R.string.confirm_label, (dialog, which)
|
setPositiveButton(R.string.confirm_label, (dialog, which)
|
||||||
-> onConfirmed(etxtUsername.getText().toString(), etxtPassword.getText().toString()));
|
-> onConfirmed(viewBinding.usernameEditText.getText().toString(),
|
||||||
|
viewBinding.passwordEditText.getText().toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onCancelled() {
|
protected void onCancelled() {
|
||||||
|
@ -28,7 +28,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester;
|
|||||||
import de.danoeh.antennapod.core.util.FeedItemPermutors;
|
import de.danoeh.antennapod.core.util.FeedItemPermutors;
|
||||||
import de.danoeh.antennapod.core.util.LongList;
|
import de.danoeh.antennapod.core.util.LongList;
|
||||||
import de.danoeh.antennapod.core.util.SortOrder;
|
import de.danoeh.antennapod.core.util.SortOrder;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -16,7 +16,7 @@ import java.util.Set;
|
|||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.core.feed.FeedItemFilter;
|
import de.danoeh.antennapod.core.feed.FeedItemFilter;
|
||||||
import de.danoeh.antennapod.core.feed.FeedItemFilterGroup;
|
import de.danoeh.antennapod.core.feed.FeedItemFilterGroup;
|
||||||
import de.danoeh.antennapod.view.RecursiveRadioGroup;
|
import de.danoeh.antennapod.ui.common.RecursiveRadioGroup;
|
||||||
|
|
||||||
public abstract class FilterDialog {
|
public abstract class FilterDialog {
|
||||||
|
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
package de.danoeh.antennapod.dialog;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.InputType;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
|
||||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
|
||||||
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a dialog that lets the user change the hostname for the gpodder.net service.
|
|
||||||
*/
|
|
||||||
public class GpodnetSetHostnameDialog {
|
|
||||||
|
|
||||||
private GpodnetSetHostnameDialog(){}
|
|
||||||
|
|
||||||
private static final String TAG = "GpodnetSetHostnameDialog";
|
|
||||||
|
|
||||||
public static AlertDialog createDialog(final Context context) {
|
|
||||||
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
|
|
||||||
final EditText et = new EditText(context);
|
|
||||||
et.setText(GpodnetPreferences.getHostname());
|
|
||||||
et.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
|
|
||||||
dialog.setTitle(R.string.pref_gpodnet_sethostname_title)
|
|
||||||
.setView(setupContentView(context, et))
|
|
||||||
.setPositiveButton(R.string.confirm_label, (dialog1, which) -> {
|
|
||||||
final Editable e = et.getText();
|
|
||||||
if (e != null) {
|
|
||||||
GpodnetPreferences.setHostname(e.toString());
|
|
||||||
}
|
|
||||||
dialog1.dismiss();
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.cancel_label, (dialog1, which) -> dialog1.cancel())
|
|
||||||
.setNeutralButton(R.string.pref_gpodnet_sethostname_use_default_host, (dialog1, which) -> {
|
|
||||||
GpodnetPreferences.setHostname(GpodnetService.DEFAULT_BASE_HOST);
|
|
||||||
dialog1.dismiss();
|
|
||||||
})
|
|
||||||
.setCancelable(true);
|
|
||||||
return dialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static View setupContentView(Context context, EditText et) {
|
|
||||||
LinearLayout ll = new LinearLayout(context);
|
|
||||||
ll.addView(et);
|
|
||||||
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) et.getLayoutParams();
|
|
||||||
if (params != null) {
|
|
||||||
params.setMargins(8, 8, 8, 8);
|
|
||||||
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
|
||||||
params.height = ViewGroup.LayoutParams.MATCH_PARENT;
|
|
||||||
}
|
|
||||||
return ll;
|
|
||||||
}
|
|
||||||
}
|
|
@ -41,7 +41,7 @@ public class PlaybackControlsDialog extends DialogFragment {
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
controller = new PlaybackController(getActivity()) {
|
controller = new PlaybackController(getActivity()) {
|
||||||
@Override
|
@Override
|
||||||
public void setupGUI() {
|
public void loadMediaInfo() {
|
||||||
setupUi();
|
setupUi();
|
||||||
setupAudioTracks();
|
setupAudioTracks();
|
||||||
}
|
}
|
||||||
|
@ -47,12 +47,12 @@ public class SleepTimerDialog extends DialogFragment {
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
controller = new PlaybackController(getActivity()) {
|
controller = new PlaybackController(getActivity()) {
|
||||||
@Override
|
@Override
|
||||||
public void setupGUI() {
|
public void onSleepTimerUpdate() {
|
||||||
updateTime();
|
updateTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSleepTimerUpdate() {
|
public void loadMediaInfo() {
|
||||||
updateTime();
|
updateTime();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,7 @@ import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
|
|||||||
import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
|
import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
|
||||||
import de.danoeh.antennapod.core.feed.SubscriptionsFilterGroup;
|
import de.danoeh.antennapod.core.feed.SubscriptionsFilterGroup;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.view.RecursiveRadioGroup;
|
import de.danoeh.antennapod.ui.common.RecursiveRadioGroup;
|
||||||
|
|
||||||
public class SubscriptionsFilterDialog {
|
public class SubscriptionsFilterDialog {
|
||||||
public static void showDialog(Context context) {
|
public static void showDialog(Context context) {
|
||||||
|
@ -56,12 +56,12 @@ public class VariableSpeedDialog extends DialogFragment {
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
controller = new PlaybackController(getActivity()) {
|
controller = new PlaybackController(getActivity()) {
|
||||||
@Override
|
@Override
|
||||||
public void setupGUI() {
|
public void onPlaybackSpeedChange() {
|
||||||
updateSpeed();
|
updateSpeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlaybackSpeedChange() {
|
public void loadMediaInfo() {
|
||||||
updateSpeed();
|
updateSpeed();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -18,7 +18,7 @@ public class GpodnetPodcastSearcher implements PodcastSearcher {
|
|||||||
return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> {
|
return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> {
|
||||||
try {
|
try {
|
||||||
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
|
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
|
||||||
GpodnetPreferences.getHostname());
|
GpodnetPreferences.getHosturl());
|
||||||
List<GpodnetPodcast> gpodnetPodcasts = service.searchPodcasts(query, 0);
|
List<GpodnetPodcast> gpodnetPodcasts = service.searchPodcasts(query, 0);
|
||||||
List<PodcastSearchResult> results = new ArrayList<>();
|
List<PodcastSearchResult> results = new ArrayList<>();
|
||||||
for (GpodnetPodcast podcast : gpodnetPodcasts) {
|
for (GpodnetPodcast podcast : gpodnetPodcasts) {
|
||||||
|
@ -15,11 +15,11 @@ public class PodcastSearcherRegistry {
|
|||||||
public static List<SearcherInfo> getSearchProviders() {
|
public static List<SearcherInfo> getSearchProviders() {
|
||||||
if (searchProviders == null) {
|
if (searchProviders == null) {
|
||||||
searchProviders = new ArrayList<>();
|
searchProviders = new ArrayList<>();
|
||||||
searchProviders.add(new SearcherInfo(new CombinedSearcher(), 1.f));
|
searchProviders.add(new SearcherInfo(new CombinedSearcher(), 1.0f));
|
||||||
searchProviders.add(new SearcherInfo(new ItunesPodcastSearcher(), 1.f));
|
|
||||||
searchProviders.add(new SearcherInfo(new FyydPodcastSearcher(), 1.f));
|
|
||||||
searchProviders.add(new SearcherInfo(new GpodnetPodcastSearcher(), 0.0f));
|
searchProviders.add(new SearcherInfo(new GpodnetPodcastSearcher(), 0.0f));
|
||||||
searchProviders.add(new SearcherInfo(new PodcastIndexPodcastSearcher(), 0.0f));
|
searchProviders.add(new SearcherInfo(new FyydPodcastSearcher(), 1.0f));
|
||||||
|
searchProviders.add(new SearcherInfo(new ItunesPodcastSearcher(), 1.0f));
|
||||||
|
searchProviders.add(new SearcherInfo(new PodcastIndexPodcastSearcher(), 1.0f));
|
||||||
}
|
}
|
||||||
return searchProviders;
|
return searchProviders;
|
||||||
}
|
}
|
||||||
|
@ -50,9 +50,11 @@ public class AddFeedFragment extends Fragment {
|
|||||||
public static final String TAG = "AddFeedFragment";
|
public static final String TAG = "AddFeedFragment";
|
||||||
private static final int REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH = 1;
|
private static final int REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH = 1;
|
||||||
private static final int REQUEST_CODE_ADD_LOCAL_FOLDER = 2;
|
private static final int REQUEST_CODE_ADD_LOCAL_FOLDER = 2;
|
||||||
|
private static final String KEY_UP_ARROW = "up_arrow";
|
||||||
|
|
||||||
private AddfeedBinding viewBinding;
|
private AddfeedBinding viewBinding;
|
||||||
private MainActivity activity;
|
private MainActivity activity;
|
||||||
|
private boolean displayUpArrow;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -64,7 +66,11 @@ public class AddFeedFragment extends Fragment {
|
|||||||
activity = (MainActivity) getActivity();
|
activity = (MainActivity) getActivity();
|
||||||
|
|
||||||
Toolbar toolbar = viewBinding.toolbar;
|
Toolbar toolbar = viewBinding.toolbar;
|
||||||
((MainActivity) getActivity()).setupToolbarToggle(toolbar);
|
displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
|
||||||
|
}
|
||||||
|
((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
|
||||||
|
|
||||||
viewBinding.searchItunesButton.setOnClickListener(v
|
viewBinding.searchItunesButton.setOnClickListener(v
|
||||||
-> activity.loadChildFragment(OnlineSearchFragment.newInstance(ItunesPodcastSearcher.class)));
|
-> activity.loadChildFragment(OnlineSearchFragment.newInstance(ItunesPodcastSearcher.class)));
|
||||||
@ -119,6 +125,12 @@ public class AddFeedFragment extends Fragment {
|
|||||||
return viewBinding.getRoot();
|
return viewBinding.getRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
private void showAddViaUrlDialog() {
|
private void showAddViaUrlDialog() {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||||
builder.setTitle(R.string.add_podcast_by_url);
|
builder.setTitle(R.string.add_podcast_by_url);
|
||||||
@ -196,7 +208,11 @@ public class AddFeedFragment extends Fragment {
|
|||||||
if (documentFile == null) {
|
if (documentFile == null) {
|
||||||
throw new IllegalArgumentException("Unable to retrieve document tree");
|
throw new IllegalArgumentException("Unable to retrieve document tree");
|
||||||
}
|
}
|
||||||
Feed dirFeed = new Feed(Feed.PREFIX_LOCAL_FOLDER + uri.toString(), null, documentFile.getName());
|
String title = documentFile.getName();
|
||||||
|
if (title == null) {
|
||||||
|
title = getString(R.string.local_folder);
|
||||||
|
}
|
||||||
|
Feed dirFeed = new Feed(Feed.PREFIX_LOCAL_FOLDER + uri.toString(), null, title);
|
||||||
dirFeed.setItems(Collections.emptyList());
|
dirFeed.setItems(Collections.emptyList());
|
||||||
dirFeed.setSortOrder(SortOrder.EPISODE_TITLE_A_Z);
|
dirFeed.setSortOrder(SortOrder.EPISODE_TITLE_A_Z);
|
||||||
Feed fromDatabase = DBTasks.updateFeed(getContext(), dirFeed, false);
|
Feed fromDatabase = DBTasks.updateFeed(getContext(), dirFeed, false);
|
||||||
|
@ -104,13 +104,12 @@ public class AllEpisodesFragment extends EpisodesListFragment {
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
protected List<FeedItem> loadData() {
|
protected List<FeedItem> loadData() {
|
||||||
return feedItemFilter.filter(DBReader.getRecentlyPublishedEpisodes(0, page * EPISODES_PER_PAGE));
|
return DBReader.getRecentlyPublishedEpisodes(0, page * EPISODES_PER_PAGE, feedItemFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
protected List<FeedItem> loadMoreData() {
|
protected List<FeedItem> loadMoreData() {
|
||||||
return feedItemFilter.filter(DBReader.getRecentlyPublishedEpisodes((page - 1) * EPISODES_PER_PAGE,
|
return DBReader.getRecentlyPublishedEpisodes((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE, feedItemFilter);
|
||||||
EPISODES_PER_PAGE));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package de.danoeh.antennapod.fragment;
|
package de.danoeh.antennapod.fragment;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@ -17,7 +15,9 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.cardview.widget.CardView;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||||
@ -29,11 +29,14 @@ import de.danoeh.antennapod.activity.CastEnabledActivity;
|
|||||||
import de.danoeh.antennapod.activity.MainActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.core.event.FavoritesEvent;
|
import de.danoeh.antennapod.core.event.FavoritesEvent;
|
||||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||||
|
import de.danoeh.antennapod.core.feed.Chapter;
|
||||||
|
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
|
||||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||||
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
|
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||||
|
import de.danoeh.antennapod.core.util.ChapterUtils;
|
||||||
import de.danoeh.antennapod.core.util.Converter;
|
import de.danoeh.antennapod.core.util.Converter;
|
||||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||||
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
|
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
|
||||||
@ -45,7 +48,8 @@ import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
|
|||||||
import de.danoeh.antennapod.dialog.SleepTimerDialog;
|
import de.danoeh.antennapod.dialog.SleepTimerDialog;
|
||||||
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
|
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
|
||||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||||
import de.danoeh.antennapod.view.PlaybackSpeedIndicatorView;
|
import de.danoeh.antennapod.view.ChapterSeekBar;
|
||||||
|
import de.danoeh.antennapod.ui.common.PlaybackSpeedIndicatorView;
|
||||||
import io.reactivex.Maybe;
|
import io.reactivex.Maybe;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
@ -62,14 +66,13 @@ import java.util.List;
|
|||||||
* Shows the audio player.
|
* Shows the audio player.
|
||||||
*/
|
*/
|
||||||
public class AudioPlayerFragment extends Fragment implements
|
public class AudioPlayerFragment extends Fragment implements
|
||||||
SeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
|
ChapterSeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
|
||||||
public static final String TAG = "AudioPlayerFragment";
|
public static final String TAG = "AudioPlayerFragment";
|
||||||
private static final int POS_COVER = 0;
|
private static final int POS_COVER = 0;
|
||||||
private static final int POS_DESCR = 1;
|
private static final int POS_DESCR = 1;
|
||||||
private static final int POS_CHAPTERS = 2;
|
private static final int POS_CHAPTERS = 2;
|
||||||
private static final int NUM_CONTENT_FRAGMENTS = 3;
|
private static final int NUM_CONTENT_FRAGMENTS = 3;
|
||||||
private static final String PREFS = "AudioPlayerFragmentPreferences";
|
public static final String PREFS = "AudioPlayerFragmentPreferences";
|
||||||
private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
|
|
||||||
private static final float EPSILON = 0.001f;
|
private static final float EPSILON = 0.001f;
|
||||||
|
|
||||||
PlaybackSpeedIndicatorView butPlaybackSpeed;
|
PlaybackSpeedIndicatorView butPlaybackSpeed;
|
||||||
@ -77,7 +80,7 @@ public class AudioPlayerFragment extends Fragment implements
|
|||||||
private ViewPager2 pager;
|
private ViewPager2 pager;
|
||||||
private TextView txtvPosition;
|
private TextView txtvPosition;
|
||||||
private TextView txtvLength;
|
private TextView txtvLength;
|
||||||
private SeekBar sbPosition;
|
private ChapterSeekBar sbPosition;
|
||||||
private ImageButton butRev;
|
private ImageButton butRev;
|
||||||
private TextView txtvRev;
|
private TextView txtvRev;
|
||||||
private ImageButton butPlay;
|
private ImageButton butPlay;
|
||||||
@ -86,6 +89,8 @@ public class AudioPlayerFragment extends Fragment implements
|
|||||||
private ImageButton butSkip;
|
private ImageButton butSkip;
|
||||||
private Toolbar toolbar;
|
private Toolbar toolbar;
|
||||||
private ProgressBar progressIndicator;
|
private ProgressBar progressIndicator;
|
||||||
|
private CardView cardViewSeek;
|
||||||
|
private TextView txtvSeek;
|
||||||
|
|
||||||
private PlaybackController controller;
|
private PlaybackController controller;
|
||||||
private Disposable disposable;
|
private Disposable disposable;
|
||||||
@ -122,6 +127,8 @@ public class AudioPlayerFragment extends Fragment implements
|
|||||||
txtvFF = root.findViewById(R.id.txtvFF);
|
txtvFF = root.findViewById(R.id.txtvFF);
|
||||||
butSkip = root.findViewById(R.id.butSkip);
|
butSkip = root.findViewById(R.id.butSkip);
|
||||||
progressIndicator = root.findViewById(R.id.progLoading);
|
progressIndicator = root.findViewById(R.id.progLoading);
|
||||||
|
cardViewSeek = root.findViewById(R.id.cardViewSeek);
|
||||||
|
txtvSeek = root.findViewById(R.id.txtvSeek);
|
||||||
|
|
||||||
setupLengthTextView();
|
setupLengthTextView();
|
||||||
setupControlButtons();
|
setupControlButtons();
|
||||||
@ -168,12 +175,33 @@ public class AudioPlayerFragment extends Fragment implements
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHasChapters(boolean hasChapters) {
|
private void setHasChapters(boolean hasChapters) {
|
||||||
this.hasChapters = hasChapters;
|
this.hasChapters = hasChapters;
|
||||||
tabLayoutMediator.detach();
|
tabLayoutMediator.detach();
|
||||||
tabLayoutMediator.attach();
|
tabLayoutMediator.attach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setChapterDividers(Playable media) {
|
||||||
|
|
||||||
|
if (media == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float[] dividerPos = null;
|
||||||
|
|
||||||
|
if (hasChapters) {
|
||||||
|
List<Chapter> chapters = media.getChapters();
|
||||||
|
dividerPos = new float[chapters.size()];
|
||||||
|
float duration = media.getDuration();
|
||||||
|
|
||||||
|
for (int i = 0; i < chapters.size(); i++) {
|
||||||
|
dividerPos[i] = chapters.get(i).getStart() / duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sbPosition.setDividerPos(dividerPos);
|
||||||
|
}
|
||||||
|
|
||||||
public View getExternalPlayerHolder() {
|
public View getExternalPlayerHolder() {
|
||||||
return getView().findViewById(R.id.playerFragment);
|
return getView().findViewById(R.id.playerFragment);
|
||||||
}
|
}
|
||||||
@ -211,16 +239,25 @@ public class AudioPlayerFragment extends Fragment implements
|
|||||||
IntentUtils.sendLocalBroadcast(getActivity(), PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
|
IntentUtils.sendLocalBroadcast(getActivity(), PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
public void onUnreadItemsUpdate(UnreadItemsUpdateEvent event) {
|
||||||
|
if (controller == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updatePosition(new PlaybackPositionEvent(controller.getPosition(),
|
||||||
|
controller.getDuration()));
|
||||||
|
}
|
||||||
|
|
||||||
private void setupLengthTextView() {
|
private void setupLengthTextView() {
|
||||||
SharedPreferences prefs = getContext().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
showTimeLeft = UserPreferences.shouldShowRemainingTime();
|
||||||
showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
|
|
||||||
txtvLength.setOnClickListener(v -> {
|
txtvLength.setOnClickListener(v -> {
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showTimeLeft = !showTimeLeft;
|
showTimeLeft = !showTimeLeft;
|
||||||
prefs.edit().putBoolean(PREF_SHOW_TIME_LEFT, showTimeLeft).apply();
|
UserPreferences.setShowRemainTimeSetting(showTimeLeft);
|
||||||
updatePosition(new PlaybackPositionEvent(controller.getPosition(), controller.getDuration()));
|
updatePosition(new PlaybackPositionEvent(controller.getPosition(),
|
||||||
|
controller.getDuration()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,26 +322,21 @@ public class AudioPlayerFragment extends Fragment implements
|
|||||||
disposable = Maybe.create(emitter -> {
|
disposable = Maybe.create(emitter -> {
|
||||||
Playable media = controller.getMedia();
|
Playable media = controller.getMedia();
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
|
ChapterUtils.loadChapters(media, getContext());
|
||||||
emitter.onSuccess(media);
|
emitter.onSuccess(media);
|
||||||
} else {
|
} else {
|
||||||
emitter.onComplete();
|
emitter.onComplete();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(media -> updateUi((Playable) media),
|
.subscribe(media -> updateUi((Playable) media),
|
||||||
error -> Log.e(TAG, Log.getStackTraceString(error)),
|
error -> Log.e(TAG, Log.getStackTraceString(error)),
|
||||||
() -> updateUi(null));
|
() -> updateUi(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private PlaybackController newPlaybackController() {
|
private PlaybackController newPlaybackController() {
|
||||||
return new PlaybackController(getActivity()) {
|
return new PlaybackController(getActivity()) {
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setupGUI() {
|
|
||||||
AudioPlayerFragment.this.loadMediaInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBufferStart() {
|
public void onBufferStart() {
|
||||||
progressIndicator.setVisibility(View.VISIBLE);
|
progressIndicator.setVisibility(View.VISIBLE);
|
||||||
@ -352,9 +384,8 @@ public class AudioPlayerFragment extends Fragment implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean loadMediaInfo() {
|
public void loadMediaInfo() {
|
||||||
AudioPlayerFragment.this.loadMediaInfo();
|
AudioPlayerFragment.this.loadMediaInfo();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -383,8 +414,15 @@ public class AudioPlayerFragment extends Fragment implements
|
|||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (media != null && media.getChapters() != null) {
|
||||||
|
setHasChapters(media.getChapters().size() > 0);
|
||||||
|
} else {
|
||||||
|
setHasChapters(false);
|
||||||
|
}
|
||||||
updatePosition(new PlaybackPositionEvent(controller.getPosition(), controller.getDuration()));
|
updatePosition(new PlaybackPositionEvent(controller.getPosition(), controller.getDuration()));
|
||||||
updatePlaybackSpeedButton(media);
|
updatePlaybackSpeedButton(media);
|
||||||
|
setChapterDividers(media);
|
||||||
setupOptionsMenu(media);
|
setupOptionsMenu(media);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,6 +471,7 @@ public class AudioPlayerFragment extends Fragment implements
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
txtvPosition.setText(Converter.getDurationStringLong(currentPosition));
|
txtvPosition.setText(Converter.getDurationStringLong(currentPosition));
|
||||||
|
showTimeLeft = UserPreferences.shouldShowRemainingTime();
|
||||||
if (showTimeLeft) {
|
if (showTimeLeft) {
|
||||||
txtvLength.setText("-" + Converter.getDurationStringLong(remainingTime));
|
txtvLength.setText("-" + Converter.getDurationStringLong(remainingTime));
|
||||||
} else {
|
} else {
|
||||||
@ -454,22 +493,22 @@ public class AudioPlayerFragment extends Fragment implements
|
|||||||
}
|
}
|
||||||
if (fromUser) {
|
if (fromUser) {
|
||||||
float prog = progress / ((float) seekBar.getMax());
|
float prog = progress / ((float) seekBar.getMax());
|
||||||
int duration = controller.getDuration();
|
|
||||||
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
|
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
|
||||||
int position = converter.convert((int) (prog * duration));
|
int position = converter.convert((int) (prog * controller.getDuration()));
|
||||||
txtvPosition.setText(Converter.getDurationStringLong(position));
|
txtvSeek.setText(Converter.getDurationStringLong(position));
|
||||||
|
|
||||||
if (showTimeLeft && prog != 0) {
|
|
||||||
int timeLeft = converter.convert(duration - (int) (prog * duration));
|
|
||||||
String length = "-" + Converter.getDurationStringLong(timeLeft);
|
|
||||||
txtvLength.setText(length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
// interrupt position Observer, restart later
|
// interrupt position Observer, restart later
|
||||||
|
cardViewSeek.setScaleX(.8f);
|
||||||
|
cardViewSeek.setScaleY(.8f);
|
||||||
|
cardViewSeek.animate()
|
||||||
|
.setInterpolator(new FastOutSlowInInterpolator())
|
||||||
|
.alpha(1f).scaleX(1f).scaleY(1f)
|
||||||
|
.setDuration(200)
|
||||||
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -478,6 +517,13 @@ public class AudioPlayerFragment extends Fragment implements
|
|||||||
float prog = seekBar.getProgress() / ((float) seekBar.getMax());
|
float prog = seekBar.getProgress() / ((float) seekBar.getMax());
|
||||||
controller.seekTo((int) (prog * controller.getDuration()));
|
controller.seekTo((int) (prog * controller.getDuration()));
|
||||||
}
|
}
|
||||||
|
cardViewSeek.setScaleX(1f);
|
||||||
|
cardViewSeek.setScaleY(1f);
|
||||||
|
cardViewSeek.animate()
|
||||||
|
.setInterpolator(new FastOutSlowInInterpolator())
|
||||||
|
.alpha(0f).scaleX(.8f).scaleY(.8f)
|
||||||
|
.setDuration(200)
|
||||||
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupOptionsMenu(Playable media) {
|
public void setupOptionsMenu(Playable media) {
|
||||||
|
@ -8,9 +8,9 @@ import android.view.ViewGroup;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
|
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
|
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
|
||||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||||
@ -45,7 +45,8 @@ public class ChaptersFragment extends Fragment {
|
|||||||
RecyclerView recyclerView = root.findViewById(R.id.recyclerView);
|
RecyclerView recyclerView = root.findViewById(R.id.recyclerView);
|
||||||
layoutManager = new LinearLayoutManager(getActivity());
|
layoutManager = new LinearLayoutManager(getActivity());
|
||||||
recyclerView.setLayoutManager(layoutManager);
|
recyclerView.setLayoutManager(layoutManager);
|
||||||
recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
|
recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(),
|
||||||
|
layoutManager.getOrientation()));
|
||||||
|
|
||||||
adapter = new ChaptersListAdapter(getActivity(), pos -> {
|
adapter = new ChaptersListAdapter(getActivity(), pos -> {
|
||||||
if (controller.getStatus() != PlayerStatus.PLAYING) {
|
if (controller.getStatus() != PlayerStatus.PLAYING) {
|
||||||
@ -71,13 +72,7 @@ public class ChaptersFragment extends Fragment {
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
controller = new PlaybackController(getActivity()) {
|
controller = new PlaybackController(getActivity()) {
|
||||||
@Override
|
@Override
|
||||||
public boolean loadMediaInfo() {
|
public void loadMediaInfo() {
|
||||||
ChaptersFragment.this.loadMediaInfo();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setupGUI() {
|
|
||||||
ChaptersFragment.this.loadMediaInfo();
|
ChaptersFragment.this.loadMediaInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +118,7 @@ public class ChaptersFragment extends Fragment {
|
|||||||
disposable = Maybe.create(emitter -> {
|
disposable = Maybe.create(emitter -> {
|
||||||
Playable media = controller.getMedia();
|
Playable media = controller.getMedia();
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
media.loadChapterMarks(getContext());
|
ChapterUtils.loadChapters(media, getContext());
|
||||||
emitter.onSuccess(media);
|
emitter.onSuccess(media);
|
||||||
} else {
|
} else {
|
||||||
emitter.onComplete();
|
emitter.onComplete();
|
||||||
@ -142,7 +137,6 @@ public class ChaptersFragment extends Fragment {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
adapter.setMedia(media);
|
adapter.setMedia(media);
|
||||||
((AudioPlayerFragment) getParentFragment()).setHasChapters(adapter.getItemCount() > 0);
|
|
||||||
int positionOfCurrentChapter = getCurrentChapter(media);
|
int positionOfCurrentChapter = getCurrentChapter(media);
|
||||||
updateChapterSelection(positionOfCurrentChapter);
|
updateChapterSelection(positionOfCurrentChapter);
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,9 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import com.bumptech.glide.load.resource.bitmap.FitCenter;
|
import com.bumptech.glide.load.resource.bitmap.FitCenter;
|
||||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
||||||
@ -25,9 +23,11 @@ import com.bumptech.glide.RequestBuilder;
|
|||||||
import com.bumptech.glide.request.RequestOptions;
|
import com.bumptech.glide.request.RequestOptions;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||||
|
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||||
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
|
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
|
||||||
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
||||||
import de.danoeh.antennapod.core.util.ChapterUtils;
|
import de.danoeh.antennapod.core.util.ChapterUtils;
|
||||||
|
import de.danoeh.antennapod.core.util.DateUtils;
|
||||||
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
|
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
|
||||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||||
@ -35,6 +35,7 @@ import io.reactivex.Maybe;
|
|||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
@ -93,7 +94,12 @@ public class CoverFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void displayMediaInfo(@NonNull Playable media) {
|
private void displayMediaInfo(@NonNull Playable media) {
|
||||||
txtvPodcastTitle.setText(media.getFeedTitle());
|
String pubDateStr = DateUtils.formatAbbrev(getActivity(), ((FeedMedia) media).getPubDate());
|
||||||
|
txtvPodcastTitle.setText(StringUtils.stripToEmpty(media.getFeedTitle())
|
||||||
|
+ "\u00A0"
|
||||||
|
+ "・"
|
||||||
|
+ "\u00A0"
|
||||||
|
+ StringUtils.replace(StringUtils.stripToEmpty(pubDateStr), " ", "\u00A0"));
|
||||||
txtvEpisodeTitle.setText(media.getEpisodeTitle());
|
txtvEpisodeTitle.setText(media.getEpisodeTitle());
|
||||||
displayedChapterIndex = -2; // Force refresh
|
displayedChapterIndex = -2; // Force refresh
|
||||||
displayCoverImage(media.getPosition());
|
displayCoverImage(media.getPosition());
|
||||||
@ -111,13 +117,7 @@ public class CoverFragment extends Fragment {
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
controller = new PlaybackController(getActivity()) {
|
controller = new PlaybackController(getActivity()) {
|
||||||
@Override
|
@Override
|
||||||
public boolean loadMediaInfo() {
|
public void loadMediaInfo() {
|
||||||
CoverFragment.this.loadMediaInfo();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setupGUI() {
|
|
||||||
CoverFragment.this.loadMediaInfo();
|
CoverFragment.this.loadMediaInfo();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -151,23 +151,25 @@ public class CoverFragment extends Fragment {
|
|||||||
if (chapter != displayedChapterIndex) {
|
if (chapter != displayedChapterIndex) {
|
||||||
displayedChapterIndex = chapter;
|
displayedChapterIndex = chapter;
|
||||||
|
|
||||||
|
RequestOptions options = new RequestOptions()
|
||||||
|
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
|
||||||
|
.dontAnimate()
|
||||||
|
.transforms(new FitCenter(),
|
||||||
|
new RoundedCorners((int) (16 * getResources().getDisplayMetrics().density)));
|
||||||
|
|
||||||
RequestBuilder<Drawable> cover = Glide.with(this)
|
RequestBuilder<Drawable> cover = Glide.with(this)
|
||||||
.load(ImageResourceUtils.getImageLocation(media))
|
.load(media.getImageLocation())
|
||||||
.apply(new RequestOptions()
|
.error(Glide.with(this)
|
||||||
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
|
.load(ImageResourceUtils.getFallbackImageLocation(media))
|
||||||
.dontAnimate()
|
.apply(options))
|
||||||
.transforms(new FitCenter(),
|
.apply(options);
|
||||||
new RoundedCorners((int) (16 * getResources().getDisplayMetrics().density))));
|
|
||||||
if (chapter == -1 || TextUtils.isEmpty(media.getChapters().get(chapter).getImageUrl())) {
|
if (chapter == -1 || TextUtils.isEmpty(media.getChapters().get(chapter).getImageUrl())) {
|
||||||
cover.into(imgvCover);
|
cover.into(imgvCover);
|
||||||
} else {
|
} else {
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.load(EmbeddedChapterImage.getModelFor(media, chapter))
|
.load(EmbeddedChapterImage.getModelFor(media, chapter))
|
||||||
.apply(new RequestOptions()
|
.apply(options)
|
||||||
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
|
|
||||||
.dontAnimate()
|
|
||||||
.transforms(new FitCenter(),
|
|
||||||
new RoundedCorners((int) (16 * getResources().getDisplayMetrics().density))))
|
|
||||||
.thumbnail(cover)
|
.thumbnail(cover)
|
||||||
.error(cover)
|
.error(cover)
|
||||||
.into(imgvCover);
|
.into(imgvCover);
|
||||||
@ -208,7 +210,7 @@ public class CoverFragment extends Fragment {
|
|||||||
imgvCover.setLayoutParams(params);
|
imgvCover.setLayoutParams(params);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
double percentageHeight = ratio * 0.8;
|
double percentageHeight = ratio * 0.6;
|
||||||
mainContainer.setOrientation(LinearLayout.HORIZONTAL);
|
mainContainer.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
if (newConfig.screenHeightDp > 0) {
|
if (newConfig.screenHeightDp > 0) {
|
||||||
params.height = (int) (convertDpToPixel(newConfig.screenHeightDp) * percentageHeight);
|
params.height = (int) (convertDpToPixel(newConfig.screenHeightDp) * percentageHeight);
|
||||||
|
@ -28,16 +28,17 @@ public class DownloadsFragment extends PagedToolbarFragment {
|
|||||||
public static final String TAG = "DownloadsFragment";
|
public static final String TAG = "DownloadsFragment";
|
||||||
|
|
||||||
public static final String ARG_SELECTED_TAB = "selected_tab";
|
public static final String ARG_SELECTED_TAB = "selected_tab";
|
||||||
|
private static final String PREF_LAST_TAB_POSITION = "tab_position";
|
||||||
|
private static final String KEY_UP_ARROW = "up_arrow";
|
||||||
|
|
||||||
public static final int POS_RUNNING = 0;
|
public static final int POS_RUNNING = 0;
|
||||||
private static final int POS_COMPLETED = 1;
|
private static final int POS_COMPLETED = 1;
|
||||||
public static final int POS_LOG = 2;
|
public static final int POS_LOG = 2;
|
||||||
private static final int TOTAL_COUNT = 3;
|
private static final int TOTAL_COUNT = 3;
|
||||||
|
|
||||||
private static final String PREF_LAST_TAB_POSITION = "tab_position";
|
|
||||||
|
|
||||||
private ViewPager2 viewPager;
|
private ViewPager2 viewPager;
|
||||||
private TabLayout tabLayout;
|
private TabLayout tabLayout;
|
||||||
|
private boolean displayUpArrow;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
@ -48,7 +49,11 @@ public class DownloadsFragment extends PagedToolbarFragment {
|
|||||||
Toolbar toolbar = root.findViewById(R.id.toolbar);
|
Toolbar toolbar = root.findViewById(R.id.toolbar);
|
||||||
toolbar.setTitle(R.string.downloads_label);
|
toolbar.setTitle(R.string.downloads_label);
|
||||||
toolbar.inflateMenu(R.menu.downloads);
|
toolbar.inflateMenu(R.menu.downloads);
|
||||||
((MainActivity) getActivity()).setupToolbarToggle(toolbar);
|
displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
|
||||||
|
}
|
||||||
|
((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
|
||||||
|
|
||||||
viewPager = root.findViewById(R.id.viewpager);
|
viewPager = root.findViewById(R.id.viewpager);
|
||||||
viewPager.setAdapter(new DownloadsPagerAdapter(this));
|
viewPager.setAdapter(new DownloadsPagerAdapter(this));
|
||||||
@ -81,6 +86,12 @@ public class DownloadsFragment extends PagedToolbarFragment {
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
@ -24,6 +24,7 @@ public class EpisodesFragment extends PagedToolbarFragment {
|
|||||||
|
|
||||||
public static final String TAG = "EpisodesFragment";
|
public static final String TAG = "EpisodesFragment";
|
||||||
private static final String PREF_LAST_TAB_POSITION = "tab_position";
|
private static final String PREF_LAST_TAB_POSITION = "tab_position";
|
||||||
|
private static final String KEY_UP_ARROW = "up_arrow";
|
||||||
|
|
||||||
private static final int POS_NEW_EPISODES = 0;
|
private static final int POS_NEW_EPISODES = 0;
|
||||||
private static final int POS_ALL_EPISODES = 1;
|
private static final int POS_ALL_EPISODES = 1;
|
||||||
@ -31,6 +32,7 @@ public class EpisodesFragment extends PagedToolbarFragment {
|
|||||||
private static final int TOTAL_COUNT = 3;
|
private static final int TOTAL_COUNT = 3;
|
||||||
|
|
||||||
private TabLayout tabLayout;
|
private TabLayout tabLayout;
|
||||||
|
private boolean displayUpArrow;
|
||||||
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -44,7 +46,11 @@ public class EpisodesFragment extends PagedToolbarFragment {
|
|||||||
toolbar.setTitle(R.string.episodes_label);
|
toolbar.setTitle(R.string.episodes_label);
|
||||||
toolbar.inflateMenu(R.menu.episodes);
|
toolbar.inflateMenu(R.menu.episodes);
|
||||||
MenuItemUtils.setupSearchItem(toolbar.getMenu(), (MainActivity) getActivity(), 0, "");
|
MenuItemUtils.setupSearchItem(toolbar.getMenu(), (MainActivity) getActivity(), 0, "");
|
||||||
((MainActivity) getActivity()).setupToolbarToggle(toolbar);
|
displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
|
||||||
|
}
|
||||||
|
((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
|
||||||
|
|
||||||
ViewPager2 viewPager = rootView.findViewById(R.id.viewpager);
|
ViewPager2 viewPager = rootView.findViewById(R.id.viewpager);
|
||||||
viewPager.setAdapter(new EpisodesPagerAdapter(this));
|
viewPager.setAdapter(new EpisodesPagerAdapter(this));
|
||||||
@ -88,6 +94,12 @@ public class EpisodesFragment extends PagedToolbarFragment {
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
static class EpisodesPagerAdapter extends FragmentStateAdapter {
|
static class EpisodesPagerAdapter extends FragmentStateAdapter {
|
||||||
|
|
||||||
EpisodesPagerAdapter(@NonNull Fragment fragment) {
|
EpisodesPagerAdapter(@NonNull Fragment fragment) {
|
||||||
|
@ -383,6 +383,14 @@ public abstract class EpisodesListFragment extends Fragment {
|
|||||||
@NonNull
|
@NonNull
|
||||||
protected abstract List<FeedItem> loadData();
|
protected abstract List<FeedItem> loadData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a new page of data as defined by {@link #page} and {@link #EPISODES_PER_PAGE}.
|
||||||
|
* If the number of items returned is less than {@link #EPISODES_PER_PAGE},
|
||||||
|
* it will be assumed that the underlying data is exhausted
|
||||||
|
* and this method will not be called again.
|
||||||
|
*
|
||||||
|
* @return The items from the next page of data
|
||||||
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
protected abstract List<FeedItem> loadMoreData();
|
protected abstract List<FeedItem> loadMoreData();
|
||||||
}
|
}
|
||||||
|
@ -108,12 +108,7 @@ public class ExternalPlayerFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean loadMediaInfo() {
|
public void loadMediaInfo() {
|
||||||
return ExternalPlayerFragment.this.loadMediaInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setupGUI() {
|
|
||||||
ExternalPlayerFragment.this.loadMediaInfo();
|
ExternalPlayerFragment.this.loadMediaInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,11 +165,11 @@ public class ExternalPlayerFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean loadMediaInfo() {
|
private void loadMediaInfo() {
|
||||||
Log.d(TAG, "Loading media info");
|
Log.d(TAG, "Loading media info");
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
Log.w(TAG, "loadMediaInfo was called while PlaybackController was null!");
|
Log.w(TAG, "loadMediaInfo was called while PlaybackController was null!");
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disposable != null) {
|
if (disposable != null) {
|
||||||
@ -186,7 +181,6 @@ public class ExternalPlayerFragment extends Fragment {
|
|||||||
.subscribe(this::updateUi,
|
.subscribe(this::updateUi,
|
||||||
error -> Log.e(TAG, Log.getStackTraceString(error)),
|
error -> Log.e(TAG, Log.getStackTraceString(error)),
|
||||||
() -> ((MainActivity) getActivity()).setPlayerVisible(false));
|
() -> ((MainActivity) getActivity()).setPlayerVisible(false));
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUi(Playable media) {
|
private void updateUi(Playable media) {
|
||||||
@ -198,14 +192,19 @@ public class ExternalPlayerFragment extends Fragment {
|
|||||||
feedName.setText(media.getFeedTitle());
|
feedName.setText(media.getFeedTitle());
|
||||||
onPositionObserverUpdate();
|
onPositionObserverUpdate();
|
||||||
|
|
||||||
|
RequestOptions options = new RequestOptions()
|
||||||
|
.placeholder(R.color.light_gray)
|
||||||
|
.error(R.color.light_gray)
|
||||||
|
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
|
||||||
|
.fitCenter()
|
||||||
|
.dontAnimate();
|
||||||
|
|
||||||
Glide.with(getActivity())
|
Glide.with(getActivity())
|
||||||
.load(ImageResourceUtils.getImageLocation(media))
|
.load(ImageResourceUtils.getEpisodeListImageLocation(media))
|
||||||
.apply(new RequestOptions()
|
.error(Glide.with(getActivity())
|
||||||
.placeholder(R.color.light_gray)
|
.load(ImageResourceUtils.getFallbackImageLocation(media))
|
||||||
.error(R.color.light_gray)
|
.apply(options))
|
||||||
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
|
.apply(options)
|
||||||
.fitCenter()
|
|
||||||
.dontAnimate())
|
|
||||||
.into(imgvCover);
|
.into(imgvCover);
|
||||||
|
|
||||||
if (controller != null && controller.isPlayingVideoLocally()) {
|
if (controller != null && controller.isPlayingVideoLocally()) {
|
||||||
|
@ -45,7 +45,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
|||||||
import de.danoeh.antennapod.core.storage.StatisticsItem;
|
import de.danoeh.antennapod.core.storage.StatisticsItem;
|
||||||
import de.danoeh.antennapod.core.util.Converter;
|
import de.danoeh.antennapod.core.util.Converter;
|
||||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
|
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
|
||||||
import de.danoeh.antennapod.fragment.preferences.StatisticsFragment;
|
import de.danoeh.antennapod.fragment.preferences.StatisticsFragment;
|
||||||
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
|
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
|
||||||
@ -130,6 +130,8 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
|
|||||||
protected void doTint(Context themedContext) {
|
protected void doTint(Context themedContext) {
|
||||||
toolbar.getMenu().findItem(R.id.visit_website_item)
|
toolbar.getMenu().findItem(R.id.visit_website_item)
|
||||||
.setIcon(ThemeUtils.getDrawableFromAttr(themedContext, R.attr.location_web_site));
|
.setIcon(ThemeUtils.getDrawableFromAttr(themedContext, R.attr.location_web_site));
|
||||||
|
toolbar.getMenu().findItem(R.id.share_parent)
|
||||||
|
.setIcon(ThemeUtils.getDrawableFromAttr(themedContext, R.attr.ic_share));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
iconTintManager.updateTint();
|
iconTintManager.updateTint();
|
||||||
@ -201,7 +203,7 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
|
|||||||
Log.d(TAG, "Author is " + feed.getAuthor());
|
Log.d(TAG, "Author is " + feed.getAuthor());
|
||||||
Log.d(TAG, "URL is " + feed.getDownload_url());
|
Log.d(TAG, "URL is " + feed.getDownload_url());
|
||||||
Glide.with(getContext())
|
Glide.with(getContext())
|
||||||
.load(feed.getImageLocation())
|
.load(feed.getImageUrl())
|
||||||
.apply(new RequestOptions()
|
.apply(new RequestOptions()
|
||||||
.placeholder(R.color.light_gray)
|
.placeholder(R.color.light_gray)
|
||||||
.error(R.color.light_gray)
|
.error(R.color.light_gray)
|
||||||
@ -210,7 +212,7 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
|
|||||||
.dontAnimate())
|
.dontAnimate())
|
||||||
.into(imgvCover);
|
.into(imgvCover);
|
||||||
Glide.with(getContext())
|
Glide.with(getContext())
|
||||||
.load(feed.getImageLocation())
|
.load(feed.getImageUrl())
|
||||||
.apply(new RequestOptions()
|
.apply(new RequestOptions()
|
||||||
.placeholder(R.color.image_readability_tint)
|
.placeholder(R.color.image_readability_tint)
|
||||||
.error(R.color.image_readability_tint)
|
.error(R.color.image_readability_tint)
|
||||||
@ -284,9 +286,13 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void refreshToolbarState() {
|
private void refreshToolbarState() {
|
||||||
|
boolean shareLinkVisible = feed != null && feed.getLink() != null;
|
||||||
|
boolean downloadUrlVisible = feed != null && !feed.isLocalFeed();
|
||||||
|
|
||||||
toolbar.getMenu().findItem(R.id.reconnect_local_folder).setVisible(feed != null && feed.isLocalFeed());
|
toolbar.getMenu().findItem(R.id.reconnect_local_folder).setVisible(feed != null && feed.isLocalFeed());
|
||||||
toolbar.getMenu().findItem(R.id.share_download_url_item).setVisible(feed != null && !feed.isLocalFeed());
|
toolbar.getMenu().findItem(R.id.share_download_url_item).setVisible(downloadUrlVisible);
|
||||||
toolbar.getMenu().findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null);
|
toolbar.getMenu().findItem(R.id.share_link_item).setVisible(shareLinkVisible);
|
||||||
|
toolbar.getMenu().findItem(R.id.share_parent).setVisible(downloadUrlVisible || shareLinkVisible);
|
||||||
toolbar.getMenu().findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null
|
toolbar.getMenu().findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null
|
||||||
&& IntentUtils.isCallable(getContext(), new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
|
&& IntentUtils.isCallable(getContext(), new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ import android.widget.AdapterView;
|
|||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@ -57,8 +56,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
|||||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||||
import de.danoeh.antennapod.core.util.FeedItemPermutors;
|
import de.danoeh.antennapod.core.util.FeedItemPermutors;
|
||||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||||
import de.danoeh.antennapod.core.util.Optional;
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
|
||||||
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
|
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
|
||||||
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
|
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
|
||||||
import de.danoeh.antennapod.dialog.FilterDialog;
|
import de.danoeh.antennapod.dialog.FilterDialog;
|
||||||
@ -89,6 +87,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||||||
Toolbar.OnMenuItemClickListener {
|
Toolbar.OnMenuItemClickListener {
|
||||||
private static final String TAG = "ItemlistFragment";
|
private static final String TAG = "ItemlistFragment";
|
||||||
private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id";
|
private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id";
|
||||||
|
private static final String KEY_UP_ARROW = "up_arrow";
|
||||||
|
|
||||||
private FeedItemListAdapter adapter;
|
private FeedItemListAdapter adapter;
|
||||||
private MoreContentListFooterUtil nextPageLoader;
|
private MoreContentListFooterUtil nextPageLoader;
|
||||||
@ -106,6 +105,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||||||
private View header;
|
private View header;
|
||||||
private Toolbar toolbar;
|
private Toolbar toolbar;
|
||||||
private ToolbarIconTintManager iconTintManager;
|
private ToolbarIconTintManager iconTintManager;
|
||||||
|
private boolean displayUpArrow;
|
||||||
|
|
||||||
private long feedID;
|
private long feedID;
|
||||||
private Feed feed;
|
private Feed feed;
|
||||||
@ -146,7 +146,11 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||||||
toolbar = root.findViewById(R.id.toolbar);
|
toolbar = root.findViewById(R.id.toolbar);
|
||||||
toolbar.inflateMenu(R.menu.feedlist);
|
toolbar.inflateMenu(R.menu.feedlist);
|
||||||
toolbar.setOnMenuItemClickListener(this);
|
toolbar.setOnMenuItemClickListener(this);
|
||||||
((MainActivity) getActivity()).setupToolbarToggle(toolbar);
|
displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
|
||||||
|
}
|
||||||
|
((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
|
||||||
refreshToolbarState();
|
refreshToolbarState();
|
||||||
|
|
||||||
recyclerView = root.findViewById(R.id.recyclerView);
|
recyclerView = root.findViewById(R.id.recyclerView);
|
||||||
@ -231,6 +235,12 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||||||
adapter = null;
|
adapter = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() {
|
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() {
|
||||||
@Override
|
@Override
|
||||||
public boolean isRefreshing() {
|
public boolean isRefreshing() {
|
||||||
@ -451,10 +461,6 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||||||
if (feed.getItemFilter() != null) {
|
if (feed.getItemFilter() != null) {
|
||||||
FeedItemFilter filter = feed.getItemFilter();
|
FeedItemFilter filter = feed.getItemFilter();
|
||||||
if (filter.getValues().length > 0) {
|
if (filter.getValues().length > 0) {
|
||||||
if (feed.hasLastUpdateFailed()) {
|
|
||||||
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) txtvInformation.getLayoutParams();
|
|
||||||
p.addRule(RelativeLayout.BELOW, R.id.txtvFailure);
|
|
||||||
}
|
|
||||||
txtvInformation.setText("{md-info-outline} " + this.getString(R.string.filtered_label));
|
txtvInformation.setText("{md-info-outline} " + this.getString(R.string.filtered_label));
|
||||||
Iconify.addIcons(txtvInformation);
|
Iconify.addIcons(txtvInformation);
|
||||||
txtvInformation.setOnClickListener((l) -> {
|
txtvInformation.setOnClickListener((l) -> {
|
||||||
@ -514,7 +520,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||||||
|
|
||||||
private void loadFeedImage() {
|
private void loadFeedImage() {
|
||||||
Glide.with(getActivity())
|
Glide.with(getActivity())
|
||||||
.load(feed.getImageLocation())
|
.load(feed.getImageUrl())
|
||||||
.apply(new RequestOptions()
|
.apply(new RequestOptions()
|
||||||
.placeholder(R.color.image_readability_tint)
|
.placeholder(R.color.image_readability_tint)
|
||||||
.error(R.color.image_readability_tint)
|
.error(R.color.image_readability_tint)
|
||||||
@ -524,7 +530,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||||||
.into(imgvBackground);
|
.into(imgvBackground);
|
||||||
|
|
||||||
Glide.with(getActivity())
|
Glide.with(getActivity())
|
||||||
.load(feed.getImageLocation())
|
.load(feed.getImageUrl())
|
||||||
.apply(new RequestOptions()
|
.apply(new RequestOptions()
|
||||||
.placeholder(R.color.light_gray)
|
.placeholder(R.color.light_gray)
|
||||||
.error(R.color.light_gray)
|
.error(R.color.light_gray)
|
||||||
@ -542,27 +548,32 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||||||
disposable = Observable.fromCallable(this::loadData)
|
disposable = Observable.fromCallable(this::loadData)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(result -> {
|
.subscribe(
|
||||||
feed = result.orElse(null);
|
result -> {
|
||||||
refreshHeaderView();
|
feed = result;
|
||||||
displayList();
|
refreshHeaderView();
|
||||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
displayList();
|
||||||
|
}, error -> {
|
||||||
|
feed = null;
|
||||||
|
refreshHeaderView();
|
||||||
|
displayList();
|
||||||
|
Log.e(TAG, Log.getStackTraceString(error));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@Nullable
|
||||||
private Optional<Feed> loadData() {
|
private Feed loadData() {
|
||||||
Feed feed = DBReader.getFeed(feedID);
|
Feed feed = DBReader.getFeed(feedID, true);
|
||||||
if (feed != null && feed.getItemFilter() != null) {
|
if (feed == null) {
|
||||||
DBReader.loadAdditionalFeedItemListData(feed.getItems());
|
return null;
|
||||||
FeedItemFilter filter = feed.getItemFilter();
|
|
||||||
feed.setItems(filter.filter(feed.getItems()));
|
|
||||||
}
|
}
|
||||||
if (feed != null && feed.getSortOrder() != null) {
|
DBReader.loadAdditionalFeedItemListData(feed.getItems());
|
||||||
|
if (feed.getSortOrder() != null) {
|
||||||
List<FeedItem> feedItems = feed.getItems();
|
List<FeedItem> feedItems = feed.getItems();
|
||||||
FeedItemPermutors.getPermutor(feed.getSortOrder()).reorder(feedItems);
|
FeedItemPermutors.getPermutor(feed.getSortOrder()).reorder(feedItems);
|
||||||
feed.setItems(feedItems);
|
feed.setItems(feedItems);
|
||||||
}
|
}
|
||||||
return Optional.ofNullable(feed);
|
return feed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class FeedItemListAdapter extends EpisodeItemListAdapter {
|
private static class FeedItemListAdapter extends EpisodeItemListAdapter {
|
||||||
|
@ -164,6 +164,7 @@ public class FeedSettingsFragment extends Fragment {
|
|||||||
setupEpisodeFilterPreference();
|
setupEpisodeFilterPreference();
|
||||||
setupPlaybackSpeedPreference();
|
setupPlaybackSpeedPreference();
|
||||||
setupFeedAutoSkipPreference();
|
setupFeedAutoSkipPreference();
|
||||||
|
setupEpisodeNotificationPreference();
|
||||||
setupTags();
|
setupTags();
|
||||||
|
|
||||||
updateAutoDeleteSummary();
|
updateAutoDeleteSummary();
|
||||||
@ -198,7 +199,7 @@ public class FeedSettingsFragment extends Fragment {
|
|||||||
protected void onConfirmed(int skipIntro, int skipEnding) {
|
protected void onConfirmed(int skipIntro, int skipEnding) {
|
||||||
feedPreferences.setFeedSkipIntro(skipIntro);
|
feedPreferences.setFeedSkipIntro(skipIntro);
|
||||||
feedPreferences.setFeedSkipEnding(skipEnding);
|
feedPreferences.setFeedSkipEnding(skipEnding);
|
||||||
feed.savePreferences();
|
DBWriter.setFeedPreferences(feedPreferences);
|
||||||
EventBus.getDefault().post(
|
EventBus.getDefault().post(
|
||||||
new SkipIntroEndingChangedEvent(feedPreferences.getFeedSkipIntro(),
|
new SkipIntroEndingChangedEvent(feedPreferences.getFeedSkipIntro(),
|
||||||
feedPreferences.getFeedSkipEnding(),
|
feedPreferences.getFeedSkipEnding(),
|
||||||
@ -226,7 +227,7 @@ public class FeedSettingsFragment extends Fragment {
|
|||||||
feedPlaybackSpeedPreference.setEntries(entries);
|
feedPlaybackSpeedPreference.setEntries(entries);
|
||||||
feedPlaybackSpeedPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
feedPlaybackSpeedPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
feedPreferences.setFeedPlaybackSpeed(Float.parseFloat((String) newValue));
|
feedPreferences.setFeedPlaybackSpeed(Float.parseFloat((String) newValue));
|
||||||
feed.savePreferences();
|
DBWriter.setFeedPreferences(feedPreferences);
|
||||||
updatePlaybackSpeedPreference();
|
updatePlaybackSpeedPreference();
|
||||||
EventBus.getDefault().post(
|
EventBus.getDefault().post(
|
||||||
new SpeedPresetChangedEvent(feedPreferences.getFeedPlaybackSpeed(), feed.getId()));
|
new SpeedPresetChangedEvent(feedPreferences.getFeedPlaybackSpeed(), feed.getId()));
|
||||||
@ -240,7 +241,7 @@ public class FeedSettingsFragment extends Fragment {
|
|||||||
@Override
|
@Override
|
||||||
protected void onConfirmed(FeedFilter filter) {
|
protected void onConfirmed(FeedFilter filter) {
|
||||||
feedPreferences.setFilter(filter);
|
feedPreferences.setFilter(filter);
|
||||||
feed.savePreferences();
|
DBWriter.setFeedPreferences(feedPreferences);
|
||||||
}
|
}
|
||||||
}.show();
|
}.show();
|
||||||
return false;
|
return false;
|
||||||
@ -256,7 +257,7 @@ public class FeedSettingsFragment extends Fragment {
|
|||||||
protected void onConfirmed(String username, String password) {
|
protected void onConfirmed(String username, String password) {
|
||||||
feedPreferences.setUsername(username);
|
feedPreferences.setUsername(username);
|
||||||
feedPreferences.setPassword(password);
|
feedPreferences.setPassword(password);
|
||||||
feed.savePreferences();
|
DBWriter.setFeedPreferences(feedPreferences);
|
||||||
}
|
}
|
||||||
}.show();
|
}.show();
|
||||||
return false;
|
return false;
|
||||||
@ -276,7 +277,7 @@ public class FeedSettingsFragment extends Fragment {
|
|||||||
feedPreferences.setAutoDeleteAction(FeedPreferences.AutoDeleteAction.NO);
|
feedPreferences.setAutoDeleteAction(FeedPreferences.AutoDeleteAction.NO);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
feed.savePreferences();
|
DBWriter.setFeedPreferences(feedPreferences);
|
||||||
updateAutoDeleteSummary();
|
updateAutoDeleteSummary();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@ -322,7 +323,7 @@ public class FeedSettingsFragment extends Fragment {
|
|||||||
feedPreferences.setVolumeAdaptionSetting(VolumeAdaptionSetting.HEAVY_REDUCTION);
|
feedPreferences.setVolumeAdaptionSetting(VolumeAdaptionSetting.HEAVY_REDUCTION);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
feed.savePreferences();
|
DBWriter.setFeedPreferences(feedPreferences);
|
||||||
updateVolumeReductionValue();
|
updateVolumeReductionValue();
|
||||||
EventBus.getDefault().post(
|
EventBus.getDefault().post(
|
||||||
new VolumeAdaptionChangedEvent(feedPreferences.getVolumeAdaptionSetting(), feed.getId()));
|
new VolumeAdaptionChangedEvent(feedPreferences.getVolumeAdaptionSetting(), feed.getId()));
|
||||||
@ -353,7 +354,7 @@ public class FeedSettingsFragment extends Fragment {
|
|||||||
pref.setOnPreferenceChangeListener((preference, newValue) -> {
|
pref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
boolean checked = newValue == Boolean.TRUE;
|
boolean checked = newValue == Boolean.TRUE;
|
||||||
feedPreferences.setKeepUpdated(checked);
|
feedPreferences.setKeepUpdated(checked);
|
||||||
feed.savePreferences();
|
DBWriter.setFeedPreferences(feedPreferences);
|
||||||
pref.setChecked(checked);
|
pref.setChecked(checked);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@ -384,7 +385,7 @@ public class FeedSettingsFragment extends Fragment {
|
|||||||
boolean checked = newValue == Boolean.TRUE;
|
boolean checked = newValue == Boolean.TRUE;
|
||||||
|
|
||||||
feedPreferences.setAutoDownload(checked);
|
feedPreferences.setAutoDownload(checked);
|
||||||
feed.savePreferences();
|
DBWriter.setFeedPreferences(feedPreferences);
|
||||||
updateAutoDownloadEnabled();
|
updateAutoDownloadEnabled();
|
||||||
ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(getActivity(), checked);
|
ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(getActivity(), checked);
|
||||||
dialog.createNewDialog().show();
|
dialog.createNewDialog().show();
|
||||||
@ -412,7 +413,7 @@ public class FeedSettingsFragment extends Fragment {
|
|||||||
feedPreferences.getTags().clear();
|
feedPreferences.getTags().clear();
|
||||||
feedPreferences.getTags().addAll(new HashSet<>(Arrays.asList(
|
feedPreferences.getTags().addAll(new HashSet<>(Arrays.asList(
|
||||||
foldersString.split(FeedPreferences.TAG_SEPARATOR))));
|
foldersString.split(FeedPreferences.TAG_SEPARATOR))));
|
||||||
feed.savePreferences();
|
DBWriter.setFeedPreferences(feedPreferences);
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.cancel_label, null)
|
.setNegativeButton(R.string.cancel_label, null)
|
||||||
.show();
|
.show();
|
||||||
@ -420,6 +421,19 @@ public class FeedSettingsFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupEpisodeNotificationPreference() {
|
||||||
|
SwitchPreferenceCompat pref = findPreference("episodeNotification");
|
||||||
|
|
||||||
|
pref.setChecked(feedPreferences.getShowEpisodeNotification());
|
||||||
|
pref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
|
boolean checked = newValue == Boolean.TRUE;
|
||||||
|
feedPreferences.setShowEpisodeNotification(checked);
|
||||||
|
DBWriter.setFeedPreferences(feedPreferences);
|
||||||
|
pref.setChecked(checked);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private class ApplyToEpisodesDialog extends ConfirmationDialog {
|
private class ApplyToEpisodesDialog extends ConfirmationDialog {
|
||||||
private final boolean autoDownload;
|
private final boolean autoDownload;
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
|
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||||
import de.danoeh.antennapod.core.util.playback.Timeline;
|
import de.danoeh.antennapod.core.util.playback.Timeline;
|
||||||
import de.danoeh.antennapod.view.ShownotesWebView;
|
import de.danoeh.antennapod.view.ShownotesWebView;
|
||||||
@ -82,7 +85,15 @@ public class ItemDescriptionFragment extends Fragment {
|
|||||||
webViewLoader.dispose();
|
webViewLoader.dispose();
|
||||||
}
|
}
|
||||||
webViewLoader = Maybe.<String>create(emitter -> {
|
webViewLoader = Maybe.<String>create(emitter -> {
|
||||||
Timeline timeline = new Timeline(getActivity(), controller.getMedia());
|
Playable media = controller.getMedia();
|
||||||
|
if (media instanceof FeedMedia) {
|
||||||
|
FeedMedia feedMedia = ((FeedMedia) media);
|
||||||
|
if (feedMedia.getItem() == null) {
|
||||||
|
feedMedia.setItem(DBReader.getFeedItem(feedMedia.getItemId()));
|
||||||
|
}
|
||||||
|
DBReader.loadDescriptionOfFeedItem(feedMedia.getItem());
|
||||||
|
}
|
||||||
|
Timeline timeline = new Timeline(getActivity(), media.getDescription(), media.getDuration());
|
||||||
emitter.onSuccess(timeline.processShownotes());
|
emitter.onSuccess(timeline.processShownotes());
|
||||||
}).subscribeOn(Schedulers.io())
|
}).subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
@ -140,14 +151,8 @@ public class ItemDescriptionFragment extends Fragment {
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
controller = new PlaybackController(getActivity()) {
|
controller = new PlaybackController(getActivity()) {
|
||||||
@Override
|
@Override
|
||||||
public boolean loadMediaInfo() {
|
public void loadMediaInfo() {
|
||||||
load();
|
load();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setupGUI() {
|
|
||||||
ItemDescriptionFragment.this.load();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
controller.init();
|
controller.init();
|
||||||
|
@ -57,7 +57,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
|
|||||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||||
import de.danoeh.antennapod.core.util.Converter;
|
import de.danoeh.antennapod.core.util.Converter;
|
||||||
import de.danoeh.antennapod.core.util.DateUtils;
|
import de.danoeh.antennapod.core.util.DateUtils;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||||
import de.danoeh.antennapod.core.util.playback.Timeline;
|
import de.danoeh.antennapod.core.util.playback.Timeline;
|
||||||
import de.danoeh.antennapod.view.ShownotesWebView;
|
import de.danoeh.antennapod.view.ShownotesWebView;
|
||||||
@ -238,7 +238,12 @@ public class ItemFragment extends Fragment {
|
|||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
EventBus.getDefault().register(this);
|
EventBus.getDefault().register(this);
|
||||||
controller = new PlaybackController(getActivity());
|
controller = new PlaybackController(getActivity()) {
|
||||||
|
@Override
|
||||||
|
public void loadMediaInfo() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
};
|
||||||
controller.init();
|
controller.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,14 +296,19 @@ public class ItemFragment extends Fragment {
|
|||||||
txtvPublished.setContentDescription(DateUtils.formatForAccessibility(getContext(), item.getPubDate()));
|
txtvPublished.setContentDescription(DateUtils.formatForAccessibility(getContext(), item.getPubDate()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RequestOptions options = new RequestOptions()
|
||||||
|
.error(R.color.light_gray)
|
||||||
|
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
|
||||||
|
.transforms(new FitCenter(),
|
||||||
|
new RoundedCorners((int) (4 * getResources().getDisplayMetrics().density)))
|
||||||
|
.dontAnimate();
|
||||||
|
|
||||||
Glide.with(getActivity())
|
Glide.with(getActivity())
|
||||||
.load(ImageResourceUtils.getImageLocation(item))
|
.load(item.getImageLocation())
|
||||||
.apply(new RequestOptions()
|
.error(Glide.with(getActivity())
|
||||||
.error(R.color.light_gray)
|
.load(ImageResourceUtils.getFallbackImageLocation(item))
|
||||||
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
|
.apply(options))
|
||||||
.transforms(new FitCenter(),
|
.apply(options)
|
||||||
new RoundedCorners((int) (4 * getResources().getDisplayMetrics().density)))
|
|
||||||
.dontAnimate())
|
|
||||||
.into(imgvCover);
|
.into(imgvCover);
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
@ -429,7 +439,9 @@ public class ItemFragment extends Fragment {
|
|||||||
FeedItem feedItem = DBReader.getFeedItem(itemId);
|
FeedItem feedItem = DBReader.getFeedItem(itemId);
|
||||||
Context context = getContext();
|
Context context = getContext();
|
||||||
if (feedItem != null && context != null) {
|
if (feedItem != null && context != null) {
|
||||||
Timeline t = new Timeline(context, feedItem);
|
int duration = feedItem.getMedia() != null ? feedItem.getMedia().getDuration() : Integer.MAX_VALUE;
|
||||||
|
DBReader.loadDescriptionOfFeedItem(feedItem);
|
||||||
|
Timeline t = new Timeline(context, feedItem.getDescription(), duration);
|
||||||
webviewData = t.processShownotes();
|
webviewData = t.processShownotes();
|
||||||
}
|
}
|
||||||
return feedItem;
|
return feedItem;
|
||||||
|
@ -424,7 +424,7 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
|
|||||||
flatItemList = result.second;
|
flatItemList = result.second;
|
||||||
updateSelection(); // Selected item might be a feed
|
updateSelection(); // Selected item might be a feed
|
||||||
navAdapter.notifyDataSetChanged();
|
navAdapter.notifyDataSetChanged();
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE); // Stays hidden once there is something in the list
|
||||||
}, error -> {
|
}, error -> {
|
||||||
Log.e(TAG, Log.getStackTraceString(error));
|
Log.e(TAG, Log.getStackTraceString(error));
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
|
@ -41,6 +41,7 @@ import java.util.List;
|
|||||||
|
|
||||||
public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuItemClickListener {
|
public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuItemClickListener {
|
||||||
public static final String TAG = "PlaybackHistoryFragment";
|
public static final String TAG = "PlaybackHistoryFragment";
|
||||||
|
private static final String KEY_UP_ARROW = "up_arrow";
|
||||||
|
|
||||||
private List<FeedItem> playbackHistory;
|
private List<FeedItem> playbackHistory;
|
||||||
private PlaybackHistoryListAdapter adapter;
|
private PlaybackHistoryListAdapter adapter;
|
||||||
@ -49,6 +50,7 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI
|
|||||||
private EmptyViewHandler emptyView;
|
private EmptyViewHandler emptyView;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
private Toolbar toolbar;
|
private Toolbar toolbar;
|
||||||
|
private boolean displayUpArrow;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@ -63,7 +65,11 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI
|
|||||||
toolbar = root.findViewById(R.id.toolbar);
|
toolbar = root.findViewById(R.id.toolbar);
|
||||||
toolbar.setTitle(R.string.playback_history_label);
|
toolbar.setTitle(R.string.playback_history_label);
|
||||||
toolbar.setOnMenuItemClickListener(this);
|
toolbar.setOnMenuItemClickListener(this);
|
||||||
((MainActivity) getActivity()).setupToolbarToggle(toolbar);
|
displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
|
||||||
|
}
|
||||||
|
((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
|
||||||
toolbar.inflateMenu(R.menu.playback_history);
|
toolbar.inflateMenu(R.menu.playback_history);
|
||||||
refreshToolbarState();
|
refreshToolbarState();
|
||||||
|
|
||||||
@ -98,6 +104,12 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
public void onEventMainThread(FeedItemEvent event) {
|
public void onEventMainThread(FeedItemEvent event) {
|
||||||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||||
|
@ -12,6 +12,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
@ -67,6 +68,7 @@ import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REM
|
|||||||
*/
|
*/
|
||||||
public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickListener {
|
public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickListener {
|
||||||
public static final String TAG = "QueueFragment";
|
public static final String TAG = "QueueFragment";
|
||||||
|
private static final String KEY_UP_ARROW = "up_arrow";
|
||||||
|
|
||||||
private TextView infoBar;
|
private TextView infoBar;
|
||||||
private EpisodeItemListRecyclerView recyclerView;
|
private EpisodeItemListRecyclerView recyclerView;
|
||||||
@ -74,6 +76,7 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
|
|||||||
private EmptyViewHandler emptyView;
|
private EmptyViewHandler emptyView;
|
||||||
private ProgressBar progLoading;
|
private ProgressBar progLoading;
|
||||||
private Toolbar toolbar;
|
private Toolbar toolbar;
|
||||||
|
private boolean displayUpArrow;
|
||||||
|
|
||||||
private List<FeedItem> queue;
|
private List<FeedItem> queue;
|
||||||
|
|
||||||
@ -420,7 +423,11 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
|
|||||||
View root = inflater.inflate(R.layout.queue_fragment, container, false);
|
View root = inflater.inflate(R.layout.queue_fragment, container, false);
|
||||||
toolbar = root.findViewById(R.id.toolbar);
|
toolbar = root.findViewById(R.id.toolbar);
|
||||||
toolbar.setOnMenuItemClickListener(this);
|
toolbar.setOnMenuItemClickListener(this);
|
||||||
((MainActivity) getActivity()).setupToolbarToggle(toolbar);
|
displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
|
||||||
|
}
|
||||||
|
((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
|
||||||
toolbar.inflateMenu(R.menu.queue);
|
toolbar.inflateMenu(R.menu.queue);
|
||||||
MenuItemUtils.setupSearchItem(toolbar.getMenu(), (MainActivity) getActivity(), 0, "");
|
MenuItemUtils.setupSearchItem(toolbar.getMenu(), (MainActivity) getActivity(), 0, "");
|
||||||
refreshToolbarState();
|
refreshToolbarState();
|
||||||
@ -530,6 +537,12 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
private void onFragmentLoaded(final boolean restoreScrollPosition) {
|
private void onFragmentLoaded(final boolean restoreScrollPosition) {
|
||||||
if (queue != null && queue.size() > 0) {
|
if (queue != null && queue.size() > 0) {
|
||||||
if (recyclerAdapter == null) {
|
if (recyclerAdapter == null) {
|
||||||
|
@ -8,6 +8,7 @@ import android.os.Bundle;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
@ -67,6 +68,8 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
|
|||||||
public static final String TAG = "SubscriptionFragment";
|
public static final String TAG = "SubscriptionFragment";
|
||||||
private static final String PREFS = "SubscriptionFragment";
|
private static final String PREFS = "SubscriptionFragment";
|
||||||
private static final String PREF_NUM_COLUMNS = "columns";
|
private static final String PREF_NUM_COLUMNS = "columns";
|
||||||
|
private static final String KEY_UP_ARROW = "up_arrow";
|
||||||
|
|
||||||
private static final int MIN_NUM_COLUMNS = 2;
|
private static final int MIN_NUM_COLUMNS = 2;
|
||||||
private static final int[] COLUMN_CHECKBOX_IDS = {
|
private static final int[] COLUMN_CHECKBOX_IDS = {
|
||||||
R.id.subscription_num_columns_2,
|
R.id.subscription_num_columns_2,
|
||||||
@ -85,6 +88,7 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
|
|||||||
|
|
||||||
private int mPosition = -1;
|
private int mPosition = -1;
|
||||||
private boolean isUpdatingFeeds = false;
|
private boolean isUpdatingFeeds = false;
|
||||||
|
private boolean displayUpArrow;
|
||||||
|
|
||||||
private Disposable disposable;
|
private Disposable disposable;
|
||||||
private SharedPreferences prefs;
|
private SharedPreferences prefs;
|
||||||
@ -103,7 +107,11 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
|
|||||||
View root = inflater.inflate(R.layout.fragment_subscriptions, container, false);
|
View root = inflater.inflate(R.layout.fragment_subscriptions, container, false);
|
||||||
toolbar = root.findViewById(R.id.toolbar);
|
toolbar = root.findViewById(R.id.toolbar);
|
||||||
toolbar.setOnMenuItemClickListener(this);
|
toolbar.setOnMenuItemClickListener(this);
|
||||||
((MainActivity) getActivity()).setupToolbarToggle(toolbar);
|
displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
|
||||||
|
}
|
||||||
|
((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
|
||||||
toolbar.inflateMenu(R.menu.subscriptions);
|
toolbar.inflateMenu(R.menu.subscriptions);
|
||||||
for (int i = 0; i < COLUMN_CHECKBOX_IDS.length; i++) {
|
for (int i = 0; i < COLUMN_CHECKBOX_IDS.length; i++) {
|
||||||
// Do this in Java to localize numbers
|
// Do this in Java to localize numbers
|
||||||
@ -130,6 +138,12 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
private void refreshToolbarState() {
|
private void refreshToolbarState() {
|
||||||
int columns = prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns());
|
int columns = prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns());
|
||||||
toolbar.getMenu().findItem(COLUMN_CHECKBOX_IDS[columns - MIN_NUM_COLUMNS]).setChecked(true);
|
toolbar.getMenu().findItem(COLUMN_CHECKBOX_IDS[columns - MIN_NUM_COLUMNS]).setChecked(true);
|
||||||
@ -218,16 +232,19 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
|
|||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
}
|
}
|
||||||
emptyView.hide();
|
emptyView.hide();
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
|
||||||
disposable = Observable.fromCallable(DBReader::getNavDrawerData)
|
disposable = Observable.fromCallable(DBReader::getNavDrawerData)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(result -> {
|
.subscribe(
|
||||||
navDrawerData = result;
|
result -> {
|
||||||
subscriptionAdapter.notifyDataSetChanged();
|
navDrawerData = result;
|
||||||
emptyView.updateVisibility();
|
subscriptionAdapter.notifyDataSetChanged();
|
||||||
progressBar.setVisibility(View.GONE);
|
emptyView.updateVisibility();
|
||||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
progressBar.setVisibility(View.GONE); // Keep hidden to avoid flickering while refreshing
|
||||||
|
}, error -> {
|
||||||
|
Log.e(TAG, Log.getStackTraceString(error));
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
});
|
||||||
|
|
||||||
if (UserPreferences.getSubscriptionsFilter().isEnabled()) {
|
if (UserPreferences.getSubscriptionsFilter().isEnabled()) {
|
||||||
feedsFilteredMsg.setText("{md-info-outline} " + getString(R.string.subscriptions_are_filtered));
|
feedsFilteredMsg.setText("{md-info-outline} " + getString(R.string.subscriptions_are_filtered));
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package de.danoeh.antennapod.fragment.gpodnet;
|
package de.danoeh.antennapod.fragment.gpodnet;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -13,9 +10,7 @@ import android.widget.Button;
|
|||||||
import android.widget.GridView;
|
import android.widget.GridView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
|
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
|
||||||
@ -25,6 +20,12 @@ import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
|||||||
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
|
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
|
||||||
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
|
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
|
||||||
import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetPodcast;
|
import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetPodcast;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a list of GPodnetPodcast-Objects in a GridView
|
* Displays a list of GPodnetPodcast-Objects in a GridView
|
||||||
@ -36,6 +37,7 @@ public abstract class PodcastListFragment extends Fragment {
|
|||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
private TextView txtvError;
|
private TextView txtvError;
|
||||||
private Button butRetry;
|
private Button butRetry;
|
||||||
|
private Disposable disposable;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
@ -64,60 +66,44 @@ public abstract class PodcastListFragment extends Fragment {
|
|||||||
protected abstract List<GpodnetPodcast> loadPodcastData(GpodnetService service) throws GpodnetServiceException;
|
protected abstract List<GpodnetPodcast> loadPodcastData(GpodnetService service) throws GpodnetServiceException;
|
||||||
|
|
||||||
final void loadData() {
|
final void loadData() {
|
||||||
AsyncTask<Void, Void, List<GpodnetPodcast>> loaderTask = new AsyncTask<Void, Void, List<GpodnetPodcast>>() {
|
if (disposable != null) {
|
||||||
volatile Exception exception = null;
|
disposable.dispose();
|
||||||
|
}
|
||||||
@Override
|
gridView.setVisibility(View.GONE);
|
||||||
protected List<GpodnetPodcast> doInBackground(Void... params) {
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
try {
|
txtvError.setVisibility(View.GONE);
|
||||||
|
butRetry.setVisibility(View.GONE);
|
||||||
|
disposable = Observable.fromCallable(
|
||||||
|
() -> {
|
||||||
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
|
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
|
||||||
GpodnetPreferences.getHostname());
|
GpodnetPreferences.getHosturl());
|
||||||
return loadPodcastData(service);
|
return loadPodcastData(service);
|
||||||
} catch (GpodnetServiceException e) {
|
})
|
||||||
exception = e;
|
.subscribeOn(Schedulers.io())
|
||||||
e.printStackTrace();
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
return null;
|
.subscribe(
|
||||||
}
|
podcasts -> {
|
||||||
}
|
progressBar.setVisibility(View.GONE);
|
||||||
|
butRetry.setVisibility(View.GONE);
|
||||||
|
|
||||||
@Override
|
if (podcasts.size() > 0) {
|
||||||
protected void onPostExecute(List<GpodnetPodcast> gpodnetPodcasts) {
|
PodcastListAdapter listAdapter = new PodcastListAdapter(getContext(), 0, podcasts);
|
||||||
super.onPostExecute(gpodnetPodcasts);
|
gridView.setAdapter(listAdapter);
|
||||||
final Context context = getActivity();
|
listAdapter.notifyDataSetChanged();
|
||||||
if (context != null && gpodnetPodcasts != null && gpodnetPodcasts.size() > 0) {
|
gridView.setVisibility(View.VISIBLE);
|
||||||
PodcastListAdapter listAdapter = new PodcastListAdapter(context, 0, gpodnetPodcasts);
|
txtvError.setVisibility(View.GONE);
|
||||||
gridView.setAdapter(listAdapter);
|
} else {
|
||||||
listAdapter.notifyDataSetChanged();
|
gridView.setVisibility(View.GONE);
|
||||||
|
txtvError.setText(getString(R.string.search_status_no_results));
|
||||||
progressBar.setVisibility(View.GONE);
|
txtvError.setVisibility(View.VISIBLE);
|
||||||
gridView.setVisibility(View.VISIBLE);
|
}
|
||||||
txtvError.setVisibility(View.GONE);
|
}, error -> {
|
||||||
butRetry.setVisibility(View.GONE);
|
gridView.setVisibility(View.GONE);
|
||||||
} else if (context != null && gpodnetPodcasts != null) {
|
progressBar.setVisibility(View.GONE);
|
||||||
gridView.setVisibility(View.GONE);
|
txtvError.setText(getString(R.string.error_msg_prefix) + error.getMessage());
|
||||||
progressBar.setVisibility(View.GONE);
|
txtvError.setVisibility(View.VISIBLE);
|
||||||
txtvError.setText(getString(R.string.search_status_no_results));
|
butRetry.setVisibility(View.VISIBLE);
|
||||||
txtvError.setVisibility(View.VISIBLE);
|
Log.e(TAG, Log.getStackTraceString(error));
|
||||||
butRetry.setVisibility(View.GONE);
|
});
|
||||||
} else if (context != null) {
|
|
||||||
gridView.setVisibility(View.GONE);
|
|
||||||
progressBar.setVisibility(View.GONE);
|
|
||||||
txtvError.setText(getString(R.string.error_msg_prefix) + exception.getMessage());
|
|
||||||
txtvError.setVisibility(View.VISIBLE);
|
|
||||||
butRetry.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
gridView.setVisibility(View.GONE);
|
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
|
||||||
txtvError.setVisibility(View.GONE);
|
|
||||||
butRetry.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,34 @@
|
|||||||
package de.danoeh.antennapod.fragment.gpodnet;
|
package de.danoeh.antennapod.fragment.gpodnet;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.ListFragment;
|
import androidx.fragment.app.ListFragment;
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.adapter.gpodnet.TagListAdapter;
|
import de.danoeh.antennapod.adapter.gpodnet.TagListAdapter;
|
||||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||||
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
||||||
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
|
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
|
||||||
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
|
|
||||||
import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetTag;
|
import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetTag;
|
||||||
|
import io.reactivex.Observable;
|
||||||
import java.util.List;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
public class TagListFragment extends ListFragment {
|
public class TagListFragment extends ListFragment {
|
||||||
private static final int COUNT = 50;
|
private static final int COUNT = 50;
|
||||||
|
private static final String TAG = "TagListFragment";
|
||||||
|
private Disposable disposable;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
getListView().setOnItemClickListener((parent, view1, position, id) -> {
|
getListView().setOnItemClickListener((parent, view1, position, id) -> {
|
||||||
GpodnetTag tag = (GpodnetTag) getListAdapter().getItem(position);
|
GpodnetTag tag = (GpodnetTag) getListAdapter().getItem(position);
|
||||||
MainActivity activity = (MainActivity) getActivity();
|
((MainActivity) getActivity()).loadChildFragment(TagFragment.newInstance(tag));
|
||||||
activity.loadChildFragment(TagFragment.newInstance(tag));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
startLoadTask();
|
startLoadTask();
|
||||||
@ -35,59 +37,36 @@ public class TagListFragment extends ListFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
cancelLoadTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
private AsyncTask<Void, Void, List<GpodnetTag>> loadTask;
|
if (disposable != null) {
|
||||||
|
disposable.dispose();
|
||||||
private void cancelLoadTask() {
|
|
||||||
if (loadTask != null && !loadTask.isCancelled()) {
|
|
||||||
loadTask.cancel(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLoadTask() {
|
private void startLoadTask() {
|
||||||
cancelLoadTask();
|
if (disposable != null) {
|
||||||
loadTask = new AsyncTask<Void, Void, List<GpodnetTag>>() {
|
disposable.dispose();
|
||||||
private Exception exception;
|
}
|
||||||
|
setListShown(false);
|
||||||
@Override
|
disposable = Observable.fromCallable(
|
||||||
protected List<GpodnetTag> doInBackground(Void... params) {
|
() -> {
|
||||||
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
|
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
|
||||||
GpodnetPreferences.getHostname());
|
GpodnetPreferences.getHosturl());
|
||||||
try {
|
return service.getTopTags(COUNT);
|
||||||
return service.getTopTags(COUNT);
|
})
|
||||||
} catch (GpodnetServiceException e) {
|
.subscribeOn(Schedulers.io())
|
||||||
e.printStackTrace();
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
exception = e;
|
.subscribe(
|
||||||
return null;
|
tags -> {
|
||||||
}
|
setListAdapter(new TagListAdapter(getContext(), android.R.layout.simple_list_item_1, tags));
|
||||||
}
|
setListShown(true);
|
||||||
|
}, error -> {
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
setListShown(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(List<GpodnetTag> gpodnetTags) {
|
|
||||||
super.onPostExecute(gpodnetTags);
|
|
||||||
final Context context = getActivity();
|
|
||||||
if (context != null) {
|
|
||||||
if (gpodnetTags != null) {
|
|
||||||
setListAdapter(new TagListAdapter(context, android.R.layout.simple_list_item_1, gpodnetTags));
|
|
||||||
} else if (exception != null) {
|
|
||||||
TextView txtvError = new TextView(getActivity());
|
TextView txtvError = new TextView(getActivity());
|
||||||
txtvError.setText(exception.getMessage());
|
txtvError.setText(error.getMessage());
|
||||||
getListView().setEmptyView(txtvError);
|
getListView().setEmptyView(txtvError);
|
||||||
}
|
setListShown(true);
|
||||||
setListShown(true);
|
Log.e(TAG, Log.getStackTraceString(error));
|
||||||
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
loadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,9 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
String[] entries = new String[values.length];
|
String[] entries = new String[values.length];
|
||||||
for (int x = 0; x < values.length; x++) {
|
for (int x = 0; x < values.length; x++) {
|
||||||
int v = Integer.parseInt(values[x]);
|
int v = Integer.parseInt(values[x]);
|
||||||
if (v == UserPreferences.EPISODE_CLEANUP_QUEUE) {
|
if (v == UserPreferences.EPISODE_CLEANUP_EXCEPT_FAVORITE) {
|
||||||
|
entries[x] = res.getString(R.string.episode_cleanup_except_favorite_removal);
|
||||||
|
} else if (v == UserPreferences.EPISODE_CLEANUP_QUEUE) {
|
||||||
entries[x] = res.getString(R.string.episode_cleanup_queue_removal);
|
entries[x] = res.getString(R.string.episode_cleanup_queue_removal);
|
||||||
} else if (v == UserPreferences.EPISODE_CLEANUP_NULL){
|
} else if (v == UserPreferences.EPISODE_CLEANUP_NULL){
|
||||||
entries[x] = res.getString(R.string.episode_cleanup_never);
|
entries[x] = res.getString(R.string.episode_cleanup_never);
|
||||||
|
@ -0,0 +1,307 @@
|
|||||||
|
package de.danoeh.antennapod.fragment.preferences;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.RadioGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.ViewFlipper;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||||
|
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
||||||
|
import de.danoeh.antennapod.core.sync.SyncService;
|
||||||
|
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
|
||||||
|
import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetDevice;
|
||||||
|
import de.danoeh.antennapod.core.util.FileNameGenerator;
|
||||||
|
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||||
|
import io.reactivex.Completable;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guides the user through the authentication process.
|
||||||
|
*/
|
||||||
|
public class GpodderAuthenticationFragment extends DialogFragment {
|
||||||
|
public static final String TAG = "GpodnetAuthActivity";
|
||||||
|
|
||||||
|
private ViewFlipper viewFlipper;
|
||||||
|
|
||||||
|
private static final int STEP_DEFAULT = -1;
|
||||||
|
private static final int STEP_HOSTNAME = 0;
|
||||||
|
private static final int STEP_LOGIN = 1;
|
||||||
|
private static final int STEP_DEVICE = 2;
|
||||||
|
private static final int STEP_FINISH = 3;
|
||||||
|
|
||||||
|
private int currentStep = -1;
|
||||||
|
|
||||||
|
private GpodnetService service;
|
||||||
|
private volatile String username;
|
||||||
|
private volatile String password;
|
||||||
|
private volatile GpodnetDevice selectedDevice;
|
||||||
|
private List<GpodnetDevice> devices;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||||
|
AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
|
||||||
|
dialog.setTitle(GpodnetService.DEFAULT_BASE_HOST);
|
||||||
|
dialog.setNegativeButton(R.string.cancel_label, null);
|
||||||
|
dialog.setCancelable(false);
|
||||||
|
this.setCancelable(false);
|
||||||
|
|
||||||
|
View root = View.inflate(getContext(), R.layout.gpodnetauth_dialog, null);
|
||||||
|
viewFlipper = root.findViewById(R.id.viewflipper);
|
||||||
|
advance();
|
||||||
|
dialog.setView(root);
|
||||||
|
|
||||||
|
return dialog.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupHostView(View view) {
|
||||||
|
final Button selectHost = view.findViewById(R.id.chooseHostButton);
|
||||||
|
final RadioGroup serverRadioGroup = view.findViewById(R.id.serverRadioGroup);
|
||||||
|
final EditText serverUrlText = view.findViewById(R.id.serverUrlText);
|
||||||
|
|
||||||
|
if (!GpodnetService.DEFAULT_BASE_HOST.equals(GpodnetPreferences.getHosturl())) {
|
||||||
|
serverUrlText.setText(GpodnetPreferences.getHosturl());
|
||||||
|
}
|
||||||
|
final TextInputLayout serverUrlTextInput = view.findViewById(R.id.serverUrlTextInput);
|
||||||
|
serverRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
|
||||||
|
serverUrlTextInput.setVisibility(checkedId == R.id.customServerRadio ? View.VISIBLE : View.GONE);
|
||||||
|
});
|
||||||
|
selectHost.setOnClickListener(v -> {
|
||||||
|
if (serverRadioGroup.getCheckedRadioButtonId() == R.id.customServerRadio) {
|
||||||
|
GpodnetPreferences.setHosturl(serverUrlText.getText().toString());
|
||||||
|
} else {
|
||||||
|
GpodnetPreferences.setHosturl(GpodnetService.DEFAULT_BASE_HOST);
|
||||||
|
}
|
||||||
|
service = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetPreferences.getHosturl());
|
||||||
|
getDialog().setTitle(GpodnetPreferences.getHosturl());
|
||||||
|
advance();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupLoginView(View view) {
|
||||||
|
final EditText username = view.findViewById(R.id.etxtUsername);
|
||||||
|
final EditText password = view.findViewById(R.id.etxtPassword);
|
||||||
|
final Button login = view.findViewById(R.id.butLogin);
|
||||||
|
final TextView txtvError = view.findViewById(R.id.credentialsError);
|
||||||
|
final ProgressBar progressBar = view.findViewById(R.id.progBarLogin);
|
||||||
|
final TextView createAccount = view.findViewById(R.id.createAccountButton);
|
||||||
|
final TextView createAccountWarning = view.findViewById(R.id.createAccountWarning);
|
||||||
|
|
||||||
|
createAccount.setPaintFlags(createAccount.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
||||||
|
createAccount.setOnClickListener(v -> IntentUtils.openInBrowser(getContext(), "https://gpodder.net/register/"));
|
||||||
|
|
||||||
|
if (GpodnetPreferences.getHosturl().startsWith("http://")) {
|
||||||
|
createAccountWarning.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
password.setOnEditorActionListener((v, actionID, event) ->
|
||||||
|
actionID == EditorInfo.IME_ACTION_GO && login.performClick());
|
||||||
|
|
||||||
|
login.setOnClickListener(v -> {
|
||||||
|
final String usernameStr = username.getText().toString();
|
||||||
|
final String passwordStr = password.getText().toString();
|
||||||
|
|
||||||
|
if (usernameHasUnwantedChars(usernameStr)) {
|
||||||
|
txtvError.setText(R.string.gpodnetsync_username_characters_error);
|
||||||
|
txtvError.setVisibility(View.VISIBLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
login.setEnabled(false);
|
||||||
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
txtvError.setVisibility(View.GONE);
|
||||||
|
InputMethodManager inputManager = (InputMethodManager) getContext()
|
||||||
|
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
inputManager.hideSoftInputFromWindow(login.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
|
||||||
|
|
||||||
|
Completable.fromAction(() -> {
|
||||||
|
service.authenticate(usernameStr, passwordStr);
|
||||||
|
devices = service.getDevices();
|
||||||
|
GpodderAuthenticationFragment.this.username = usernameStr;
|
||||||
|
GpodderAuthenticationFragment.this.password = passwordStr;
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(() -> {
|
||||||
|
login.setEnabled(true);
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
advance();
|
||||||
|
}, error -> {
|
||||||
|
login.setEnabled(true);
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
txtvError.setText(error.getCause().getMessage());
|
||||||
|
txtvError.setVisibility(View.VISIBLE);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupDeviceView(View view) {
|
||||||
|
final EditText deviceName = view.findViewById(R.id.deviceName);
|
||||||
|
final LinearLayout devicesContainer = view.findViewById(R.id.devicesContainer);
|
||||||
|
deviceName.setText(generateDeviceName());
|
||||||
|
|
||||||
|
MaterialButton createDeviceButton = view.findViewById(R.id.createDeviceButton);
|
||||||
|
createDeviceButton.setOnClickListener(v -> createDevice(view));
|
||||||
|
|
||||||
|
for (GpodnetDevice device : devices) {
|
||||||
|
View row = View.inflate(getContext(), R.layout.gpodnetauth_device_row, null);
|
||||||
|
Button selectDeviceButton = row.findViewById(R.id.selectDeviceButton);
|
||||||
|
selectDeviceButton.setOnClickListener(v -> {
|
||||||
|
selectedDevice = device;
|
||||||
|
advance();
|
||||||
|
});
|
||||||
|
selectDeviceButton.setText(device.getCaption());
|
||||||
|
devicesContainer.addView(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createDevice(View view) {
|
||||||
|
final EditText deviceName = view.findViewById(R.id.deviceName);
|
||||||
|
final TextView txtvError = view.findViewById(R.id.deviceSelectError);
|
||||||
|
final ProgressBar progBarCreateDevice = view.findViewById(R.id.progbarCreateDevice);
|
||||||
|
|
||||||
|
String deviceNameStr = deviceName.getText().toString();
|
||||||
|
if (isDeviceInList(deviceNameStr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progBarCreateDevice.setVisibility(View.VISIBLE);
|
||||||
|
txtvError.setVisibility(View.GONE);
|
||||||
|
deviceName.setEnabled(false);
|
||||||
|
|
||||||
|
Observable.fromCallable(() -> {
|
||||||
|
String deviceId = generateDeviceId(deviceNameStr);
|
||||||
|
service.configureDevice(deviceId, deviceNameStr, GpodnetDevice.DeviceType.MOBILE);
|
||||||
|
return new GpodnetDevice(deviceId, deviceNameStr, GpodnetDevice.DeviceType.MOBILE.toString(), 0);
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(device -> {
|
||||||
|
progBarCreateDevice.setVisibility(View.GONE);
|
||||||
|
selectedDevice = device;
|
||||||
|
advance();
|
||||||
|
}, error -> {
|
||||||
|
deviceName.setEnabled(true);
|
||||||
|
progBarCreateDevice.setVisibility(View.GONE);
|
||||||
|
txtvError.setText(error.getMessage());
|
||||||
|
txtvError.setVisibility(View.VISIBLE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateDeviceName() {
|
||||||
|
String baseName = getString(R.string.gpodnetauth_device_name_default, Build.MODEL);
|
||||||
|
String name = baseName;
|
||||||
|
int num = 1;
|
||||||
|
while (isDeviceInList(name)) {
|
||||||
|
name = baseName + " (" + num + ")";
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateDeviceId(String name) {
|
||||||
|
// devices names must be of a certain form:
|
||||||
|
// https://gpoddernet.readthedocs.org/en/latest/api/reference/general.html#devices
|
||||||
|
return FileNameGenerator.generateFileName(name).replaceAll("\\W", "_").toLowerCase(Locale.US);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDeviceInList(String name) {
|
||||||
|
if (devices == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String id = generateDeviceId(name);
|
||||||
|
for (GpodnetDevice device : devices) {
|
||||||
|
if (device.getId().equals(id) || device.getCaption().equals(name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GpodnetDevice findDevice(String id) {
|
||||||
|
if (devices == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (GpodnetDevice device : devices) {
|
||||||
|
if (device.getId().equals(id)) {
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupFinishView(View view) {
|
||||||
|
final Button sync = view.findViewById(R.id.butSyncNow);
|
||||||
|
|
||||||
|
sync.setOnClickListener(v -> {
|
||||||
|
dismiss();
|
||||||
|
SyncService.sync(getContext());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeLoginCredentials() {
|
||||||
|
GpodnetPreferences.setUsername(username);
|
||||||
|
GpodnetPreferences.setPassword(password);
|
||||||
|
GpodnetPreferences.setDeviceID(selectedDevice.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void advance() {
|
||||||
|
if (currentStep < STEP_FINISH) {
|
||||||
|
|
||||||
|
View view = viewFlipper.getChildAt(currentStep + 1);
|
||||||
|
if (currentStep == STEP_DEFAULT) {
|
||||||
|
setupHostView(view);
|
||||||
|
} else if (currentStep == STEP_HOSTNAME) {
|
||||||
|
setupLoginView(view);
|
||||||
|
} else if (currentStep == STEP_LOGIN) {
|
||||||
|
if (username == null || password == null) {
|
||||||
|
throw new IllegalStateException("Username and password must not be null here");
|
||||||
|
} else {
|
||||||
|
setupDeviceView(view);
|
||||||
|
}
|
||||||
|
} else if (currentStep == STEP_DEVICE) {
|
||||||
|
if (selectedDevice == null) {
|
||||||
|
throw new IllegalStateException("Device must not be null here");
|
||||||
|
} else {
|
||||||
|
writeLoginCredentials();
|
||||||
|
setupFinishView(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentStep != STEP_DEFAULT) {
|
||||||
|
viewFlipper.showNext();
|
||||||
|
}
|
||||||
|
currentStep++;
|
||||||
|
} else {
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean usernameHasUnwantedChars(String username) {
|
||||||
|
Pattern special = Pattern.compile("[!@#$%&*()+=|<>?{}\\[\\]~]");
|
||||||
|
Matcher containsUnwantedChars = special.matcher(username);
|
||||||
|
return containsUnwantedChars.find();
|
||||||
|
}
|
||||||
|
}
|
@ -14,19 +14,16 @@ import de.danoeh.antennapod.core.event.SyncServiceEvent;
|
|||||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||||
import de.danoeh.antennapod.core.sync.SyncService;
|
import de.danoeh.antennapod.core.sync.SyncService;
|
||||||
import de.danoeh.antennapod.dialog.AuthenticationDialog;
|
import de.danoeh.antennapod.dialog.AuthenticationDialog;
|
||||||
import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
|
|
||||||
|
|
||||||
public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
||||||
private static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate";
|
private static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate";
|
||||||
private static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information";
|
private static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information";
|
||||||
private static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync";
|
private static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync";
|
||||||
private static final String PREF_GPODNET_FORCE_FULL_SYNC = "pref_gpodnet_force_full_sync";
|
private static final String PREF_GPODNET_FORCE_FULL_SYNC = "pref_gpodnet_force_full_sync";
|
||||||
private static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout";
|
private static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout";
|
||||||
private static final String PREF_GPODNET_HOSTNAME = "pref_gpodnet_hostname";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||||
@ -51,6 +48,7 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||||
public void syncStatusChanged(SyncServiceEvent event) {
|
public void syncStatusChanged(SyncServiceEvent event) {
|
||||||
|
updateGpodnetPreferenceScreen();
|
||||||
if (!GpodnetPreferences.loggedIn()) {
|
if (!GpodnetPreferences.loggedIn()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -66,6 +64,10 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
private void setupGpodderScreen() {
|
private void setupGpodderScreen() {
|
||||||
final Activity activity = getActivity();
|
final Activity activity = getActivity();
|
||||||
|
|
||||||
|
findPreference(PREF_GPODNET_LOGIN).setOnPreferenceClickListener(preference -> {
|
||||||
|
new GpodderAuthenticationFragment().show(getChildFragmentManager(), GpodderAuthenticationFragment.TAG);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
findPreference(PREF_GPODNET_SETLOGIN_INFORMATION)
|
findPreference(PREF_GPODNET_SETLOGIN_INFORMATION)
|
||||||
.setOnPreferenceClickListener(preference -> {
|
.setOnPreferenceClickListener(preference -> {
|
||||||
AuthenticationDialog dialog = new AuthenticationDialog(activity,
|
AuthenticationDialog dialog = new AuthenticationDialog(activity,
|
||||||
@ -94,11 +96,6 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
updateGpodnetPreferenceScreen();
|
updateGpodnetPreferenceScreen();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
findPreference(PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(preference -> {
|
|
||||||
GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(
|
|
||||||
dialog -> updateGpodnetPreferenceScreen());
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateGpodnetPreferenceScreen() {
|
private void updateGpodnetPreferenceScreen() {
|
||||||
@ -119,7 +116,6 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
} else {
|
} else {
|
||||||
findPreference(PREF_GPODNET_LOGOUT).setSummary(null);
|
findPreference(PREF_GPODNET_LOGOUT).setSummary(null);
|
||||||
}
|
}
|
||||||
findPreference(PREF_GPODNET_HOSTNAME).setSummary(GpodnetPreferences.getHostname());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLastGpodnetSyncReport(boolean successful, long lastTime) {
|
private void updateLastGpodnetSyncReport(boolean successful, long lastTime) {
|
||||||
|
@ -71,13 +71,13 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
Context context = getActivity().getApplicationContext();
|
Context context = getActivity().getApplicationContext();
|
||||||
String val;
|
String val;
|
||||||
long interval = UserPreferences.getUpdateInterval();
|
long interval = UserPreferences.getUpdateInterval();
|
||||||
if(interval > 0) {
|
if (interval > 0) {
|
||||||
int hours = (int) TimeUnit.MILLISECONDS.toHours(interval);
|
int hours = (int) TimeUnit.MILLISECONDS.toHours(interval);
|
||||||
String hoursStr = context.getResources().getQuantityString(R.plurals.time_hours_quantified, hours, hours);
|
val = context.getResources().getQuantityString(
|
||||||
val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_every), hoursStr);
|
R.plurals.pref_autoUpdateIntervallOrTime_every_hours, hours, hours);
|
||||||
} else {
|
} else {
|
||||||
int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
|
int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
|
||||||
if(timeOfDay.length == 2) {
|
if (timeOfDay.length == 2) {
|
||||||
Calendar cal = new GregorianCalendar();
|
Calendar cal = new GregorianCalendar();
|
||||||
cal.set(Calendar.HOUR_OF_DAY, timeOfDay[0]);
|
cal.set(Calendar.HOUR_OF_DAY, timeOfDay[0]);
|
||||||
cal.set(Calendar.MINUTE, timeOfDay[1]);
|
cal.set(Calendar.MINUTE, timeOfDay[1]);
|
||||||
|
@ -9,11 +9,14 @@ import androidx.preference.PreferenceFragmentCompat;
|
|||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||||
|
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
|
||||||
|
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog;
|
import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog;
|
||||||
import de.danoeh.antennapod.dialog.FeedSortDialog;
|
import de.danoeh.antennapod.dialog.FeedSortDialog;
|
||||||
import de.danoeh.antennapod.fragment.NavDrawerFragment;
|
import de.danoeh.antennapod.fragment.NavDrawerFragment;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.greenrobot.eventbus.EventBus;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -37,8 +40,17 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
(preference, newValue) -> {
|
(preference, newValue) -> {
|
||||||
getActivity().recreate();
|
getActivity().recreate();
|
||||||
return true;
|
return true;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
findPreference(UserPreferences.PREF_SHOW_TIME_LEFT)
|
||||||
|
.setOnPreferenceChangeListener(
|
||||||
|
(preference, newValue) -> {
|
||||||
|
UserPreferences.setShowRemainTimeSetting((Boolean) newValue);
|
||||||
|
EventBus.getDefault().post(new UnreadItemsUpdateEvent());
|
||||||
|
EventBus.getDefault().post(new PlayerStatusEvent());
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
|
findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
|
||||||
.setOnPreferenceClickListener(preference -> {
|
.setOnPreferenceClickListener(preference -> {
|
||||||
showDrawerPreferencesDialog();
|
showDrawerPreferencesDialog();
|
||||||
|
@ -9,7 +9,7 @@ import androidx.appcompat.widget.SearchView;
|
|||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
import de.danoeh.antennapod.fragment.SearchFragment;
|
import de.danoeh.antennapod.fragment.SearchFragment;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -2,6 +2,7 @@ package de.danoeh.antennapod.preferences;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.view.KeyEvent;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import de.danoeh.antennapod.BuildConfig;
|
import de.danoeh.antennapod.BuildConfig;
|
||||||
@ -92,5 +93,16 @@ public class PreferenceUpgrader {
|
|||||||
if (oldVersion < 1080100) {
|
if (oldVersion < 1080100) {
|
||||||
prefs.edit().putString(UserPreferences.PREF_VIDEO_BEHAVIOR, "pip").apply();
|
prefs.edit().putString(UserPreferences.PREF_VIDEO_BEHAVIOR, "pip").apply();
|
||||||
}
|
}
|
||||||
|
if (oldVersion < 2010300) {
|
||||||
|
// Migrate hardware button preferences
|
||||||
|
if (prefs.getBoolean("prefHardwareForwardButtonSkips", false)) {
|
||||||
|
prefs.edit().putString(UserPreferences.PREF_HARDWARE_FORWARD_BUTTON,
|
||||||
|
String.valueOf(KeyEvent.KEYCODE_MEDIA_NEXT)).apply();
|
||||||
|
}
|
||||||
|
if (prefs.getBoolean("prefHardwarePreviousButtonRestarts", false)) {
|
||||||
|
prefs.edit().putString(UserPreferences.PREF_HARDWARE_PREVIOUS_BUTTON,
|
||||||
|
String.valueOf(KeyEvent.KEYCODE_MEDIA_PREVIOUS)).apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
129
app/src/main/java/de/danoeh/antennapod/view/ChapterSeekBar.java
Normal file
129
app/src/main/java/de/danoeh/antennapod/view/ChapterSeekBar.java
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package de.danoeh.antennapod.view;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
|
|
||||||
|
public class ChapterSeekBar extends androidx.appcompat.widget.AppCompatSeekBar {
|
||||||
|
|
||||||
|
private float top;
|
||||||
|
private float width;
|
||||||
|
private float bottom;
|
||||||
|
private float density;
|
||||||
|
private float progressPrimary;
|
||||||
|
private float progressSecondary;
|
||||||
|
private float[] dividerPos;
|
||||||
|
private final Paint paintBackground = new Paint();
|
||||||
|
private final Paint paintProgressPrimary = new Paint();
|
||||||
|
private final Paint paintProgressSecondary = new Paint();
|
||||||
|
|
||||||
|
public ChapterSeekBar(Context context) {
|
||||||
|
super(context);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChapterSeekBar(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChapterSeekBar(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Context context) {
|
||||||
|
setBackground(null); // Removes the thumb shadow
|
||||||
|
dividerPos = null;
|
||||||
|
density = context.getResources().getDisplayMetrics().density;
|
||||||
|
paintBackground.setColor(ThemeUtils.getColorFromAttr(getContext(),
|
||||||
|
de.danoeh.antennapod.core.R.attr.currently_playing_background));
|
||||||
|
paintBackground.setAlpha(128);
|
||||||
|
paintProgressPrimary.setColor(ThemeUtils.getColorFromAttr(getContext(),
|
||||||
|
de.danoeh.antennapod.core.R.attr.colorPrimary));
|
||||||
|
paintProgressSecondary.setColor(ThemeUtils.getColorFromAttr(getContext(),
|
||||||
|
de.danoeh.antennapod.core.R.attr.seek_background));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the relative positions of the chapter dividers.
|
||||||
|
* @param dividerPos of the chapter dividers relative to the duration of the media.
|
||||||
|
*/
|
||||||
|
public void setDividerPos(final float[] dividerPos) {
|
||||||
|
if (dividerPos != null) {
|
||||||
|
this.dividerPos = new float[dividerPos.length + 2];
|
||||||
|
this.dividerPos[0] = 0;
|
||||||
|
System.arraycopy(dividerPos, 0, this.dividerPos, 1, dividerPos.length);
|
||||||
|
this.dividerPos[this.dividerPos.length - 1] = 1;
|
||||||
|
} else {
|
||||||
|
this.dividerPos = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected synchronized void onDraw(Canvas canvas) {
|
||||||
|
top = getTop() + density * 7.5f;
|
||||||
|
bottom = getBottom() - density * 7.5f;
|
||||||
|
width = (float) (getRight() - getPaddingRight() - getLeft() - getPaddingLeft());
|
||||||
|
progressSecondary = getSecondaryProgress() / (float) getMax() * width;
|
||||||
|
progressPrimary = getProgress() / (float) getMax() * width;
|
||||||
|
|
||||||
|
if (dividerPos == null) {
|
||||||
|
drawProgress(canvas);
|
||||||
|
} else {
|
||||||
|
drawProgressChapters(canvas);
|
||||||
|
}
|
||||||
|
drawThumb(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawProgress(Canvas canvas) {
|
||||||
|
final int saveCount = canvas.save();
|
||||||
|
canvas.translate(getPaddingLeft(), getPaddingTop());
|
||||||
|
canvas.drawRect(0, top, width, bottom, paintBackground);
|
||||||
|
canvas.drawRect(0, top, progressSecondary, bottom, paintProgressSecondary);
|
||||||
|
canvas.drawRect(0, top, progressPrimary, bottom, paintProgressPrimary);
|
||||||
|
canvas.restoreToCount(saveCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawProgressChapters(Canvas canvas) {
|
||||||
|
final int saveCount = canvas.save();
|
||||||
|
int currChapter = 1;
|
||||||
|
float chapterMargin = density * 0.6f;
|
||||||
|
float topExpanded = getTop() + density * 7;
|
||||||
|
float bottomExpanded = getBottom() - density * 7;
|
||||||
|
|
||||||
|
canvas.translate(getPaddingLeft(), getPaddingTop());
|
||||||
|
|
||||||
|
for (int i = 1; i < dividerPos.length; i++) {
|
||||||
|
float right = dividerPos[i] * width - chapterMargin;
|
||||||
|
float left = dividerPos[i - 1] * width + chapterMargin;
|
||||||
|
float rightCurr = dividerPos[currChapter] * width - chapterMargin;
|
||||||
|
float leftCurr = dividerPos[currChapter - 1] * width + chapterMargin;
|
||||||
|
|
||||||
|
canvas.drawRect(left, top, right, bottom, paintBackground);
|
||||||
|
|
||||||
|
if (right < progressPrimary) {
|
||||||
|
currChapter = i + 1;
|
||||||
|
canvas.drawRect(left, top, right, bottom, paintProgressPrimary);
|
||||||
|
} else if (isPressed()) {
|
||||||
|
canvas.drawRect(leftCurr, topExpanded, rightCurr, bottomExpanded, paintBackground);
|
||||||
|
canvas.drawRect(leftCurr, topExpanded, progressPrimary, bottomExpanded, paintProgressPrimary);
|
||||||
|
} else {
|
||||||
|
if (progressSecondary > leftCurr) {
|
||||||
|
canvas.drawRect(leftCurr, top, progressSecondary, bottom, paintProgressSecondary);
|
||||||
|
}
|
||||||
|
canvas.drawRect(leftCurr, top, progressPrimary, bottom, paintProgressPrimary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas.restoreToCount(saveCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawThumb(Canvas canvas) {
|
||||||
|
final int saveCount = canvas.save();
|
||||||
|
canvas.translate(getPaddingLeft() - getThumbOffset(), getPaddingTop());
|
||||||
|
getThumb().draw(canvas);
|
||||||
|
canvas.restoreToCount(saveCount);
|
||||||
|
}
|
||||||
|
}
|
@ -6,9 +6,9 @@ import android.content.res.Configuration;
|
|||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import androidx.appcompat.view.ContextThemeWrapper;
|
import androidx.appcompat.view.ContextThemeWrapper;
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
|
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import io.reactivex.annotations.Nullable;
|
import io.reactivex.annotations.Nullable;
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ public class EpisodeItemListRecyclerView extends RecyclerView {
|
|||||||
layoutManager.setRecycleChildrenOnDetach(true);
|
layoutManager.setRecycleChildrenOnDetach(true);
|
||||||
setLayoutManager(layoutManager);
|
setLayoutManager(layoutManager);
|
||||||
setHasFixedSize(true);
|
setHasFixedSize(true);
|
||||||
addItemDecoration(new HorizontalDividerItemDecoration.Builder(getContext()).build());
|
addItemDecoration(new DividerItemDecoration(getContext(), layoutManager.getOrientation()));
|
||||||
setClipToPadding(false);
|
setClipToPadding(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ public class DownloadItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
public final TextView type;
|
public final TextView type;
|
||||||
public final TextView date;
|
public final TextView date;
|
||||||
public final TextView reason;
|
public final TextView reason;
|
||||||
|
public final TextView tapForDetails;
|
||||||
|
|
||||||
public DownloadItemViewHolder(Context context, ViewGroup parent) {
|
public DownloadItemViewHolder(Context context, ViewGroup parent) {
|
||||||
super(LayoutInflater.from(context).inflate(R.layout.downloadlog_item, parent, false));
|
super(LayoutInflater.from(context).inflate(R.layout.downloadlog_item, parent, false));
|
||||||
@ -27,6 +28,7 @@ public class DownloadItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
type = itemView.findViewById(R.id.txtvType);
|
type = itemView.findViewById(R.id.txtvType);
|
||||||
icon = itemView.findViewById(R.id.txtvIcon);
|
icon = itemView.findViewById(R.id.txtvIcon);
|
||||||
reason = itemView.findViewById(R.id.txtvReason);
|
reason = itemView.findViewById(R.id.txtvReason);
|
||||||
|
tapForDetails = itemView.findViewById(R.id.txtvTapForDetails);
|
||||||
secondaryActionButton = itemView.findViewById(R.id.secondaryActionButton);
|
secondaryActionButton = itemView.findViewById(R.id.secondaryActionButton);
|
||||||
secondaryActionIcon = itemView.findViewById(R.id.secondaryActionIcon);
|
secondaryActionIcon = itemView.findViewById(R.id.secondaryActionIcon);
|
||||||
title = itemView.findViewById(R.id.txtvTitle);
|
title = itemView.findViewById(R.id.txtvTitle);
|
||||||
|
@ -13,9 +13,7 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.cardview.widget.CardView;
|
import androidx.cardview.widget.CardView;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.joanzapata.iconify.Iconify;
|
import com.joanzapata.iconify.Iconify;
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.adapter.CoverLoader;
|
import de.danoeh.antennapod.adapter.CoverLoader;
|
||||||
@ -25,13 +23,15 @@ import de.danoeh.antennapod.core.feed.FeedItem;
|
|||||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||||
import de.danoeh.antennapod.core.feed.MediaType;
|
import de.danoeh.antennapod.core.feed.MediaType;
|
||||||
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
|
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
|
||||||
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||||
|
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||||
import de.danoeh.antennapod.core.util.Converter;
|
import de.danoeh.antennapod.core.util.Converter;
|
||||||
import de.danoeh.antennapod.core.util.DateUtils;
|
import de.danoeh.antennapod.core.util.DateUtils;
|
||||||
import de.danoeh.antennapod.core.util.NetworkUtils;
|
import de.danoeh.antennapod.core.util.NetworkUtils;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||||
import de.danoeh.antennapod.view.CircularProgressBar;
|
import de.danoeh.antennapod.ui.common.CircularProgressBar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the view which shows FeedItems.
|
* Holds the view which shows FeedItems.
|
||||||
@ -121,8 +121,8 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
|
|
||||||
if (coverHolder.getVisibility() == View.VISIBLE) {
|
if (coverHolder.getVisibility() == View.VISIBLE) {
|
||||||
new CoverLoader(activity)
|
new CoverLoader(activity)
|
||||||
.withUri(ImageResourceUtils.getImageLocation(item))
|
.withUri(ImageResourceUtils.getEpisodeListImageLocation(item))
|
||||||
.withFallbackUri(item.getFeed().getImageLocation())
|
.withFallbackUri(item.getFeed().getImageUrl())
|
||||||
.withPlaceholderView(placeholder)
|
.withPlaceholderView(placeholder)
|
||||||
.withCoverView(cover)
|
.withCoverView(cover)
|
||||||
.load();
|
.load();
|
||||||
@ -132,9 +132,6 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
private void bind(FeedMedia media) {
|
private void bind(FeedMedia media) {
|
||||||
isVideo.setVisibility(media.getMediaType() == MediaType.VIDEO ? View.VISIBLE : View.GONE);
|
isVideo.setVisibility(media.getMediaType() == MediaType.VIDEO ? View.VISIBLE : View.GONE);
|
||||||
duration.setVisibility(media.getDuration() > 0 ? View.VISIBLE : View.GONE);
|
duration.setVisibility(media.getDuration() > 0 ? View.VISIBLE : View.GONE);
|
||||||
duration.setText(Converter.getDurationStringLong(media.getDuration()));
|
|
||||||
duration.setContentDescription(activity.getString(R.string.chapter_duration,
|
|
||||||
Converter.getDurationStringLocalized(activity, media.getDuration())));
|
|
||||||
|
|
||||||
if (media.isCurrentlyPlaying()) {
|
if (media.isCurrentlyPlaying()) {
|
||||||
itemView.setBackgroundColor(ThemeUtils.getColorFromAttr(activity, R.attr.currently_playing_background));
|
itemView.setBackgroundColor(ThemeUtils.getColorFromAttr(activity, R.attr.currently_playing_background));
|
||||||
@ -152,6 +149,9 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
secondaryActionProgress.setPercentage(0, item); // Animate X% -> 0%
|
secondaryActionProgress.setPercentage(0, item); // Animate X% -> 0%
|
||||||
}
|
}
|
||||||
|
|
||||||
|
duration.setText(Converter.getDurationStringLong(media.getDuration()));
|
||||||
|
duration.setContentDescription(activity.getString(R.string.chapter_duration,
|
||||||
|
Converter.getDurationStringLocalized(activity, media.getDuration())));
|
||||||
if (item.getState() == FeedItem.State.PLAYING || item.getState() == FeedItem.State.IN_PROGRESS) {
|
if (item.getState() == FeedItem.State.PLAYING || item.getState() == FeedItem.State.IN_PROGRESS) {
|
||||||
int progress = (int) (100.0 * media.getPosition() / media.getDuration());
|
int progress = (int) (100.0 * media.getPosition() / media.getDuration());
|
||||||
progressBar.setProgress(progress);
|
progressBar.setProgress(progress);
|
||||||
@ -160,6 +160,11 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
Converter.getDurationStringLocalized(activity, media.getPosition())));
|
Converter.getDurationStringLocalized(activity, media.getPosition())));
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
position.setVisibility(View.VISIBLE);
|
position.setVisibility(View.VISIBLE);
|
||||||
|
if (UserPreferences.shouldShowRemainingTime()) {
|
||||||
|
duration.setText("-" + Converter.getDurationStringLong(media.getDuration() - media.getPosition()));
|
||||||
|
duration.setContentDescription(activity.getString(R.string.chapter_duration,
|
||||||
|
Converter.getDurationStringLocalized(activity, (media.getDuration() - media.getPosition()))));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
position.setVisibility(View.GONE);
|
position.setVisibility(View.GONE);
|
||||||
@ -186,6 +191,22 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateDuration(PlaybackPositionEvent event) {
|
||||||
|
int currentPosition = event.getPosition();
|
||||||
|
int timeDuration = event.getDuration();
|
||||||
|
int remainingTime = event.getDuration() - event.getPosition();
|
||||||
|
Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition));
|
||||||
|
if (currentPosition == PlaybackService.INVALID_TIME || timeDuration == PlaybackService.INVALID_TIME) {
|
||||||
|
Log.w(TAG, "Could not react to position observer update because of invalid time");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (UserPreferences.shouldShowRemainingTime()) {
|
||||||
|
duration.setText("-" + Converter.getDurationStringLong(remainingTime));
|
||||||
|
} else {
|
||||||
|
duration.setText(Converter.getDurationStringLong(timeDuration));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public FeedItem getFeedItem() {
|
public FeedItem getFeedItem() {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@ -197,7 +218,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
|
public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
|
||||||
progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
|
progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
|
||||||
position.setText(Converter.getDurationStringLong(event.getPosition()));
|
position.setText(Converter.getDurationStringLong(event.getPosition()));
|
||||||
duration.setText(Converter.getDurationStringLong(event.getDuration()));
|
updateDuration(event);
|
||||||
duration.setVisibility(View.VISIBLE); // Even if the duration was previously unknown, it is now known
|
duration.setVisibility(View.VISIBLE); // Even if the duration was previously unknown, it is now known
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ AntennaPod je správce a přehrávač podcastů, co vám umožňuje okamžitý p
|
|||||||
Stahujte, streamujte nebo si vytvořte frontu epizod a užijte si poslech tak, jak ho máte rádi s nastavitelnou rychlostí přehrávání, podporou kapitol a s časovačem vypnutí.
|
Stahujte, streamujte nebo si vytvořte frontu epizod a užijte si poslech tak, jak ho máte rádi s nastavitelnou rychlostí přehrávání, podporou kapitol a s časovačem vypnutí.
|
||||||
Ušetři si námahu, baterku i mobilní data s pomocí robustní automatické kontroly nad stahováním epizod (urči časy, intervaly a WIFI sítě) a mazáním epizod (na základě oblíbenosti a nastavení zpoždění).
|
Ušetři si námahu, baterku i mobilní data s pomocí robustní automatické kontroly nad stahováním epizod (urči časy, intervaly a WIFI sítě) a mazáním epizod (na základě oblíbenosti a nastavení zpoždění).
|
||||||
|
|
||||||
Vytvořeno nadšenci do podcastů, AntennaPod je otevřený software (OSS), zdarma a bez reklam.
|
Vytvořeno nadšenci do podcastů, AntennaPod je software s otevřeným zdrojovým kódem, zdarma a bez reklam.
|
||||||
|
|
||||||
<b>Importujte, zorganizujte a přehrávejte</b>
|
<b>Importujte, organizujte a přehrávejte</b>
|
||||||
• Ovládejte přehrávání odkudkoli: z widgetu na domovské obrazovce, z oznámení nebo pomocí tlačítek na sluchátkách včetně Bluetooth
|
• Ovládejte přehrávání odkudkoli: z widgetu na domovské obrazovce, z oznámení nebo pomocí tlačítek na sluchátkách včetně Bluetooth
|
||||||
• Přidejte a importujte podcasty přes iTunes anebo gPodder.net, OPML soubory a RSS anebo Atom odkazy
|
• Přidejte a importujte podcasty přes iTunes anebo gPodder.net, OPML soubory a RSS anebo Atom odkazy
|
||||||
• Užijte si poslech s nastavitelnou rychlostí přehrávání, podporou kapitol, zapamatování poslední pozice přehrávání a pokročilým časovačem vypnutí (restart zatřesením, snížení hlasitosti)
|
• Užijte si poslech s nastavitelnou rychlostí přehrávání, podporou kapitol, zapamatování poslední pozice přehrávání a pokročilým časovačem vypnutí (restart zatřesením, snížení hlasitosti)
|
||||||
@ -21,7 +21,7 @@ Vytvořeno nadšenci do podcastů, AntennaPod je otevřený software (OSS), zdar
|
|||||||
• Přizpůsobte si aplikaci svému prostředí pomocí světlého nebo tmavého motivu
|
• Přizpůsobte si aplikaci svému prostředí pomocí světlého nebo tmavého motivu
|
||||||
• Zálohujte své sbírky pomocí služby gPodder.net nebo exportem OPML souborů
|
• Zálohujte své sbírky pomocí služby gPodder.net nebo exportem OPML souborů
|
||||||
|
|
||||||
<b>Přidejte se do komunity AntennaPodu!</b>
|
<b>Přidejte se ke komunitě AntennaPod!</b>
|
||||||
AntennaPod je aktivně vyvíjen dobrovolníky. Můžete přispět také svým kódem nebo komentáři!
|
AntennaPod je aktivně vyvíjen dobrovolníky. Můžete přispět také svým kódem nebo komentáři!
|
||||||
|
|
||||||
Naše přátelská členská základna vám ráda zodpoví jakékoli dotazy. Zveme vás k diskuzi o AntennaPodu nebo i podcastech obecně.
|
Naše přátelská členská základna vám ráda zodpoví jakékoli dotazy. Zveme vás k diskuzi o AntennaPodu nebo i podcastech obecně.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
AntennaPod ist ein Podcast-Manager und -Player, der dir unmittelbar Zugriff auf Millionen von freien und bezahlten Podcasts ermöglicht. Angefangen von unabhängigen Podcastern zu großen Rundfunkanstalten oder Hörfunksendern wie BBC, NPR und CNN. Abonniere, importiere und exportiere deine Feeds mühelos mit Hilfe des iTunes-Verzeichnisses, OPML-Dateien oder einfachen RSS-URLs.
|
AntennaPod ist ein Podcast-Manager und -Player, mit dem Du direkten Zugriff auf Millionen von freien und kostenpflichtigen Podcasts hast. Angefangen von unabhängigen Podcastern zu großen Rundfunkanstalten oder Hörfunksendern wie BBC, NPR und CNN. Abonniere, importiere und exportiere deine Feeds mühelos mit Hilfe des iTunes-Podcast-Verzeichnisses, OPML-Dateien oder RSS-URLs.
|
||||||
Downloade, streame oder sortiere Episoden in der Abspielliste und genieße sie mit einstellbarer Abspielgeschwindigkeit, Unterstützung von Kapiteln und Schlummerfunktion.
|
Downloade, streame oder sortiere Episoden in der Abspielliste und genieße sie mit einstellbarer Abspielgeschwindigkeit, Unterstützung von Kapiteln und Schlummerfunktion.
|
||||||
Reduziere Aufwand, Stromverbrauch und Datenverbrauch durch leistungsfähige Kontrolle der Downloads (bestimmte Uhrzeiten, Intervalle, WiFi-Netze) und des Löschens (basierend auf deinen Favoriten und weiteren Einstellungen).
|
Reduziere Aufwand, Stromverbrauch und Datenverbrauch durch leistungsfähige Kontrolle der Downloads (bestimmte Uhrzeiten, Intervalle, WiFi-Netze) und des Löschens (basierend auf deinen Favoriten und weiteren Einstellungen).
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 9.5 KiB |
@ -24,8 +24,8 @@ Dodawaj i importuj kanały z iTunes i gPodder.net, plików OPML oraz z adresów
|
|||||||
<b>Dołącz do społeczności AntennaPod</b>
|
<b>Dołącz do społeczności AntennaPod</b>
|
||||||
AntennaPod jest ciągle rozwijane przez ochotników. Ty też możesz pomóc, kodem lub komentarzem!
|
AntennaPod jest ciągle rozwijane przez ochotników. Ty też możesz pomóc, kodem lub komentarzem!
|
||||||
|
|
||||||
Chcesz zgłosić błąd lub brakuje Ci jakiejś funkcji, a może programujesz? Odwiedź nasz GitHub:
|
Życzliwi użytkownicy forum chętnie odpowiedzą na twoje pytania. Zapraszamy do rozmów o funkcjach programu i generalnie o podcastingu.
|
||||||
https://www.github.com/AntennaPod/AntennaPod
|
https://forum.antennapod.org/
|
||||||
|
|
||||||
Chcesz pomóc tłumaczyć AntennaPod - możesz to zrobić na Transifex:
|
Chcesz pomóc tłumaczyć AntennaPod - możesz to zrobić na Transifex:
|
||||||
https://www.transifex.com/antennapod/antennapod
|
https://www.transifex.com/antennapod/antennapod
|
31
app/src/main/play/listings/sk/full-description.txt
Normal file
31
app/src/main/play/listings/sk/full-description.txt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
AntennaPod je správca a prehrávač podcastov, ktorý vám sprostredkuje okamžitý prístup k miliónom bezplatným a spoplatneným podcastom - od nezávislých podcastérov až k vydavateľstvám ako BBC, NPR a CNN. Pridávajte, importujte a exportujte ich zdroje bez problémov pomocou databázy podcastov iTunes, OPML súborov alebo odkazov na RSS.
|
||||||
|
Stiahnite, streamujte alebo plánujte epizódy a užívajte si ich ako sa vám páči s nastaviteľnou rýchlosťou prehrávania, podporou kapitol a časovačom vypnutia.
|
||||||
|
Ušetrite si námahu, baterku a mobilné dáta pomocou výkonnej automatickej kontroly sťahovania epizód (určite čas, intervaly a WiFi siete) a mazania epizód (založené na obľúbených epizódach a nastavení oneskorenia).
|
||||||
|
|
||||||
|
AntennaPod je spravovaný podcastovými entuziastami a je slobodný v každom slova zmysle: otvorený zdrojový kód, zadarmo, bez reklamy.
|
||||||
|
|
||||||
|
<b>Importovať, spravovať a prehrať</b>
|
||||||
|
• Ovládajte prehrávanie odkiaľkoľvek: domovská obrazovka, systémové upozornenia a handsfree a ovládanie cez bluetooth
|
||||||
|
• Pridať a importovať zdroje pomocou priečinkov iTunes a gPodder.net, OPML súborov a odkazov RSS alebo Atom
|
||||||
|
• Užívajte si počúvanie s nastaviteľnou rýchlosťou prehrávania, podporou kapitol, zapamätanou pozíciou prehrávania a pokročilým nastavením vypnutia (zatrasením resetuj, stíš hlasitosť)
|
||||||
|
Počúvajte heslom chránené zdroje a epizódy
|
||||||
|
|
||||||
|
<b>Sledovať, zdielať a oceniť</b>
|
||||||
|
• Sledujte najlepšie z najlepších pomocou označenia ako obľúbené
|
||||||
|
• Nájdite epizódu v histórií prehrávaní alebo v nadpisoch a popisoch
|
||||||
|
• Zdielajte epizódy a zdroje pomocou pokročilých nastavení sociálnych sietí a e-mailu, služieb gPodder.net a cez export do OPML súboru
|
||||||
|
|
||||||
|
<b>Spravovať systém</b>
|
||||||
|
• Spravujte automatické sťahovanie: vyberte zdroje, vylúčte mobilné siete, vyberte konkrétne WiFi siete, vyžadujte nabíjanie telefónu a nastavte časy a intervaly
|
||||||
|
• Spravujte úložisko nastavením počtu uložených epizód, inteligentným mazaním a zvolením umiestnenia súborov
|
||||||
|
• Prispôsobte si vzhľad na svetlý alebo tmavý
|
||||||
|
• Zálohujte si odbery pomocou gPodder.net a OPML exportu
|
||||||
|
|
||||||
|
<b>Pridať sa do komunity AntennaPod!</b>
|
||||||
|
AntennaPod aktívne vyvíjajú dobrovoľníci. Tiež môžete prispieť kódom alebo komentárom.
|
||||||
|
|
||||||
|
Na našom fóre môžete v priateľskej atmosfére diskutovať a nájsť odpovede na vaše otázky o nových funkciách alebo všeobecne o podcastingu.
|
||||||
|
https://forum.antennapod.org/
|
||||||
|
|
||||||
|
Transifex je miesto, kde môžete pomôcť s prekladom:
|
||||||
|
https://www.transifex.com/antennapod/antennapod
|
1
app/src/main/play/listings/sk/short-description.txt
Normal file
1
app/src/main/play/listings/sk/short-description.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Ľahko použíteľný, flexibilný a open source správca a prehrávač podcastov
|
1
app/src/main/play/listings/sk/title.txt
Normal file
1
app/src/main/play/listings/sk/title.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
AntennaPod
|
@ -1,7 +1,12 @@
|
|||||||
- A long-standing wish of many: playing local files! In the 'Add podcast' screen simply tap 'Add local folder' and select a location on your phone! (@ByteHamster, @igoralmeida & @damoasda)
|
NEW
|
||||||
- Pick a country for the 'Discover' screen (@tonytamsf)
|
- Optional notifications for new episodes (@connectety)
|
||||||
- Keyboard shortcuts (@asdoi)
|
- Use PodcastIndex for main search (@tonytamsf)
|
||||||
- Search the PodcastIndex.org database (@edwinhere)
|
- Sleep timer extend buttons (@max-wittig)
|
||||||
- Pull to refresh (@asdoi)
|
- Optional rewind, forward & skip buttons on widget (@tonytamsf)
|
||||||
- Playback speed & filter dialogs (@ByteHamster & @bws9000)
|
- 'When not favorited' as Episode Cleanup (@spacecowboy)
|
||||||
- Smooth sleep timer volume (@olivoto)
|
|
||||||
|
IMPROVED
|
||||||
|
- More actions for hardware buttons (@timakro)
|
||||||
|
- Android Auto & chapter support (@tonytamsf, @ByteHamster)
|
||||||
|
- Fixed stuck notification (@a1291762)
|
||||||
|
- Player screen usability for visually impaired (@ByteHamster)
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
android:id="@+id/widget_config_preview"
|
android:id="@+id/widget_config_preview"
|
||||||
layout="@layout/player_widget"
|
layout="@layout/player_widget"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="80dp"
|
android:layout_height="96dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_margin="16dp" />
|
android:layout_margin="16dp" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
@ -68,13 +68,38 @@
|
|||||||
android:max="100"
|
android:max="100"
|
||||||
android:progress="100" />
|
android:progress="100" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/ckRewind"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Rewind" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/ckFastForward"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Forward" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/ckSkip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Skip" />
|
||||||
|
</LinearLayout>
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/butConfirm"
|
android:id="@+id/butConfirm"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:text="@string/widget_create_button" />
|
android:text="@string/widget_create_button" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -51,6 +51,34 @@
|
|||||||
app:tint="?android:attr/windowBackground"
|
app:tint="?android:attr/windowBackground"
|
||||||
android:importantForAccessibility="no"/>
|
android:importantForAccessibility="no"/>
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/cardViewSeek"
|
||||||
|
android:alpha="0"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignBottom="@+id/pager"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardBackgroundColor="?attr/seek_background"
|
||||||
|
app:cardElevation="0dp"
|
||||||
|
tools:alpha="1">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtvSeek"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="24dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingRight="24dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="24sp"
|
||||||
|
tools:text="1:06:29" />
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/playtime_layout"
|
android:id="@+id/playtime_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -60,7 +88,7 @@
|
|||||||
android:layoutDirection="ltr"
|
android:layoutDirection="ltr"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<SeekBar
|
<de.danoeh.antennapod.view.ChapterSeekBar
|
||||||
android:id="@+id/sbPosition"
|
android:id="@+id/sbPosition"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -128,7 +156,7 @@
|
|||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
tools:srcCompat="@drawable/ic_av_play_white_24dp"/>
|
tools:srcCompat="@drawable/ic_av_play_white_24dp"/>
|
||||||
|
|
||||||
<de.danoeh.antennapod.view.CircularProgressBar
|
<de.danoeh.antennapod.ui.common.CircularProgressBar
|
||||||
android:layout_width="@dimen/audioplayer_playercontrols_length_big"
|
android:layout_width="@dimen/audioplayer_playercontrols_length_big"
|
||||||
android:layout_height="@dimen/audioplayer_playercontrols_length_big"
|
android:layout_height="@dimen/audioplayer_playercontrols_length_big"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
@ -136,7 +164,8 @@
|
|||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
android:layout_centerVertical="true"/>
|
android:layout_centerVertical="true"
|
||||||
|
app:foregroundColor="?attr/action_icon_color"/>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
style="?android:attr/progressBarStyle"
|
style="?android:attr/progressBarStyle"
|
||||||
@ -177,7 +206,7 @@
|
|||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:clickable="false"/>
|
android:clickable="false"/>
|
||||||
|
|
||||||
<de.danoeh.antennapod.view.PlaybackSpeedIndicatorView
|
<de.danoeh.antennapod.ui.common.PlaybackSpeedIndicatorView
|
||||||
android:id="@+id/butPlaybackSpeed"
|
android:id="@+id/butPlaybackSpeed"
|
||||||
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
||||||
android:layout_height="@dimen/audioplayer_playercontrols_length"
|
android:layout_height="@dimen/audioplayer_playercontrols_length"
|
||||||
@ -186,7 +215,8 @@
|
|||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="@string/playback_speed"
|
android:contentDescription="@string/playback_speed"
|
||||||
tools:srcCompat="@drawable/ic_playback_speed_white"/>
|
tools:srcCompat="@drawable/ic_playback_speed_white"
|
||||||
|
app:foregroundColor="?attr/action_icon_color"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtvPlaybackSpeed"
|
android:id="@+id/txtvPlaybackSpeed"
|
||||||
|
@ -1,30 +1,59 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical" >
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/etxtUsername"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:orientation="vertical"
|
||||||
android:layout_margin="16dp"
|
android:padding="16dp">
|
||||||
android:hint="@string/username_label"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:cursorVisible="true"/>
|
|
||||||
|
|
||||||
<EditText
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/etxtPassword"
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_marginBottom="8dp">
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:inputType="textPassword"
|
|
||||||
android:hint="@string/password_label"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:cursorVisible="true"/>
|
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/usernameEditText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/username_label"
|
||||||
|
android:lines="1"/>
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/passwordEditText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/password_label"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:lines="1"/>
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.joanzapata.iconify.widget.IconTextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/showPasswordButton"
|
||||||
|
android:text="{fa-eye}"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:alpha="0.6"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginStart="8dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -10,7 +10,7 @@
|
|||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
android:gravity="center">
|
android:gravity="center">
|
||||||
|
|
||||||
<de.danoeh.antennapod.view.SquareImageView
|
<de.danoeh.antennapod.ui.common.SquareImageView
|
||||||
android:id="@+id/imgvCover"
|
android:id="@+id/imgvCover"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="200dp"
|
android:layout_height="200dp"
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtvTitle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/authentication_notification_title"
|
|
||||||
android:textSize="@dimen/text_size_large"
|
|
||||||
android:textColor="?android:attr/textColorPrimary"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtvDescription"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/authentication_notification_msg"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"/>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/etxtUsername"
|
|
||||||
android:hint="@string/username_label"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:cursorVisible="true"/>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/etxtPassword"
|
|
||||||
android:hint="@string/password_label"
|
|
||||||
android:inputType="textPassword"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:cursorVisible="true"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="end">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/butCancel"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/cancel_label"
|
|
||||||
style="@style/Widget.MaterialComponents.Button.TextButton"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/butConfirm"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/confirm_label"
|
|
||||||
style="@style/Widget.MaterialComponents.Button.TextButton"/>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@ -83,6 +83,14 @@
|
|||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
tools:text="@string/design_time_downloaded_log_failure_reason"/>
|
tools:text="@string/design_time_downloaded_log_failure_reason"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtvTapForDetails"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:text="@string/download_error_tap_for_details"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include layout="@layout/secondary_action"/>
|
<include layout="@layout/secondary_action"/>
|
||||||
|
@ -94,17 +94,6 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtvInformation"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="2dp"
|
|
||||||
android:background="?android:attr/windowBackground"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:gravity="center"
|
|
||||||
tools:visibility="visible"
|
|
||||||
tools:text="(i) Information"/>
|
|
||||||
|
|
||||||
<com.joanzapata.iconify.widget.IconTextView
|
<com.joanzapata.iconify.widget.IconTextView
|
||||||
android:id="@+id/txtvFailure"
|
android:id="@+id/txtvFailure"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -117,4 +106,15 @@
|
|||||||
android:text="@string/refresh_failed_msg"
|
android:text="@string/refresh_failed_msg"
|
||||||
tools:visibility="visible"
|
tools:visibility="visible"
|
||||||
tools:text="(!) Last refresh failed"/>
|
tools:text="(!) Last refresh failed"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtvInformation"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:background="?android:attr/windowBackground"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:gravity="center"
|
||||||
|
tools:visibility="visible"
|
||||||
|
tools:text="(i) Information"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -145,6 +145,7 @@
|
|||||||
android:layout_marginRight="4dp"
|
android:layout_marginRight="4dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:text="·"
|
android:text="·"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
tools:background="@android:color/holo_blue_light"/>
|
tools:background="@android:color/holo_blue_light"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -163,6 +164,7 @@
|
|||||||
android:layout_marginRight="4dp"
|
android:layout_marginRight="4dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:text="·"
|
android:text="·"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
tools:background="@android:color/holo_blue_light"/>
|
tools:background="@android:color/holo_blue_light"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user