summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.circleci/config.yml94
-rw-r--r--.tx/config2
-rw-r--r--app/build.gradle2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java14
-rw-r--r--app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java203
-rw-r--r--app/src/main/AndroidManifest.xml9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java22
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java55
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java22
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java75
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java72
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java81
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java95
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java28
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java71
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java55
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java180
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java92
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java4
-rw-r--r--app/src/main/res/layout/bug_report.xml28
-rw-r--r--app/src/main/res/layout/checkbox_do_not_show_again.xml17
-rw-r--r--app/src/main/res/layout/downloaded_episodeslist_item.xml2
-rw-r--r--app/src/main/res/layout/feeditem_fragment.xml2
-rw-r--r--app/src/main/res/layout/feeditemlist_header.xml2
-rw-r--r--app/src/main/res/layout/gpodnet_podcast_listitem.xml2
-rw-r--r--app/src/main/res/layout/itunes_podcast_listitem.xml2
-rw-r--r--app/src/main/res/layout/mediaplayerinfo_activity.xml29
-rw-r--r--app/src/main/res/layout/nav_feedlistitem.xml2
-rw-r--r--app/src/main/res/layout/queue_listitem.xml2
-rw-r--r--app/src/main/res/layout/searchlist_item.xml2
-rw-r--r--app/src/main/res/layout/statistics_listitem.xml2
-rw-r--r--app/src/main/res/menu/allepisodes_context.xml86
-rw-r--r--app/src/main/res/menu/feedinfo.xml2
-rw-r--r--app/src/main/res/menu/feeditem_options.xml5
-rw-r--r--app/src/main/res/menu/feeditemlist_context.xml5
-rw-r--r--app/src/main/res/menu/feedlist.xml2
-rw-r--r--app/src/main/res/menu/mediaplayer.xml8
-rw-r--r--app/src/main/res/menu/queue_context.xml73
-rw-r--r--app/src/main/res/menu/subscriptions.xml7
-rw-r--r--app/src/main/res/xml/preferences.xml14
-rw-r--r--app/src/main/res/xml/preferences_user_interface.xml2
-rw-r--r--app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java54
-rw-r--r--app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java15
-rw-r--r--build.gradle15
-rw-r--r--config/checkstyle/checkstyle.xml82
-rw-r--r--core/build.gradle2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java23
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java39
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java43
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java27
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java60
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java117
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java38
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java36
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java15
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Optional.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java27
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java70
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java2
-rwxr-xr-xcore/src/main/res/drawable-hdpi/ic_baseline_question_answer_white_24dp.pngbin187 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_cast_disabled_light.pngbin770 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_cast_light.pngbin975 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_cast_off_light.pngbin867 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_cast_on_0_light.pngbin961 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_cast_on_1_light.pngbin979 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_cast_on_2_light.pngbin976 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_cast_on_light.pngbin982 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_close_light.pngbin493 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_format_list_bulleted_grey600_24dp.pngbin492 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_format_list_bulleted_white_24dp.pngbin459 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_forum_grey600_24dp.pngbin601 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_media_cast_disconnect.pngbin2355 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_pause_light.pngbin191 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_play_light.pngbin562 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_stat_antenna_default.pngbin541 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_stat_authentication.pngbin371 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-ldpi/ic_stat_antenna_default.pngbin259 -> 0 bytes
-rwxr-xr-xcore/src/main/res/drawable-mdpi/ic_baseline_question_answer_white_24dp.pngbin139 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_cast_disabled_light.pngbin536 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_cast_light.pngbin693 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_cast_off_light.pngbin635 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_cast_on_0_light.pngbin684 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_cast_on_1_light.pngbin696 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_cast_on_2_light.pngbin690 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_cast_on_light.pngbin694 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_close_light.pngbin379 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_format_list_bulleted_grey600_24dp.pngbin423 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_format_list_bulleted_white_24dp.pngbin406 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_forum_grey600_24dp.pngbin488 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_media_cast_disconnect.pngbin1467 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_pause_light.pngbin280 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_play_light.pngbin447 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_stat_antenna_default.pngbin349 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_stat_authentication.pngbin244 -> 0 bytes
-rwxr-xr-xcore/src/main/res/drawable-xhdpi/ic_baseline_question_answer_white_24dp.pngbin212 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_cast_disabled_light.pngbin976 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_cast_light.pngbin1328 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_cast_off_light.pngbin1161 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_cast_on_0_light.pngbin1286 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_cast_on_1_light.pngbin1308 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_cast_on_2_light.pngbin1309 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_cast_on_light.pngbin1331 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_close_light.pngbin526 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_grey600_24dp.pngbin504 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_white_24dp.pngbin462 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_forum_grey600_24dp.pngbin602 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_media_cast_disconnect.pngbin4496 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_pause_light.pngbin221 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_play_light.pngbin678 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_stat_antenna_default.pngbin783 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_stat_authentication.pngbin418 -> 0 bytes
-rwxr-xr-xcore/src/main/res/drawable-xxhdpi/ic_baseline_question_answer_white_24dp.pngbin276 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_cast_disabled_light.pngbin1429 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_cast_light.pngbin1952 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_cast_off_light.pngbin1679 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_cast_on_0_light.pngbin1832 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_cast_on_1_light.pngbin1893 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_cast_on_2_light.pngbin1910 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_cast_on_light.pngbin1944 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_close_light.pngbin673 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_grey600_24dp.pngbin631 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_white_24dp.pngbin594 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_forum_grey600_24dp.pngbin766 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_media_cast_disconnect.pngbin7096 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_pause_light.pngbin317 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_play_light.pngbin955 -> 0 bytes
-rwxr-xr-xcore/src/main/res/drawable-xxhdpi/ic_stat_authentication.pngbin848 -> 0 bytes
-rwxr-xr-xcore/src/main/res/drawable-xxxhdpi/ic_baseline_question_answer_white_24db.pngbin310 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_close_light.pngbin805 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_grey600_24dp.pngbin738 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_white_24dp.pngbin666 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_forum_grey600_24dp.pngbin900 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_pause_light.pngbin400 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_play_light.pngbin1190 -> 0 bytes
-rw-r--r--core/src/main/res/drawable/ic_antenna.xml6
-rw-r--r--core/src/main/res/drawable/ic_chat_grey600.xml5
-rw-r--r--core/src/main/res/drawable/ic_chat_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification.pngbin3887 -> 0 bytes
-rw-r--r--core/src/main/res/drawable/ic_notification_cast_off.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_fast_forward.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_fast_rewind.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_key.xml6
-rw-r--r--core/src/main/res/drawable/ic_notification_pause.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_play.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_skip.xml5
-rw-r--r--core/src/main/res/drawable/ic_playback_speed_dark.xml8
-rw-r--r--core/src/main/res/drawable/ic_playback_speed_white.xml8
-rw-r--r--core/src/main/res/drawable/ic_questionmark_grey600.xml5
-rw-r--r--core/src/main/res/drawable/ic_questionmark_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_warning_red.xml5
-rw-r--r--core/src/main/res/drawable/white_circle.xml11
-rw-r--r--core/src/main/res/values-in/strings.xml (renamed from core/src/main/res/values-id/strings.xml)0
-rw-r--r--core/src/main/res/values-land/styles.xml6
-rw-r--r--core/src/main/res/values-large/dimens.xml1
-rw-r--r--core/src/main/res/values-v16/styles.xml8
-rw-r--r--core/src/main/res/values-v19/colors.xml1
-rw-r--r--core/src/main/res/values/arrays.xml10
-rw-r--r--core/src/main/res/values/attrs.xml5
-rw-r--r--core/src/main/res/values/colors.xml6
-rw-r--r--core/src/main/res/values/dimens.xml3
-rw-r--r--core/src/main/res/values/integers.xml1
-rw-r--r--core/src/main/res/values/strings.xml55
-rw-r--r--core/src/main/res/values/styles.xml27
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java13
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java4
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java31
198 files changed, 1401 insertions, 1278 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 258340afc..b6a6ba78e 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,39 +1,103 @@
version: 2
jobs:
- build:
+ test-debug:
docker:
- image: circleci/android:api-28
-
working_directory: ~/AntennaPod
-
environment:
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
_JAVA_OPTIONS: "-Xms256m -Xmx1280m"
-
steps:
- checkout
-
- restore_cache:
keys:
- v1-android-{{ checksum "build.gradle" }}
- # fallback to using the latest cache if no exact match is found
- v1-android-
+ - run:
+ name: Build debug
+ command: ./gradlew assembleDebug -PdisablePreDex
+ - run:
+ name: Execute debug unit tests
+ command: ./gradlew :core:testPlayDebugUnitTest -PdisablePreDex
+ - save_cache:
+ paths:
+ - ~/.android
+ - ~/.gradle
+ - ~/android
+ key: v1-android-{{ checksum "build.gradle" }}
+ test-release:
+ docker:
+ - image: circleci/android:api-28
+ working_directory: ~/AntennaPod
+ environment:
+ GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
+ _JAVA_OPTIONS: "-Xms256m -Xmx1280m"
+ steps:
+ - checkout
+ - restore_cache:
+ keys:
+ - v1-android-{{ checksum "build.gradle" }}
+ - v1-android-
+ - run:
+ name: Create temporary release keystore
+ command: keytool -noprompt -genkey -v -keystore "app/keystore" -alias alias -storepass password -keypass password -keyalg RSA -validity 10 -dname "CN=antennapod.org, OU=dummy, O=dummy, L=dummy, S=dummy, C=US"
- run:
- # To build release, we need to create a temporary keystore that can be used to sign the app
- command: |
- keytool -noprompt -genkey -v -keystore "app/keystore" -alias alias -storepass password -keypass password -keyalg RSA -validity 10 -dname "CN=antennapod.org, OU=dummy, O=dummy, L=dummy, S=dummy, C=US"
- ./gradlew assembleRelease :core:testPlayReleaseUnitTest :app:assemblePlayDebugAndroidTest -PdisablePreDex
- no_output_timeout: 1800
-
- - store_artifacts:
- path: app/build/outputs/apk
- destination: apks
+ name: Build release
+ command: ./gradlew assembleRelease -PdisablePreDex
+ - run:
+ name: Execute release unit tests
+ command: ./gradlew :core:testPlayReleaseUnitTest -PdisablePreDex
+ - save_cache:
+ paths:
+ - ~/.android
+ - ~/.gradle
+ - ~/android
+ key: v1-android-{{ checksum "build.gradle" }}
+ build-androidtest:
+ docker:
+ - image: circleci/android:api-28
+ working_directory: ~/AntennaPod
+ environment:
+ GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
+ _JAVA_OPTIONS: "-Xms256m -Xmx1280m"
+ steps:
+ - checkout
+ - restore_cache:
+ keys:
+ - v1-android-{{ checksum "build.gradle" }}
+ - v1-android-
+ - run:
+ name: Build integration tests
+ command: ./gradlew :app:assemblePlayDebugAndroidTest -PdisablePreDex
- save_cache:
paths:
- ~/.android
- ~/.gradle
- ~/android
key: v1-android-{{ checksum "build.gradle" }}
+
+ checkstyle:
+ docker:
+ - image: circleci/android:api-28
+ working_directory: ~/AntennaPod
+ steps:
+ - checkout
+ - run:
+ name: Checkstyle
+ command: ./gradlew checkstyle
+
+workflows:
+ version: 2
+
+ unit-tests:
+ jobs:
+ - test-debug
+ - test-release
+ - build-androidtest
+
+ static-analysis:
+ jobs:
+ - checkstyle
diff --git a/.tx/config b/.tx/config
index d7356e199..ea1c0ccef 100644
--- a/.tx/config
+++ b/.tx/config
@@ -24,7 +24,7 @@ trans.gl = core/src/main/res/values-gl-rES/strings.xml
trans.he_IL = core/src/main/res/values-iw-rIL/strings.xml
trans.hi_IN = core/src/main/res/values-hi-rIN/strings.xml
trans.hu = core/src/main/res/values-hu/strings.xml
-trans.id = core/src/main/res/values-id/strings.xml
+trans.id = core/src/main/res/values-in/strings.xml
trans.it_IT = core/src/main/res/values-it/strings.xml
trans.is = core/src/main/res/values-is-rIS/strings.xml
trans.ja = core/src/main/res/values-ja/strings.xml
diff --git a/app/build.gradle b/app/build.gradle
index c9d6200d9..3996d7ba5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -173,8 +173,8 @@ dependencies {
implementation 'com.github.mfietz:fyydlin:v0.4.2'
implementation 'com.github.ByteHamster:SearchPreference:v1.3.0'
- implementation "org.awaitility:awaitility:$awaitilityVersion"
+ androidTestImplementation "org.awaitility:awaitility:$awaitilityVersion"
androidTestImplementation 'com.nanohttpd:nanohttpd-webserver:2.1.1'
androidTestImplementation "com.jayway.android.robotium:robotium-solo:$robotiumSoloVersion"
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
diff --git a/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java b/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
index 3d8417bf6..4158fd31c 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
@@ -72,20 +72,6 @@ public class HTTPBin extends NanoHTTPD {
return servedFiles.size() - 1;
}
- /**
- * Removes the file with the given ID from the server.
- *
- * @return True if a file was removed, false otherwise
- */
- public synchronized boolean removeFile(int id) {
- if (id < 0) throw new IllegalArgumentException("ID < 0");
- if (id >= servedFiles.size()) {
- return false;
- } else {
- return servedFiles.remove(id) != null;
- }
- }
-
public synchronized File accessFile(int id) {
if (id < 0 || id >= servedFiles.size()) {
return null;
diff --git a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
index 79d7e02f2..519d4c61b 100644
--- a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
@@ -7,212 +7,9 @@ import android.support.v7.app.AppCompatActivity;
* network.
*/
public abstract class CastEnabledActivity extends AppCompatActivity {
-// implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String TAG = "CastEnabledActivity";
-// protected CastManager castManager;
-// protected SwitchableMediaRouteActionProvider mediaRouteActionProvider;
-// private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
-//
-// @Override
-// protected void onCreate(Bundle savedInstanceState) {
-// super.onCreate(savedInstanceState);
-//
-// PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).
-// registerOnSharedPreferenceChangeListener(this);
-//
-// castManager = CastManager.getInstance();
-// castManager.addCastConsumer(castConsumer);
-// castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
-// onCastConnectionChanged(castManager.isConnected());
-// }
-//
-// @Override
-// protected void onDestroy() {
-// PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
-// .unregisterOnSharedPreferenceChangeListener(this);
-// castManager.removeCastConsumer(castConsumer);
-// super.onDestroy();
-// }
-//
-// @Override
-// @CallSuper
-// public boolean onCreateOptionsMenu(Menu menu) {
-// super.onCreateOptionsMenu(menu);
-// getMenuInflater().inflate(R.menu.cast_enabled, menu);
-// castButtonVisibilityManager.setMenu(menu);
-// return true;
-// }
-//
-// @Override
-// @CallSuper
-// public boolean onPrepareOptionsMenu(Menu menu) {
-// super.onPrepareOptionsMenu(menu);
-// mediaRouteActionProvider = castManager
-// .addMediaRouterButton(menu.findItem(R.id.media_route_menu_item));
-// mediaRouteActionProvider.setEnabled(castButtonVisibilityManager.shouldEnable());
-// return true;
-// }
-//
-// @Override
-// protected void onResume() {
-// super.onResume();
-// castButtonVisibilityManager.setResumed(true);
-// }
-//
-// @Override
-// protected void onPause() {
-// super.onPause();
-// castButtonVisibilityManager.setResumed(false);
-// }
-//
-// @Override
-// public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-// if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
-// boolean newValue = UserPreferences.isCastEnabled();
-// Log.d(TAG, "onSharedPreferenceChanged(), isCastEnabled set to " + newValue);
-// castButtonVisibilityManager.setPrefEnabled(newValue);
-// // PlaybackService has its own listener, so if it's active we don't have to take action here.
-// if (!newValue && !PlaybackService.isRunning) {
-// CastManager.getInstance().disconnect();
-// }
-// }
-// }
-//
-// CastConsumer castConsumer = new DefaultCastConsumer() {
-// @Override
-// public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
-// onCastConnectionChanged(true);
-// }
-//
-// @Override
-// public void onDisconnected() {
-// onCastConnectionChanged(false);
-// }
-// };
-//
-// private void onCastConnectionChanged(boolean connected) {
-// if (connected) {
-// castButtonVisibilityManager.onConnected();
-// setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
-// } else {
-// castButtonVisibilityManager.onDisconnected();
-// setVolumeControlStream(AudioManager.STREAM_MUSIC);
-// }
-// }
-//
-// /**
-// * Should be called by any activity or fragment for which the cast button should be shown.
-// *
-// * @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
-// */
public final void requestCastButton(int showAsAction) {
// no-op
}
-
-// private class CastButtonVisibilityManager {
-// private volatile boolean prefEnabled = false;
-// private volatile boolean viewRequested = false;
-// private volatile boolean resumed = false;
-// private volatile boolean connected = false;
-// private volatile int showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
-// private Menu menu;
-//
-// public synchronized void setPrefEnabled(boolean newValue) {
-// if (prefEnabled != newValue && resumed && (viewRequested || connected)) {
-// if (newValue) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// prefEnabled = newValue;
-// if (mediaRouteActionProvider != null) {
-// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
-// }
-// }
-//
-// public synchronized void setResumed(boolean newValue) {
-// if (resumed == newValue) {
-// Log.e(TAG, "resumed should never change to the same value");
-// return;
-// }
-// resumed = newValue;
-// if (prefEnabled && (viewRequested || connected)) {
-// if (resumed) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// }
-//
-// public synchronized void setViewRequested(boolean newValue) {
-// if (viewRequested != newValue && resumed && prefEnabled && !connected) {
-// if (newValue) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// viewRequested = newValue;
-// if (mediaRouteActionProvider != null) {
-// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
-// }
-// }
-//
-// public synchronized void setConnected(boolean newValue) {
-// if (connected != newValue && resumed && prefEnabled && !prefEnabled) {
-// if (newValue) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// connected = newValue;
-// if (mediaRouteActionProvider != null) {
-// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
-// }
-// }
-//
-// public synchronized boolean shouldEnable() {
-// return prefEnabled && viewRequested;
-// }
-//
-// public void setMenu(Menu menu) {
-// setViewRequested(false);
-// showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
-// this.menu = menu;
-// setShowAsAction();
-// }
-//
-// public void requestCastButton(int showAsAction) {
-// setViewRequested(true);
-// this.showAsAction = showAsAction;
-// setShowAsAction();
-// }
-//
-// public void onConnected() {
-// setConnected(true);
-// setShowAsAction();
-// }
-//
-// public void onDisconnected() {
-// setConnected(false);
-// setShowAsAction();
-// }
-//
-// private void setShowAsAction() {
-// if (menu == null) {
-// Log.d(TAG, "setShowAsAction() without a menu");
-// return;
-// }
-// MenuItem item = menu.findItem(R.id.media_route_menu_item);
-// if (item == null) {
-// Log.e(TAG, "setShowAsAction(), but cast button not inflated");
-// return;
-// }
-// MenuItemCompat.setShowAsAction(item, connected? MenuItem.SHOW_AS_ACTION_ALWAYS : showAsAction);
-// }
-// }
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 73af654e9..c2c6f53c5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -36,7 +36,7 @@
android:usesCleartextTraffic="true"
android:logo="@mipmap/ic_launcher">
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
- android:resource="@drawable/ic_notification" />
+ android:resource="@drawable/ic_antenna" />
<meta-data
android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI3a05VToCTlqBymJrbFGaKQMvF-bBAuLsOdavBA"/>
@@ -209,6 +209,13 @@
android:name=".activity.OpmlFeedChooserActivity"
android:label="@string/opml_import_label">
</activity>
+ <activity
+ android:name=".activity.BugReportActivity"
+ android:label="@string/bug_report_title">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
+ </activity>
<activity
android:name=".activity.VideoplayerActivity"
diff --git a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
index ea2166674..061ea9ae2 100644
--- a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
+++ b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
@@ -9,6 +9,9 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -32,13 +35,7 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler {
PrintWriter out = null;
try {
out = new PrintWriter(new FileWriter(path));
- out.println("[ Environment ]");
- out.println("Android version: " + Build.VERSION.RELEASE);
- out.println("OS version: " + System.getProperty("os.version"));
- out.println("AntennaPod version: " + BuildConfig.VERSION_NAME);
- out.println("Model: " + Build.MODEL);
- out.println("Device: " + Build.DEVICE);
- out.println("Product: " + Build.PRODUCT);
+ out.println(getSystemInfo());
out.println();
out.println("[ StackTrace ]");
ex.printStackTrace(out);
@@ -49,4 +46,15 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler {
}
defaultHandler.uncaughtException(thread, ex);
}
+
+ public static String getSystemInfo() {
+ return "[ Environment ]" +
+ "\nAndroid version: " + Build.VERSION.RELEASE +
+ "\nOS version: " + System.getProperty("os.version") +
+ "\nAntennaPod version: " + BuildConfig.VERSION_NAME +
+ "\nModel: " + Build.MODEL +
+ "\nDevice: " + Build.DEVICE +
+ "\nProduct: " + Build.PRODUCT +
+ "\nTime: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date()) + "\n";
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
index 1bcdada44..821e2f347 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
@@ -15,6 +15,7 @@ import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.LinearLayout;
+import de.danoeh.antennapod.core.util.IntentUtils;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
@@ -57,8 +58,7 @@ public class AboutActivity extends AppCompatActivity {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("http")) {
- Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- startActivity(browserIntent);
+ IntentUtils.openInBrowser(AboutActivity.this, url);
return true;
} else {
url = url.replace("file:///android_asset/", "");
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
index 2321a3602..07c970197 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -60,11 +60,13 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
}
if (controller == null) {
butPlaybackSpeed.setVisibility(View.GONE);
+ txtvPlaybackSpeed.setVisibility(View.GONE);
return;
}
updatePlaybackSpeedButtonText();
ViewCompat.setAlpha(butPlaybackSpeed, controller.canSetPlaybackSpeed() ? 1.0f : 0.5f);
butPlaybackSpeed.setVisibility(View.VISIBLE);
+ txtvPlaybackSpeed.setVisibility(View.VISIBLE);
}
@Override
@@ -74,14 +76,15 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
}
if (controller == null) {
butPlaybackSpeed.setVisibility(View.GONE);
+ txtvPlaybackSpeed.setVisibility(View.GONE);
return;
}
float speed = 1.0f;
if(controller.canSetPlaybackSpeed()) {
speed = UserPreferences.getPlaybackSpeed();
}
- String speedStr = new DecimalFormat("0.00x").format(speed);
- butPlaybackSpeed.setText(speedStr);
+ String speedStr = new DecimalFormat("0.00").format(speed);
+ txtvPlaybackSpeed.setText(speedStr);
}
@Override
@@ -136,6 +139,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
return true;
});
butPlaybackSpeed.setVisibility(View.VISIBLE);
+ txtvPlaybackSpeed.setVisibility(View.VISIBLE);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
new file mode 100644
index 000000000..7df973f9b
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
@@ -0,0 +1,55 @@
+package de.danoeh.antennapod.activity;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.AppCompatActivity;
+import android.widget.TextView;
+import de.danoeh.antennapod.CrashReportWriter;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+/**
+ * Displays the 'crash report' screen
+ */
+public class BugReportActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ getSupportActionBar().setDisplayShowHomeEnabled(true);
+ setContentView(R.layout.bug_report);
+
+ TextView crashDetailsText = findViewById(R.id.crash_report_logs);
+
+ try {
+ File crashFile = CrashReportWriter.getFile();
+ String crashReportContent = IOUtils.toString(new FileInputStream(crashFile), Charset.forName("UTF-8"));
+ crashDetailsText.setText(crashReportContent);
+ } catch (IOException e) {
+ e.printStackTrace();
+ crashDetailsText.setText("No crash report recorded\n" + CrashReportWriter.getSystemInfo());
+ }
+
+ findViewById(R.id.btn_open_bug_tracker).setOnClickListener(v -> {
+ IntentUtils.openInBrowser(BugReportActivity.this, "https://github.com/AntennaPod/AntennaPod/issues");
+ });
+
+ findViewById(R.id.btn_copy_log).setOnClickListener(v -> {
+ ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText(getString(R.string.bug_report_title), crashDetailsText.getText());
+ clipboard.setPrimaryClip(clip);
+ Snackbar.make(findViewById(android.R.id.content), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show();
+ });
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
index 871e9c279..c60c7b769 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
@@ -49,6 +49,7 @@ public class CastplayerActivity extends MediaplayerInfoActivity {
super.setupGUI();
if (butPlaybackSpeed != null) {
butPlaybackSpeed.setVisibility(View.GONE);
+ txtvPlaybackSpeed.setVisibility(View.GONE);
}
// if (butCastDisconnect != null) {
// butCastDisconnect.setOnClickListener(v -> castManager.disconnect());
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 339ce01c2..08686bd02 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.activity;
import android.annotation.TargetApi;
import android.app.ProgressDialog;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -10,6 +11,7 @@ import android.database.DataSetObserver;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
@@ -32,9 +34,11 @@ import android.widget.Toast;
import com.bumptech.glide.Glide;
-import de.danoeh.antennapod.preferences.PreferenceUpgrader;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
import java.util.List;
@@ -67,13 +71,11 @@ import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
+import de.danoeh.antennapod.preferences.PreferenceUpgrader;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
/**
* The activity that is shown when the user launches the app.
@@ -93,7 +95,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
public static final String EXTRA_NAV_INDEX = "nav_index";
public static final String EXTRA_FRAGMENT_TAG = "fragment_tag";
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
- public static final String EXTRA_FEED_ID = "fragment_feed_id";
+ private static final String EXTRA_FEED_ID = "fragment_feed_id";
private static final String SAVE_BACKSTACK_COUNT = "backstackCount";
private static final String SAVE_TITLE = "title";
@@ -127,6 +129,14 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private long lastBackButtonPressTime = 0;
+ @NonNull
+ public static Intent getIntentToOpenFeed(@NonNull Context context, long feedId) {
+ Intent intent = new Intent(context.getApplicationContext(), MainActivity.class);
+ intent.putExtra(MainActivity.EXTRA_FEED_ID, feedId);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ return intent;
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getNoTitleTheme());
@@ -240,7 +250,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(PREF_IS_FIRST_LAUNCH, false);
- edit.commit();
+ edit.apply();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index 52497a27f..91c3796af 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -322,6 +322,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
Playable media = controller.getMedia();
boolean isFeedMedia = media != null && (media instanceof FeedMedia);
+ menu.findItem(R.id.open_feed_item).setVisible(isFeedMedia); // FeedMedia implies it belongs to a Feed
+
boolean hasWebsiteLink = ( getWebsiteLinkWithFallback(media) != null );
menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink);
@@ -389,29 +391,24 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
return true;
} else {
if (media != null) {
+ final @Nullable FeedItem feedItem = getFeedItem(media); // some options option requires FeedItem
switch (item.getItemId()) {
case R.id.add_to_favorites_item:
- if(media instanceof FeedMedia) {
- FeedItem feedItem = ((FeedMedia)media).getItem();
- if(feedItem != null) {
- DBWriter.addFavoriteItem(feedItem);
- isFavorite = true;
- invalidateOptionsMenu();
- Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT)
- .show();
- }
+ if (feedItem != null) {
+ DBWriter.addFavoriteItem(feedItem);
+ isFavorite = true;
+ invalidateOptionsMenu();
+ Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT)
+ .show();
}
break;
case R.id.remove_from_favorites_item:
- if(media instanceof FeedMedia) {
- FeedItem feedItem = ((FeedMedia)media).getItem();
- if(feedItem != null) {
- DBWriter.removeFavoriteItem(feedItem);
- isFavorite = false;
- invalidateOptionsMenu();
- Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT)
- .show();
- }
+ if (feedItem != null) {
+ DBWriter.removeFavoriteItem(feedItem);
+ isFavorite = false;
+ invalidateOptionsMenu();
+ Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT)
+ .show();
}
break;
case R.id.disable_sleeptimer_item:
@@ -448,28 +445,33 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(isPlayingVideo);
dialog.show(getSupportFragmentManager(), "playback_controls");
break;
+ case R.id.open_feed_item:
+ if (feedItem != null) {
+ Intent intent = MainActivity.getIntentToOpenFeed(this, feedItem.getFeedId());
+ startActivity(intent);
+ }
+ break;
case R.id.visit_website_item:
- Uri uri = Uri.parse(getWebsiteLinkWithFallback(media));
- startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ IntentUtils.openInBrowser(MediaplayerActivity.this, getWebsiteLinkWithFallback(media));
break;
case R.id.share_link_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem());
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemLink(this, feedItem);
}
break;
case R.id.share_download_url_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem());
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemDownloadLink(this, feedItem);
}
break;
case R.id.share_link_with_position_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem(), true);
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemLink(this, feedItem, true);
}
break;
case R.id.share_download_url_with_position_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true);
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemDownloadLink(this, feedItem, true);
}
break;
case R.id.share_file:
@@ -635,7 +637,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
}
- static public void showSkipPreference(Activity activity, SkipDirection direction) {
+ public static void showSkipPreference(Activity activity, SkipDirection direction) {
int checked = 0;
int skipSecs = direction.getPrefSkipSeconds();
final int[] values = activity.getResources().getIntArray(R.array.seek_delta_values);
@@ -813,11 +815,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
private void checkFavorite() {
- Playable playable = controller.getMedia();
- if (!(playable instanceof FeedMedia)) {
- return;
- }
- FeedItem feedItem = ((FeedMedia) playable).getItem();
+ FeedItem feedItem = getFeedItem(controller.getMedia());
if (feedItem == null) {
return;
}
@@ -872,4 +870,13 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
}
}
+
+ @Nullable
+ private static FeedItem getFeedItem(@Nullable Playable playable) {
+ if ((playable != null) && (playable instanceof FeedMedia)) {
+ return ((FeedMedia)playable).getItem();
+ } else {
+ return null;
+ }
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
index 4fec1cfc5..5210bdc06 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
@@ -26,6 +26,7 @@ import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListView;
+import android.widget.TextView;
import android.widget.Toast;
import com.viewpagerindicator.CirclePageIndicator;
@@ -92,7 +93,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
NavListAdapter.SUBSCRIPTION_LIST_TAG
};
- Button butPlaybackSpeed;
+ ImageButton butPlaybackSpeed;
+ TextView txtvPlaybackSpeed;
ImageButton butCastDisconnect;
private DrawerLayout drawerLayout;
private NavListAdapter navAdapter;
@@ -258,6 +260,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
});
butPlaybackSpeed = findViewById(R.id.butPlaybackSpeed);
+ txtvPlaybackSpeed = findViewById(R.id.txtvPlaybackSpeed);
butCastDisconnect = findViewById(R.id.butCastDisconnect);
pager = findViewById(R.id.pager);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index ea7687bc9..c38eb7d47 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -32,7 +32,6 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
-import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -55,6 +54,7 @@ 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.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
@@ -442,11 +442,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
subscribeButton.setOnClickListener(v -> {
if(feedInFeedlist(feed)) {
- Intent intent = new Intent(OnlineFeedViewActivity.this, MainActivity.class);
// feed.getId() is always 0, we have to retrieve the id from the feed list from
// the database
- intent.putExtra(MainActivity.EXTRA_FEED_ID, getFeedId(feed));
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ Intent intent = MainActivity.getIntentToOpenFeed(this, getFeedId(feed));
startActivity(intent);
} else {
Feed f = new Feed(selectedDownloadUrl, null, feed.getTitle());
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
index 2eff33339..85b5e3985 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -262,7 +262,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
FeedItem item = itemAccess.getItem(getAdapterPosition());
MenuInflater inflater = mainActivityRef.get().getMenuInflater();
- inflater.inflate(R.menu.allepisodes_context, menu);
+ inflater.inflate(R.menu.feeditemlist_context, menu);
if (item != null) {
menu.setHeaderTitle(item.getTitle());
@@ -277,9 +277,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
item1.setVisible(visible);
}
};
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null);
-
- contextMenuInterface.setItemVisibility(R.id.remove_new_flag_item, item.isNew());
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
index c10bb7638..8d469c7a6 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
+import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -51,6 +52,17 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
.replaceAll("\\s+", " ")
.trim();
holder.description.setText(description);
+
+ final int MAX_LINES_COLLAPSED = 3;
+ holder.description.setMaxLines(MAX_LINES_COLLAPSED);
+ holder.description.setOnClickListener(v -> {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+ && holder.description.getMaxLines() > MAX_LINES_COLLAPSED) {
+ holder.description.setMaxLines(MAX_LINES_COLLAPSED);
+ } else {
+ holder.description.setMaxLines(2000);
+ }
+ });
}
return convertView;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
index 382abfb32..30057dde3 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.adapter;
-import android.content.Context;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
@@ -169,7 +168,8 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
FeedItem item = itemAccess.getItem(getAdapterPosition());
MenuInflater inflater = mainActivity.get().getMenuInflater();
- inflater.inflate(R.menu.queue_context, menu);
+ inflater.inflate(R.menu.queue_context, menu); // queue-specific menu items
+ inflater.inflate(R.menu.feeditemlist_context, menu); // generic menu items for item feeds
if (item != null) {
menu.setHeaderTitle(item.getTitle());
@@ -184,7 +184,18 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
item1.setVisible(visible);
}
};
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, itemAccess.getQueueIds());
+
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item,
+ R.id.skip_episode_item); // Skip Episode is not useful in Queue, so hide it.
+ // Queue-specific menu preparation
+ final boolean keepSorted = UserPreferences.isQueueKeepSorted();
+ final LongList queueAccess = itemAccess.getQueueIds();
+ if (queueAccess.size() == 0 || queueAccess.get(0) == item.getId() || keepSorted) {
+ contextMenuInterface.setItemVisibility(R.id.move_to_top_item, false);
+ }
+ if (queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == item.getId() || keepSorted) {
+ contextMenuInterface.setItemVisibility(R.id.move_to_bottom_item, false);
+ }
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
index da5ebf6e1..6dbeccfc9 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
@@ -20,12 +20,12 @@ public abstract class ItemActionButton {
}
@StringRes
- abstract public int getLabel();
+ public abstract int getLabel();
@AttrRes
- abstract public int getDrawable();
+ public abstract int getDrawable();
- abstract public void onClick(Context context);
+ public abstract void onClick(Context context);
public int getVisibility() {
return View.VISIBLE;
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
new file mode 100644
index 000000000..0e9572a82
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
@@ -0,0 +1,72 @@
+package de.danoeh.antennapod.asynctask;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.v4.provider.DocumentFile;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import de.danoeh.antennapod.core.export.ExportWriter;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.util.LangUtils;
+import io.reactivex.Observable;
+
+/**
+ * Writes an OPML file into the user selected export directory in the background.
+ */
+public class DocumentFileExportWorker {
+
+ private final @NonNull ExportWriter exportWriter;
+ private @NonNull Context context;
+ private @NonNull Uri outputFileUri;
+
+ public DocumentFileExportWorker(@NonNull ExportWriter exportWriter, @NonNull Context context, @NonNull Uri outputFileUri) {
+ this.exportWriter = exportWriter;
+ this.context = context;
+ this.outputFileUri = outputFileUri;
+ }
+
+ public Observable<DocumentFile> exportObservable() {
+ DocumentFile output = DocumentFile.fromSingleUri(context, outputFileUri);
+ return Observable.create(subscriber -> {
+ OutputStream outputStream = null;
+ OutputStreamWriter writer = null;
+ try {
+ Uri uri = output.getUri();
+ if (uri == null) {
+ throw new FileNotFoundException("Export file not found.");
+ }
+ outputStream = context.getContentResolver().openOutputStream(uri);
+ if (outputStream == null) {
+ throw new IOException();
+ }
+ writer = new OutputStreamWriter(outputStream, LangUtils.UTF_8);
+ exportWriter.writeDocument(DBReader.getFeedList(), writer);
+ subscriber.onNext(output);
+ } catch (IOException e) {
+ subscriber.onError(e);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ subscriber.onError(e);
+ }
+ }
+ if (outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ subscriber.onError(e);
+ }
+ }
+ subscriber.onComplete();
+ }
+ });
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
index eb70d8e0b..c3f5d898c 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
@@ -35,6 +35,6 @@ public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
@Override
public int getNotificationIconResource(Context context) {
- return R.drawable.ic_stat_antenna_default;
+ return R.drawable.ic_antenna;
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
index c185a5557..4cfa7e870 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
@@ -9,7 +9,7 @@ import de.danoeh.antennapod.adapter.DataFolderAdapter;
public class ChooseDataFolderDialog {
- public static abstract class RunnableWithString implements Runnable {
+ public abstract static class RunnableWithString implements Runnable {
public RunnableWithString() {
super();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
index 24656ed29..5969963f2 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
@@ -15,6 +15,7 @@ import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.util.IntentUtils;
public class RatingDialog {
@@ -59,14 +60,10 @@ public class RatingDialog {
private static void rateNow() {
Context context = mContext.get();
- if(context == null) {
+ if (context == null) {
return;
}
- final String appPackage = "de.danoeh.antennapod";
- final Uri uri = Uri.parse("https://play.google.com/store/apps/details?id=" + appPackage);
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
+ IntentUtils.openInBrowser(context, "https://play.google.com/store/apps/details?id=de.danoeh.antennapod");
saveRated();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
index 1cedb5a91..c0c23685a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -4,9 +4,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.os.Handler;
import android.support.annotation.NonNull;
-import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.LinearLayoutManager;
@@ -23,8 +21,16 @@ import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-import com.joanzapata.iconify.Iconify;
+
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.ArrayList;
+import java.util.List;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
@@ -33,21 +39,16 @@ import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
-import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedItemFilter;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
-import de.danoeh.antennapod.dialog.FilterDialog;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.view.EmptyViewHandler;
@@ -55,13 +56,6 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
/**
* Shows unread or recently published episodes
@@ -149,7 +143,7 @@ public abstract class EpisodesListFragment extends Fragment {
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(PREF_SCROLL_POSITION, firstItem);
editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
- editor.commit();
+ editor.apply();
}
private void restoreScrollPosition() {
@@ -162,7 +156,7 @@ public abstract class EpisodesListFragment extends Fragment {
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(PREF_SCROLL_POSITION, 0);
editor.putFloat(PREF_SCROLL_OFFSET, 0.0f);
- editor.commit();
+ editor.apply();
}
}
@@ -202,10 +196,7 @@ public abstract class EpisodesListFragment extends Fragment {
if (!super.onOptionsItemSelected(item)) {
switch (item.getItemId()) {
case R.id.refresh_item:
- List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
- if (feeds != null) {
- DBTasks.refreshAllFeeds(getActivity(), feeds);
- }
+ AutoUpdateManager.runImmediate(requireContext());
return true;
case R.id.mark_all_read_item:
ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(),
@@ -262,17 +253,7 @@ public abstract class EpisodesListFragment extends Fragment {
}
FeedItem selectedItem = listAdapter.getSelectedItem();
- // Remove new flag contains UI logic specific to All/New/FavoriteSegments,
- // e.g., Undo with Snackbar,
- // and is handled by this class rather than the generic FeedItemMenuHandler
- // Undo is useful for Remove new flag, given there is no UI to undo it otherwise,
- // i.e., there is context menu item for Mark as new
- if (R.id.remove_new_flag_item == item.getItemId()) {
- removeNewFlagWithUndo(selectedItem);
- return true;
- }
-
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
+ return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem);
}
@NonNull
@@ -412,7 +393,7 @@ public abstract class EpisodesListFragment extends Fragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) {
+ if (isMenuInvalidationAllowed && event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
requireActivity().invalidateOptionsMenu();
}
if (update.mediaIds.length > 0) {
@@ -453,36 +434,4 @@ public abstract class EpisodesListFragment extends Fragment {
@NonNull
protected abstract List<FeedItem> loadData();
-
- void removeNewFlagWithUndo(FeedItem item) {
- if (item == null) {
- return;
- }
-
- Log.d(TAG, "removeNewFlagWithUndo(" + item.getId() + ")");
- if (disposable != null) {
- disposable.dispose();
- }
- // we're marking it as unplayed since the user didn't actually play it
- // but they don't want it considered 'NEW' anymore
- DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
-
- final Handler h = new Handler(getActivity().getMainLooper());
- final Runnable r = () -> {
- FeedMedia media = item.getMedia();
- if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
- DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
- }
- };
-
- Snackbar snackbar = Snackbar.make(getView(), getString(R.string.removed_new_flag_label),
- Snackbar.LENGTH_LONG);
- snackbar.setAction(getString(R.string.undo), v -> {
- DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
- // don't forget to cancel the thing that's going to remove the media
- h.removeCallbacks(r);
- });
- snackbar.show();
- h.postDelayed(r, (int) Math.ceil(snackbar.getDuration() * 1.05f));
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
index 4e4b40096..1aad74466 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -323,7 +323,7 @@ public class FeedItemlistFragment extends ListFragment {
contextMenu = menu;
lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null);
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item);
}
@Override
@@ -340,7 +340,7 @@ public class FeedItemlistFragment extends ListFragment {
return super.onContextItemSelected(item);
}
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
+ return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem);
}
@Override
@@ -390,10 +390,10 @@ public class FeedItemlistFragment extends ListFragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (isUpdatingFeed != event.update.feedIds.length > 0) {
+ if (event.hasChangedFeedUpdateStatus(isUpdatingFeed)) {
updateProgressBarVisibility();
}
- if(adapter != null && update.mediaIds.length > 0) {
+ if (adapter != null && update.mediaIds.length > 0) {
adapter.notifyDataSetChanged();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index 5cf2c5eeb..bfca90b2a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -96,13 +96,7 @@ public class ItemDescriptionFragment extends Fragment {
if (Timeline.isTimecodeLink(url)) {
onTimecodeLinkSelected(url);
} else {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException e) {
- e.printStackTrace();
- return true;
- }
+ IntentUtils.openInBrowser(getContext(), url);
}
return true;
}
@@ -159,11 +153,7 @@ public class ItemDescriptionFragment extends Fragment {
if (selectedURL != null) {
switch (item.getItemId()) {
case R.id.open_in_browser_item:
- Uri uri = Uri.parse(selectedURL);
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(getActivity(), intent)) {
- getActivity().startActivity(intent);
- }
+ IntentUtils.openInBrowser(getContext(), selectedURL);
break;
case R.id.share_url_item:
ShareUtils.shareLink(getActivity(), selectedURL);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index 3a48c5431..9395b0994 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -55,7 +55,6 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.Downloader;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -211,10 +210,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
webvDescription.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- if(IntentUtils.isCallable(getActivity(), intent)) {
- startActivity(intent);
- }
+ IntentUtils.openInBrowser(getContext(), url);
return true;
}
});
@@ -336,10 +332,10 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
inflater.inflate(R.menu.feeditem_options, menu);
popupMenu = menu;
if (item.hasMedia()) {
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null);
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item);
} else {
// these are already available via button1 and button2
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null,
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item,
R.id.mark_read_item, R.id.visit_website_item);
}
}
@@ -351,7 +347,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
openPodcast();
return true;
default:
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item);
+ return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item);
}
}
@@ -485,11 +481,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
if (selectedURL != null) {
switch (item.getItemId()) {
case R.id.open_in_browser_item:
- Uri uri = Uri.parse(selectedURL);
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(getActivity(), intent)) {
- getActivity().startActivity(intent);
- }
+ IntentUtils.openInBrowser(getContext(), selectedURL);
break;
case R.id.share_url_item:
ShareUtils.shareLink(getActivity(), selectedURL);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
index adae4f2a5..07667118d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -6,7 +6,6 @@ import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -16,6 +15,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
* Like 'EpisodesFragment' except that it only shows new episodes and
@@ -63,7 +63,7 @@ public class NewEpisodesFragment extends EpisodesListFragment {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder) viewHolder;
- removeNewFlagWithUndo(holder.getFeedItem());
+ FeedItemMenuHandler.removeNewFlagWithUndo(NewEpisodesFragment.this, holder.getFeedItem());
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index 9763f8f2d..a1f5dca18 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -7,6 +7,7 @@ import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
+import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
@@ -19,11 +20,16 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
import java.util.List;
import de.danoeh.antennapod.R;
@@ -35,14 +41,12 @@ import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
-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.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
@@ -50,18 +54,15 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.QueueSorter;
import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-
import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REMOVE_FROM_QUEUE;
@@ -91,10 +92,12 @@ public class QueueFragment extends Fragment {
private static final String PREFS = "QueueFragment";
private static final String PREF_SCROLL_POSITION = "scroll_position";
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
+ private static final String PREF_SHOW_LOCK_WARNING = "show_lock_warning";
private Disposable disposable;
private LinearLayoutManager layoutManager;
private ItemTouchHelper itemTouchHelper;
+ private SharedPreferences prefs;
@Override
@@ -102,6 +105,7 @@ public class QueueFragment extends Fragment {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
+ prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
}
@Override
@@ -196,8 +200,8 @@ public class QueueFragment extends Fragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (isUpdatingFeeds != update.feedIds.length > 0) {
- getActivity().supportInvalidateOptionsMenu();
+ if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
+ getActivity().invalidateOptionsMenu();
}
if (recyclerAdapter != null && update.mediaIds.length > 0) {
for (long mediaId : update.mediaIds) {
@@ -219,15 +223,13 @@ public class QueueFragment extends Fragment {
topOffset = firstItemView.getTop();
}
- SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(PREF_SCROLL_POSITION, firstItem);
- editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
- editor.commit();
+ prefs.edit()
+ .putInt(PREF_SCROLL_POSITION, firstItem)
+ .putFloat(PREF_SCROLL_OFFSET, topOffset)
+ .apply();
}
private void restoreScrollPosition() {
- SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
if (position > 0 || offset > 0) {
@@ -299,25 +301,10 @@ public class QueueFragment extends Fragment {
if (!super.onOptionsItemSelected(item)) {
switch (item.getItemId()) {
case R.id.queue_lock:
- boolean newLockState = !UserPreferences.isQueueLocked();
- UserPreferences.setQueueLocked(newLockState);
- getActivity().supportInvalidateOptionsMenu();
- if (recyclerAdapter != null) {
- recyclerAdapter.setLocked(newLockState);
- }
- if (newLockState) {
- Snackbar.make(getActivity().findViewById(R.id.content), R.string
- .queue_locked, Snackbar.LENGTH_SHORT).show();
- } else {
- Snackbar.make(getActivity().findViewById(R.id.content), R.string
- .queue_unlocked, Snackbar.LENGTH_SHORT).show();
- }
+ toggleQueueLock();
return true;
case R.id.refresh_item:
- List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
- if (feeds != null) {
- DBTasks.refreshAllFeeds(getActivity(), feeds);
- }
+ AutoUpdateManager.runImmediate(requireContext());
return true;
case R.id.clear_queue:
// make sure the user really wants to clear the queue
@@ -394,6 +381,48 @@ public class QueueFragment extends Fragment {
}
}
+ private void toggleQueueLock() {
+ boolean isLocked = UserPreferences.isQueueLocked();
+ if (isLocked) {
+ setQueueLocked(false);
+ } else {
+ boolean shouldShowLockWarning = prefs.getBoolean(PREF_SHOW_LOCK_WARNING, true);
+ if (!shouldShowLockWarning) {
+ setQueueLocked(true);
+ } else {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setTitle(R.string.lock_queue);
+ builder.setMessage(R.string.queue_lock_warning);
+
+ View view = View.inflate(getContext(), R.layout.checkbox_do_not_show_again, null);
+ CheckBox checkDoNotShowAgain = view.findViewById(R.id.checkbox_do_not_show_again);
+ builder.setView(view);
+
+ builder.setPositiveButton(R.string.lock_queue, (dialog, which) -> {
+ prefs.edit().putBoolean(PREF_SHOW_LOCK_WARNING, !checkDoNotShowAgain.isChecked()).apply();
+ setQueueLocked(true);
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.show();
+ }
+ }
+ }
+
+ private void setQueueLocked(boolean locked) {
+ UserPreferences.setQueueLocked(locked);
+ getActivity().supportInvalidateOptionsMenu();
+ if (recyclerAdapter != null) {
+ recyclerAdapter.setLocked(locked);
+ }
+ if (locked) {
+ Snackbar.make(getActivity().findViewById(R.id.content), R.string
+ .queue_locked, Snackbar.LENGTH_SHORT).show();
+ } else {
+ Snackbar.make(getActivity().findViewById(R.id.content), R.string
+ .queue_unlocked, Snackbar.LENGTH_SHORT).show();
+ }
+ }
+
/**
* This method is called if the user clicks on a sort order menu item.
*
@@ -430,7 +459,7 @@ public class QueueFragment extends Fragment {
DBWriter.moveQueueItemToBottom(selectedItem.getId(), true);
return true;
default:
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
+ return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem);
}
}
@@ -606,7 +635,7 @@ public class QueueFragment extends Fragment {
/ playbackSpeed);
}
}
- info += " \u2022 ";
+ info += " • ";
info += getString(R.string.time_left_label);
info += Converter.getDurationStringLocalized(getActivity(), timeLeft);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
index 15c6052a9..ed315050b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -25,19 +25,28 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SubscriptionsAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
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.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
+import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Fragment for displaying feed subscriptions
@@ -56,6 +65,7 @@ public class SubscriptionFragment extends Fragment {
private SubscriptionsAdapter subscriptionAdapter;
private int mPosition = -1;
+ private boolean isUpdatingFeeds = false;
private Disposable disposable;
private SharedPreferences prefs;
@@ -89,6 +99,8 @@ public class SubscriptionFragment extends Fragment {
menu.findItem(R.id.subscription_num_columns_3).setChecked(columns == 3);
menu.findItem(R.id.subscription_num_columns_4).setChecked(columns == 4);
menu.findItem(R.id.subscription_num_columns_5).setChecked(columns == 5);
+
+ isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
@Override
@@ -97,6 +109,9 @@ public class SubscriptionFragment extends Fragment {
return true;
}
switch (item.getItemId()) {
+ case R.id.refresh_item:
+ AutoUpdateManager.runImmediate(requireContext());
+ return true;
case R.id.subscription_num_columns_2:
setColumnNumber(2);
return true;
@@ -136,6 +151,7 @@ public class SubscriptionFragment extends Fragment {
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
loadSubscriptions();
}
@@ -143,6 +159,7 @@ public class SubscriptionFragment extends Fragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
+ EventBus.getDefault().unregister(this);
if(disposable != null) {
disposable.dispose();
}
@@ -278,6 +295,17 @@ public class SubscriptionFragment extends Fragment {
}
};
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
+ getActivity().invalidateOptionsMenu();
+ }
+ }
+
+ private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
+ () -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
+
private final SubscriptionsAdapter.ItemAccess itemAccess = new SubscriptionsAdapter.ItemAccess() {
@Override
public int getCount() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
index a04615a00..d0c209326 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
@@ -1,29 +1,41 @@
package de.danoeh.antennapod.fragment.preferences;
+import android.Manifest;
import android.app.Activity;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.preference.PreferenceScreen;
import android.util.Log;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
private static final String TAG = "AutoDnldPrefFragment";
+
+ private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
+ private static final String PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT = "prefAutoDownloadWifiFilterAndroid10PermissionPrompt";
+
private CheckBoxPreference[] selectedNetworks;
+ private Preference prefPermissionRequestPromptOnAndroid10 = null;
+
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences_autodownload);
@@ -175,10 +187,65 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
}
private void setSelectedNetworksEnabled(boolean b) {
+ if (showPermissionRequestPromptOnAndroid10IfNeeded(b)) {
+ return;
+ }
+
if (selectedNetworks != null) {
for (Preference p : selectedNetworks) {
p.setEnabled(b);
}
}
}
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
+ return;
+ }
+ if (permissions.length > 0 && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION) &&
+ grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ buildAutodownloadSelectedNetworksPreference();
+ }
+ }
+
+ private boolean showPermissionRequestPromptOnAndroid10IfNeeded(boolean wifiFilterEnabled) {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
+ return false;
+ }
+
+ // Cases Android 10(Q) or later
+ if (prefPermissionRequestPromptOnAndroid10 != null) {
+ getPreferenceScreen().removePreference(prefPermissionRequestPromptOnAndroid10);
+ prefPermissionRequestPromptOnAndroid10 = null;
+ }
+
+ if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+
+ // Case location permission not yet granted, permission-specific UI is needed
+ if (!wifiFilterEnabled) {
+ // Don't show the UI when WiFi filter disabled.
+ // it still return true, so that the caller knows
+ // it does not have required permission, and will not invoke codes that require so.
+ return true;
+ }
+
+ Preference pref = new Preference(requireActivity());
+ pref.setKey(PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT);
+ pref.setTitle(R.string.autodl_wifi_filter_permission_title);
+ pref.setSummary(R.string.autodl_wifi_filter_permission_message);
+ pref.setIcon(R.drawable.ic_warning_red);
+ pref.setOnPreferenceClickListener(preference -> {
+ requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
+ return true;
+ });
+ pref.setPersistent(false);
+ getPreferenceScreen().addPreference(pref);
+ prefPermissionRequestPromptOnAndroid10 = pref;
+ return true;
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
index dcba5fe89..9f36e1355 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
@@ -1,27 +1,21 @@
package de.danoeh.antennapod.fragment.preferences;
import android.content.ActivityNotFoundException;
-import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
-import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.util.Log;
import android.widget.Toast;
import com.bytehamster.lib.preferencesearch.SearchConfiguration;
import com.bytehamster.lib.preferencesearch.SearchPreference;
-import de.danoeh.antennapod.CrashReportWriter;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AboutActivity;
+import de.danoeh.antennapod.activity.BugReportActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.activity.StatisticsActivity;
-
-import java.util.List;
+import de.danoeh.antennapod.core.util.IntentUtils;
public class MainPreferencesFragment extends PreferenceFragmentCompat {
private static final String TAG = "MainPreferencesFragment";
@@ -31,9 +25,9 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
private static final String PREF_SCREEN_NETWORK = "prefScreenNetwork";
private static final String PREF_SCREEN_INTEGRATIONS = "prefScreenIntegrations";
private static final String PREF_SCREEN_STORAGE = "prefScreenStorage";
- private static final String PREF_KNOWN_ISSUES = "prefKnownIssues";
private static final String PREF_FAQ = "prefFaq";
- private static final String PREF_SEND_CRASH_REPORT = "prefSendCrashReport";
+ private static final String PREF_VIEW_MAILING_LIST = "prefViewMailingList";
+ private static final String PREF_SEND_BUG_REPORT = "prefSendBugReport";
private static final String STATISTICS = "statistics";
private static final String PREF_ABOUT = "prefAbout";
@@ -78,49 +72,20 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
return true;
}
);
- findPreference(PREF_KNOWN_ISSUES).setOnPreferenceClickListener(preference -> {
- openInBrowser("https://github.com/AntennaPod/AntennaPod/labels/bug");
+ findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
+ IntentUtils.openInBrowser(getContext(), "https://antennapod.org/faq.html");
return true;
});
- findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
- openInBrowser("https://antennapod.org/faq.html");
+ findPreference(PREF_VIEW_MAILING_LIST).setOnPreferenceClickListener(preference -> {
+ IntentUtils.openInBrowser(getContext(), "https://groups.google.com/forum/#!forum/antennapod");
return true;
});
- findPreference(PREF_SEND_CRASH_REPORT).setOnPreferenceClickListener(preference -> {
- Context context = getActivity().getApplicationContext();
- Intent emailIntent = new Intent(Intent.ACTION_SEND);
- emailIntent.setType("text/plain");
- emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"Martin.Fietz@gmail.com"});
- emailIntent.putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report");
- emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe what you were doing when the app crashed");
- // the attachment
- Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority),
- CrashReportWriter.getFile());
- emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
- emailIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- String intentTitle = getActivity().getString(R.string.send_email);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(emailIntent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- }
- getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle));
+ findPreference(PREF_SEND_BUG_REPORT).setOnPreferenceClickListener(preference -> {
+ startActivity(new Intent(getActivity(), BugReportActivity.class));
return true;
});
}
- private void openInBrowser(String url) {
- try {
- Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- startActivity(myIntent);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(getActivity(), R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
- Log.e(TAG, Log.getStackTraceString(e));
- }
- }
-
private void setupSearch() {
SearchPreference searchPreference = (SearchPreference) findPreference("searchPreference");
SearchConfiguration config = searchPreference.getSearchConfiguration();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
index b4226b546..e36476c6f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
@@ -4,6 +4,7 @@ import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -13,13 +14,16 @@ import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.FileProvider;
+import android.support.v4.provider.DocumentFile;
import android.support.v7.app.AlertDialog;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.util.Log;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
import de.danoeh.antennapod.activity.ImportExportActivity;
import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
+import de.danoeh.antennapod.asynctask.DocumentFileExportWorker;
import de.danoeh.antennapod.asynctask.ExportWorker;
import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.core.export.html.HtmlWriter;
@@ -45,6 +49,12 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE };
private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41;
+ private static final int CHOOSE_OPML_EXPORT_PATH = 1;
+ private static final String DEFAULT_OPML_OUTPUT_NAME = "antennapod-feeds.opml";
+ private static final String CONTENT_TYPE_OPML = "text/x-opml";
+ private static final int CHOOSE_HTML_EXPORT_PATH = 2;
+ private static final String DEFAULT_HTML_OUTPUT_NAME = "antennapod-feeds.html";
+ private static final String CONTENT_TYPE_HTML = "text/html";
private Disposable disposable;
@Override
@@ -59,6 +69,14 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
setDataFolderText();
}
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
private void setupStorageScreen() {
final Activity activity = getActivity();
@@ -69,9 +87,16 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
}
);
findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
- preference -> export(new OpmlWriter()));
+ preference -> {
+ openOpmlExportPathPicker();
+ return true;
+ }
+ );
findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
- preference -> export(new HtmlWriter()));
+ preference -> {
+ openHtmlExportPathPicker();
+ return true;
+ });
findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
preference -> {
activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class));
@@ -129,52 +154,65 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
}
private boolean export(ExportWriter exportWriter) {
+ return export(exportWriter, null);
+ }
+
+ private boolean export(ExportWriter exportWriter, final Uri uri) {
Context context = getActivity();
final ProgressDialog progressDialog = new ProgressDialog(context);
progressDialog.setMessage(context.getString(R.string.exporting_label));
progressDialog.setIndeterminate(true);
progressDialog.show();
- final AlertDialog.Builder alert = new AlertDialog.Builder(context)
- .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
- Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
- disposable = observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(output -> {
- alert.setTitle(R.string.export_success_title);
- String message = context.getString(R.string.export_success_sum, output.toString());
- alert.setMessage(message);
- alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
+ if (uri == null) {
+ Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
+ disposable = observable.subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(output -> {
Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
context.getString(R.string.provider_authority), output);
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_SUBJECT,
- context.getResources().getText(R.string.opml_export_label));
- sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
- sendIntent.setType("text/plain");
- sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- }
- context.startActivity(Intent.createChooser(sendIntent,
- context.getResources().getText(R.string.send_label)));
- });
- alert.create().show();
- }, error -> {
- alert.setTitle(R.string.export_error_label);
- alert.setMessage(error.getMessage());
- alert.show();
- }, progressDialog::dismiss);
+ showExportSuccessDialog(context.getString(R.string.export_success_sum, output.toString()), fileUri);
+ }, this::showExportErrorDialog, progressDialog::dismiss);
+ } else {
+ Observable<DocumentFile> observable = new DocumentFileExportWorker(exportWriter, context, uri).exportObservable();
+ disposable = observable.subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(output -> {
+ showExportSuccessDialog(context.getString(R.string.export_success_sum, output.getUri()), output.getUri());
+ }, this::showExportErrorDialog, progressDialog::dismiss);
+ }
return true;
}
- public void unsubscribeExportSubscription() {
- if (disposable != null) {
- disposable.dispose();
- }
+ private void showExportSuccessDialog(final String message, final Uri streamUri) {
+ final AlertDialog.Builder alert = new AlertDialog.Builder(getContext())
+ .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ alert.setTitle(R.string.export_success_title);
+ alert.setMessage(message);
+ alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.opml_export_label));
+ sendIntent.putExtra(Intent.EXTRA_STREAM, streamUri);
+ sendIntent.setType("text/plain");
+ sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
+ List<ResolveInfo> resInfoList = getContext().getPackageManager()
+ .queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ getContext().grantUriPermission(packageName, streamUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ getContext().startActivity(Intent.createChooser(sendIntent, getString(R.string.send_label)));
+ });
+ alert.create().show();
+ }
+
+ private void showExportErrorDialog(final Throwable error) {
+ final AlertDialog.Builder alert = new AlertDialog.Builder(getContext())
+ .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ alert.setTitle(R.string.export_error_label);
+ alert.setMessage(error.getMessage());
+ alert.show();
}
@SuppressLint("NewApi")
@@ -184,22 +222,22 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
File path;
- if(dir != null) {
+ if (dir != null) {
path = new File(dir);
} else {
path = getActivity().getExternalFilesDir(null);
}
String message = null;
- final Context context= getActivity().getApplicationContext();
- if(!path.exists()) {
+ final Context context = getActivity().getApplicationContext();
+ if (!path.exists()) {
message = String.format(context.getString(R.string.folder_does_not_exist_error), dir);
- } else if(!path.canRead()) {
+ } else if (!path.canRead()) {
message = String.format(context.getString(R.string.folder_not_readable_error), dir);
- } else if(!path.canWrite()) {
+ } else if (!path.canWrite()) {
message = String.format(context.getString(R.string.folder_not_writable_error), dir);
}
- if(message == null) {
+ if (message == null) {
Log.d(TAG, "Setting data folder: " + dir);
UserPreferences.setDataFolder(dir);
setDataFolderText();
@@ -210,6 +248,16 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
ab.show();
}
}
+
+ if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_OPML_EXPORT_PATH) {
+ Uri uri = data.getData();
+ export(new OpmlWriter(), uri);
+ }
+
+ if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_HTML_EXPORT_PATH) {
+ Uri uri = data.getData();
+ export(new HtmlWriter(), uri);
+ }
}
private void setDataFolderText() {
@@ -231,6 +279,50 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
}
+ private void openOpmlExportPathPicker() {
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType(CONTENT_TYPE_OPML)
+ .putExtra(Intent.EXTRA_TITLE, DEFAULT_OPML_OUTPUT_NAME);
+
+ // Creates an implicit intent to launch a file manager which lets
+ // the user choose a specific directory to export to.
+ try {
+ startActivityForResult(intentPickAction, CHOOSE_OPML_EXPORT_PATH);
+ return;
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found. Should never happen...");
+ }
+ }
+
+ // If we are using a SDK lower than API 21 or the implicit intent failed
+ // fallback to the legacy export process
+ export(new OpmlWriter());
+ }
+
+ private void openHtmlExportPathPicker() {
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType(CONTENT_TYPE_HTML)
+ .putExtra(Intent.EXTRA_TITLE, DEFAULT_HTML_OUTPUT_NAME);
+
+ // Creates an implicit intent to launch a file manager which lets
+ // the user choose a specific directory to export to.
+ try {
+ startActivityForResult(intentPickAction, CHOOSE_HTML_EXPORT_PATH);
+ return;
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found. Should never happen...");
+ }
+ }
+
+ // If we are using a SDK lower than API 21 or the implicit intent failed
+ // fallback to the legacy export process
+ export(new HtmlWriter());
+ }
+
private void showChooseDataFolderDialog() {
ChooseDataFolderDialog.showDialog(
getActivity(), new ChooseDataFolderDialog.RunnableWithString() {
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
index 156657a00..add62b480 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -3,7 +3,10 @@ package de.danoeh.antennapod.menuhandler;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
-import android.support.annotation.Nullable;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.Fragment;
import android.util.Log;
import android.widget.Toast;
@@ -18,7 +21,6 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.ShareUtils;
/**
@@ -51,35 +53,21 @@ public class FeedItemMenuHandler {
* @param mi An instance of MenuInterface that the method uses to change a
* MenuItem's visibility
* @param selectedItem The FeedItem for which the menu is supposed to be prepared
- * @param showExtendedMenu True if MenuItems that let the user share information about
- * the FeedItem and visit its website should be set visible. This
- * parameter should be set to false if the menu space is limited.
- * @param queueAccess Used for testing if the queue contains the selected item; only used for
- * move to top/bottom in the queue
* @return Returns true if selectedItem is not null.
*/
public static boolean onPrepareMenu(MenuInterface mi,
- FeedItem selectedItem,
- boolean showExtendedMenu,
- @Nullable LongList queueAccess) {
+ FeedItem selectedItem) {
if (selectedItem == null) {
return false;
}
boolean hasMedia = selectedItem.getMedia() != null;
boolean isPlaying = hasMedia && selectedItem.getState() == FeedItem.State.PLAYING;
- boolean keepSorted = UserPreferences.isQueueKeepSorted();
if (!isPlaying) {
mi.setItemVisibility(R.id.skip_episode_item, false);
}
boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE);
- if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId() || keepSorted) {
- mi.setItemVisibility(R.id.move_to_top_item, false);
- }
- if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId() || keepSorted) {
- mi.setItemVisibility(R.id.move_to_bottom_item, false);
- }
if (!isInQueue) {
mi.setItemVisibility(R.id.remove_from_queue_item, false);
}
@@ -87,12 +75,12 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.add_to_queue_item, false);
}
- if (!showExtendedMenu || !ShareUtils.hasLinkToShare(selectedItem)) {
+ if (!ShareUtils.hasLinkToShare(selectedItem)) {
mi.setItemVisibility(R.id.visit_website_item, false);
mi.setItemVisibility(R.id.share_link_item, false);
mi.setItemVisibility(R.id.share_link_with_position_item, false);
}
- if (!showExtendedMenu || !hasMedia || selectedItem.getMedia().getDownload_url() == null) {
+ if (!hasMedia || selectedItem.getMedia().getDownload_url() == null) {
mi.setItemVisibility(R.id.share_download_url_item, false);
mi.setItemVisibility(R.id.share_download_url_with_position_item, false);
}
@@ -104,6 +92,7 @@ public class FeedItemMenuHandler {
boolean fileDownloaded = hasMedia && selectedItem.getMedia().fileExists();
mi.setItemVisibility(R.id.share_file, fileDownloaded);
+ mi.setItemVisibility(R.id.remove_new_flag_item, selectedItem.isNew());
if (selectedItem.isPlayed()) {
mi.setItemVisibility(R.id.mark_read_item, false);
} else {
@@ -114,7 +103,7 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.reset_position, false);
}
- if(!UserPreferences.isEnableAutodownload()) {
+ if(!UserPreferences.isEnableAutodownload() || fileDownloaded) {
mi.setItemVisibility(R.id.activate_auto_download, false);
mi.setItemVisibility(R.id.deactivate_auto_download, false);
} else if(selectedItem.getAutoDownload()) {
@@ -141,10 +130,8 @@ public class FeedItemMenuHandler {
*/
public static boolean onPrepareMenu(MenuInterface mi,
FeedItem selectedItem,
- boolean showExtendedMenu,
- LongList queueAccess,
int... excludeIds) {
- boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess);
+ boolean rc = onPrepareMenu(mi, selectedItem);
if (rc && excludeIds != null) {
for (int id : excludeIds) {
mi.setItemVisibility(id, false);
@@ -153,8 +140,16 @@ public class FeedItemMenuHandler {
return rc;
}
- public static boolean onMenuItemClicked(Context context, int menuItemId,
- FeedItem selectedItem) {
+ /**
+ * Default menu handling for the given FeedItem.
+ *
+ * A Fragment instance, (rather than the more generic Context), is needed as a parameter
+ * to support some UI operations, e.g., creating a Snackbar.
+ */
+ public static boolean onMenuItemClicked(@NonNull Fragment fragment, int menuItemId,
+ @NonNull FeedItem selectedItem) {
+
+ @NonNull Context context = fragment.requireContext();
switch (menuItemId) {
case R.id.skip_episode_item:
IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
@@ -162,6 +157,9 @@ public class FeedItemMenuHandler {
case R.id.remove_item:
DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId());
break;
+ case R.id.remove_new_flag_item:
+ removeNewFlagWithUndo(fragment, selectedItem);
+ break;
case R.id.mark_read_item:
selectedItem.setPlayed(true);
DBWriter.markItemPlayed(selectedItem, FeedItem.PLAYED, false);
@@ -216,14 +214,7 @@ public class FeedItemMenuHandler {
DBWriter.setFeedItemAutoDownload(selectedItem, false);
break;
case R.id.visit_website_item:
- Uri uri = Uri.parse(FeedItemUtil.getLinkWithFallback(selectedItem));
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(context, intent)) {
- context.startActivity(intent);
- } else {
- Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
- Toast.LENGTH_SHORT).show();
- }
+ IntentUtils.openInBrowser(context, FeedItemUtil.getLinkWithFallback(selectedItem));
break;
case R.id.share_link_item:
ShareUtils.shareFeedItemLink(context, selectedItem);
@@ -249,4 +240,39 @@ public class FeedItemMenuHandler {
return true;
}
+ /**
+ * Remove new flag with additional UI logic to allow undo with Snackbar.
+ *
+ * Undo is useful for Remove new flag, given there is no UI to undo it otherwise
+ * ,i.e., there is (context) menu item for add new flag
+ */
+ public static void removeNewFlagWithUndo(@NonNull Fragment fragment, FeedItem item) {
+ if (item == null) {
+ return;
+ }
+
+ Log.d(TAG, "removeNewFlagWithUndo(" + item.getId() + ")");
+ // we're marking it as unplayed since the user didn't actually play it
+ // but they don't want it considered 'NEW' anymore
+ DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
+
+ final Handler h = new Handler(fragment.requireContext().getMainLooper());
+ final Runnable r = () -> {
+ FeedMedia media = item.getMedia();
+ if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
+ DBWriter.deleteFeedMediaOfItem(fragment.requireContext(), media.getId());
+ }
+ };
+
+ Snackbar snackbar = Snackbar.make(fragment.getView(), fragment.getString(R.string.removed_new_flag_label),
+ Snackbar.LENGTH_LONG);
+ snackbar.setAction(fragment.getString(R.string.undo), v -> {
+ DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
+ // don't forget to cancel the thing that's going to remove the media
+ h.removeCallbacks(r);
+ });
+ snackbar.show();
+ h.postDelayed(r, (int) Math.ceil(snackbar.getDuration() * 1.05f));
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
index 3949119fc..dbb3b6e7b 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -86,14 +86,7 @@ public class FeedMenuHandler {
conDialog.createNewDialog().show();
break;
case R.id.visit_website_item:
- Uri uri = Uri.parse(selectedFeed.getLink());
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(context, intent)) {
- context.startActivity(intent);
- } else {
- Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
- Toast.LENGTH_SHORT).show();
- }
+ IntentUtils.openInBrowser(context, selectedFeed.getLink());
break;
case R.id.share_link_item:
ShareUtils.shareFeedlink(context, selectedFeed);
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
index 1879dcbf8..6392d0535 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
@@ -3,8 +3,11 @@ package de.danoeh.antennapod.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
+
import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
public class PreferenceUpgrader {
@@ -21,7 +24,7 @@ public class PreferenceUpgrader {
if (oldVersion != newVersion) {
NotificationUtils.createChannels(context);
- UserPreferences.restartUpdateAlarm();
+ AutoUpdateManager.restartUpdateAlarm();
upgrade(oldVersion);
upgraderPrefs.edit().putInt(PREF_CONFIGURED_VERSION, newVersion).apply();
@@ -62,5 +65,13 @@ public class PreferenceUpgrader {
break;
}
}
+ if (oldVersion < 1070400) {
+ int theme = UserPreferences.getTheme();
+ if (theme == R.style.Theme_AntennaPod_Light) {
+ prefs.edit().putString(UserPreferences.PREF_THEME, "system").apply();
+ }
+
+ UserPreferences.setQueueLocked(false);
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
index 03958508d..5fa6588d9 100644
--- a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
+++ b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
@@ -46,7 +46,7 @@ public class SPAUtil {
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, true);
- editor.commit();
+ editor.apply();
return true;
} else {
@@ -63,7 +63,7 @@ public class SPAUtil {
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(c.getApplicationContext()).edit();
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, false);
- editor.commit();
+ editor.apply();
}
}
}
diff --git a/app/src/main/res/layout/bug_report.xml b/app/src/main/res/layout/bug_report.xml
new file mode 100644
index 000000000..e97e85265
--- /dev/null
+++ b/app/src/main/res/layout/bug_report.xml
@@ -0,0 +1,28 @@
+<?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">
+ <Button
+ android:id="@+id/btn_open_bug_tracker"
+ android:text="@string/open_bug_tracker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <Button
+ android:id="@+id/btn_copy_log"
+ android:text="@string/copy_to_clipboard"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:layout_marginTop="8dp"
+ android:id="@+id/crash_report_logs"
+ android:textIsSelectable="true"
+ android:textSize="12sp"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/checkbox_do_not_show_again.xml b/app/src/main/res/layout/checkbox_do_not_show_again.xml
new file mode 100644
index 000000000..15f26e8b4
--- /dev/null
+++ b/app/src/main/res/layout/checkbox_do_not_show_again.xml
@@ -0,0 +1,17 @@
+<?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="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp">
+
+ <CheckBox
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/checkbox_do_not_show_again"
+ android:text="@string/checkbox_do_not_show_again"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/downloaded_episodeslist_item.xml b/app/src/main/res/layout/downloaded_episodeslist_item.xml
index 65a08251f..3f8065466 100644
--- a/app/src/main/res/layout/downloaded_episodeslist_item.xml
+++ b/app/src/main/res/layout/downloaded_episodeslist_item.xml
@@ -17,7 +17,7 @@
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml
index 000539e60..b047b3da0 100644
--- a/app/src/main/res/layout/feeditem_fragment.xml
+++ b/app/src/main/res/layout/feeditem_fragment.xml
@@ -37,7 +37,7 @@
android:contentDescription="@string/cover_label"
android:gravity="center_vertical"
android:foreground="?attr/selectableItemBackground"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark" />
<TextView
diff --git a/app/src/main/res/layout/feeditemlist_header.xml b/app/src/main/res/layout/feeditemlist_header.xml
index e1f451e9e..596135d88 100644
--- a/app/src/main/res/layout/feeditemlist_header.xml
+++ b/app/src/main/res/layout/feeditemlist_header.xml
@@ -27,7 +27,7 @@
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="@string/cover_label"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<ImageButton
diff --git a/app/src/main/res/layout/gpodnet_podcast_listitem.xml b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
index 27a8bbdca..6e02fa090 100644
--- a/app/src/main/res/layout/gpodnet_podcast_listitem.xml
+++ b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
@@ -23,7 +23,7 @@
android:contentDescription="@string/cover_label"
android:cropToPadding="true"
android:scaleType="fitXY"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark" />
<LinearLayout
diff --git a/app/src/main/res/layout/itunes_podcast_listitem.xml b/app/src/main/res/layout/itunes_podcast_listitem.xml
index 4848563b1..b2411c5df 100644
--- a/app/src/main/res/layout/itunes_podcast_listitem.xml
+++ b/app/src/main/res/layout/itunes_podcast_listitem.xml
@@ -25,7 +25,7 @@
android:cropToPadding="true"
android:scaleType="fitXY"
tools:background="@android:color/holo_green_dark"
- tools:src="@drawable/ic_stat_antenna_default" />
+ tools:src="@drawable/ic_antenna" />
<LinearLayout
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/mediaplayerinfo_activity.xml b/app/src/main/res/layout/mediaplayerinfo_activity.xml
index a6427e985..c4217db54 100644
--- a/app/src/main/res/layout/mediaplayerinfo_activity.xml
+++ b/app/src/main/res/layout/mediaplayerinfo_activity.xml
@@ -155,11 +155,11 @@
android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
- android:textSize="10sp"
+ android:textSize="12sp"
android:textColor="?android:attr/textColorSecondary"
android:clickable="false"/>
- <Button
+ <ImageButton
android:id="@+id/butPlaybackSpeed"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
@@ -167,13 +167,28 @@
android:layout_toStartOf="@id/butRev"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/set_playback_speed_label"
- android:src="?attr/av_fast_forward"
- android:textSize="@dimen/text_size_medium"
- android:textAllCaps="false"
- android:maxLines="1"
+ android:src="?attr/av_speed"
+ android:scaleType="fitCenter"
+ tools:src="@drawable/ic_playback_speed_white"
tools:visibility="gone"
tools:background="@android:color/holo_green_dark" />
+ <TextView
+ android:id="@+id/txtvPlaybackSpeed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/butPlaybackSpeed"
+ android:layout_alignLeft="@id/butPlaybackSpeed"
+ android:layout_alignStart="@id/butPlaybackSpeed"
+ android:layout_alignRight="@id/butPlaybackSpeed"
+ android:layout_alignEnd="@id/butPlaybackSpeed"
+ android:layout_marginTop="-8dp"
+ android:gravity="center"
+ android:text="1.00"
+ android:textSize="12sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:clickable="false"/>
+
<ImageButton
android:id="@+id/butCastDisconnect"
android:layout_width="@dimen/audioplayer_playercontrols_length"
@@ -216,7 +231,7 @@
android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
- android:textSize="10sp"
+ android:textSize="12sp"
android:textColor="?android:attr/textColorSecondary"
android:clickable="false"/>
diff --git a/app/src/main/res/layout/nav_feedlistitem.xml b/app/src/main/res/layout/nav_feedlistitem.xml
index 816870d1c..52833b3cd 100644
--- a/app/src/main/res/layout/nav_feedlistitem.xml
+++ b/app/src/main/res/layout/nav_feedlistitem.xml
@@ -25,7 +25,7 @@
android:scaleType="centerCrop"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<TextView
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index 6b41b68d5..1dcc34bce 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -51,7 +51,7 @@
android:layout_height="@dimen/thumbnail_length_queue_item"
android:layout_centerVertical="true"
android:contentDescription="@string/cover_label"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
</RelativeLayout>
diff --git a/app/src/main/res/layout/searchlist_item.xml b/app/src/main/res/layout/searchlist_item.xml
index 50374c737..4a055fea9 100644
--- a/app/src/main/res/layout/searchlist_item.xml
+++ b/app/src/main/res/layout/searchlist_item.xml
@@ -18,7 +18,7 @@
android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<LinearLayout
diff --git a/app/src/main/res/layout/statistics_listitem.xml b/app/src/main/res/layout/statistics_listitem.xml
index b186add9e..f52aa73e0 100644
--- a/app/src/main/res/layout/statistics_listitem.xml
+++ b/app/src/main/res/layout/statistics_listitem.xml
@@ -23,7 +23,7 @@
android:scaleType="centerCrop"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<TextView
diff --git a/app/src/main/res/menu/allepisodes_context.xml b/app/src/main/res/menu/allepisodes_context.xml
deleted file mode 100644
index 907bc9334..000000000
--- a/app/src/main/res/menu/allepisodes_context.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item
- android:id="@id/skip_episode_item"
- android:menuCategory="container"
- android:title="@string/skip_episode_label" />
-
-
- <item
- android:id="@+id/remove_new_flag_item"
- android:menuCategory="container"
- android:title="@string/remove_new_flag_label" />
-
- <item
- android:id="@+id/mark_read_item"
- android:menuCategory="container"
- android:title="@string/mark_read_label" />
- <item
- android:id="@+id/mark_unread_item"
- android:menuCategory="container"
- android:title="@string/mark_unread_label" />
-
- <item
- android:id="@+id/add_to_queue_item"
- android:menuCategory="container"
- android:title="@string/add_to_queue_label" />
- <item
- android:id="@+id/remove_from_queue_item"
- android:menuCategory="container"
- android:title="@string/remove_from_queue_label" />
- <item
- android:id="@+id/add_to_favorites_item"
- android:menuCategory="container"
- android:title="@string/add_to_favorite_label" />
- <item
- android:id="@+id/remove_from_favorites_item"
- android:menuCategory="container"
- android:title="@string/remove_from_favorite_label" />
- <item
- android:id="@+id/reset_position"
- android:menuCategory="container"
- android:title="@string/reset_position" />
-
- <item
- android:id="@+id/activate_auto_download"
- android:menuCategory="container"
- android:title="@string/activate_auto_download" />
- <item
- android:id="@+id/deactivate_auto_download"
- android:menuCategory="container"
- android:title="@string/deactivate_auto_download" />
-
- <item
- android:id="@+id/visit_website_item"
- android:menuCategory="container"
- android:title="@string/visit_website_label" />
- <item
- android:id="@+id/share_item"
- android:menuCategory="container"
- android:title="@string/share_label">
- <menu>
- <item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
- android:id="@+id/share_link_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_link_with_position_label" />
- <item
- android:id="@+id/share_download_url_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_label" />
- <item
- android:id="@+id/share_download_url_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_with_position_label" />
- <item
- android:id="@+id/share_file"
- android:menuCategory="container"
- android:title="@string/share_file_label" />
- </menu>
- </item>
-</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/feedinfo.xml b/app/src/main/res/menu/feedinfo.xml
index 300068007..f20d679a5 100644
--- a/app/src/main/res/menu/feedinfo.xml
+++ b/app/src/main/res/menu/feedinfo.xml
@@ -11,7 +11,7 @@
<item
android:id="@+id/share_link_item"
custom:showAsAction="collapseActionView"
- android:title="@string/share_link_label">
+ android:title="@string/share_website_url_label">
</item>
<item
android:id="@+id/share_download_url_item"
diff --git a/app/src/main/res/menu/feeditem_options.xml b/app/src/main/res/menu/feeditem_options.xml
index 0801b79a1..e415ff85a 100644
--- a/app/src/main/res/menu/feeditem_options.xml
+++ b/app/src/main/res/menu/feeditem_options.xml
@@ -9,6 +9,11 @@
</item>
<item
+ android:id="@+id/remove_new_flag_item"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/remove_new_flag_label" />
+
+ <item
android:id="@+id/mark_read_item"
custom:showAsAction="collapseActionView"
android:title="@string/mark_read_label">
diff --git a/app/src/main/res/menu/feeditemlist_context.xml b/app/src/main/res/menu/feeditemlist_context.xml
index df13cb027..6e4966206 100644
--- a/app/src/main/res/menu/feeditemlist_context.xml
+++ b/app/src/main/res/menu/feeditemlist_context.xml
@@ -8,6 +8,11 @@
android:title="@string/skip_episode_label" />
<item
+ android:id="@+id/remove_new_flag_item"
+ android:menuCategory="container"
+ android:title="@string/remove_new_flag_label" />
+
+ <item
android:id="@+id/mark_read_item"
android:menuCategory="container"
android:title="@string/mark_read_label" />
diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml
index 0cc8addfe..4144c392f 100644
--- a/app/src/main/res/menu/feedlist.xml
+++ b/app/src/main/res/menu/feedlist.xml
@@ -49,7 +49,7 @@
android:id="@+id/share_link_item"
android:menuCategory="container"
custom:showAsAction="collapseActionView"
- android:title="@string/share_link_label">
+ android:title="@string/share_website_url_label">
</item>
<item
android:id="@+id/share_download_url_item"
diff --git a/app/src/main/res/menu/mediaplayer.xml b/app/src/main/res/menu/mediaplayer.xml
index 44d511ee4..055951760 100644
--- a/app/src/main/res/menu/mediaplayer.xml
+++ b/app/src/main/res/menu/mediaplayer.xml
@@ -35,6 +35,14 @@
</item>
<item
+ android:id="@+id/open_feed_item"
+ android:icon="?attr/feed"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/open_podcast"
+ android:visible="false">
+ </item>
+
+ <item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
custom:showAsAction="collapseActionView"
diff --git a/app/src/main/res/menu/queue_context.xml b/app/src/main/res/menu/queue_context.xml
index e1c3e6216..522e712e2 100644
--- a/app/src/main/res/menu/queue_context.xml
+++ b/app/src/main/res/menu/queue_context.xml
@@ -11,77 +11,6 @@
android:id="@+id/move_to_bottom_item"
android:menuCategory="container"
android:title="@string/move_to_bottom_label" />
+ <!-- rest of the menu items can be found in the generic feeditemlist_context.xml -->
- <item
- android:id="@+id/mark_read_item"
- android:menuCategory="container"
- android:title="@string/mark_read_label" />
-
- <item
- android:id="@+id/mark_unread_item"
- android:menuCategory="container"
- android:title="@string/mark_unread_label" />
-
- <item
- android:id="@+id/remove_from_queue_item"
- android:menuCategory="container"
- android:title="@string/remove_from_queue_label" />
- <item
- android:id="@+id/remove_item"
- android:menuCategory="container"
- android:title="@string/delete_label" />
-
- <item
- android:id="@+id/add_to_favorites_item"
- android:menuCategory="container"
- android:title="@string/add_to_favorite_label" />
- <item
- android:id="@+id/remove_from_favorites_item"
- android:menuCategory="container"
- android:title="@string/remove_from_favorite_label" />
- <item
- android:id="@+id/reset_position"
- android:menuCategory="container"
- android:title="@string/reset_position" />
-
- <item
- android:id="@+id/activate_auto_download"
- android:menuCategory="container"
- android:title="@string/activate_auto_download" />
- <item
- android:id="@+id/deactivate_auto_download"
- android:menuCategory="container"
- android:title="@string/deactivate_auto_download" />
-
- <item
- android:id="@+id/visit_website_item"
- android:menuCategory="container"
- android:title="@string/visit_website_label" />
- <item
- android:id="@+id/share_item"
- android:menuCategory="container"
- android:title="@string/share_label">
- <menu>
- <item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
- android:id="@+id/share_link_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_link_with_position_label" />
- <item
- android:id="@+id/share_download_url_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_label" />
- <item
- android:id="@+id/share_download_url_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_with_position_label" />
- <item
- android:id="@+id/share_file"
- android:menuCategory="container"
- android:title="@string/share_file_label" />
- </menu>
- </item>
</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/subscriptions.xml b/app/src/main/res/menu/subscriptions.xml
index f39e0ac97..1780592d5 100644
--- a/app/src/main/res/menu/subscriptions.xml
+++ b/app/src/main/res/menu/subscriptions.xml
@@ -3,6 +3,13 @@
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
+ android:id="@+id/refresh_item"
+ android:title="@string/refresh_label"
+ android:menuCategory="container"
+ custom:showAsAction="always"
+ android:icon="?attr/navigation_refresh"/>
+
+ <item
android:id="@+id/subscription_num_columns"
android:title="@string/subscription_num_columns"
custom:showAsAction="never">
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 6c1e470c0..37707ead6 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -43,16 +43,14 @@
<Preference
android:key="prefFaq"
android:title="@string/pref_faq"
- android:icon="?attr/ic_question_answer" />
-
+ android:icon="?attr/ic_questionmark" />
<Preference
- android:key="prefKnownIssues"
- android:title="@string/pref_known_issues"
- android:icon="?attr/ic_known_issues" />
+ android:key="prefViewMailingList"
+ android:title="@string/view_mailing_list"
+ android:icon="?attr/ic_chat" />
<Preference
- android:key="prefSendCrashReport"
- android:title="@string/crash_report_title"
- android:summary="@string/crash_report_sum"
+ android:key="prefSendBugReport"
+ android:title="@string/bug_report_title"
android:icon="?attr/ic_bug" />
<Preference
android:key="prefAbout"
diff --git a/app/src/main/res/xml/preferences_user_interface.xml b/app/src/main/res/xml/preferences_user_interface.xml
index 1d970a5f7..c48e9adc8 100644
--- a/app/src/main/res/xml/preferences_user_interface.xml
+++ b/app/src/main/res/xml/preferences_user_interface.xml
@@ -10,7 +10,7 @@
android:title="@string/pref_set_theme_title"
android:key="prefTheme"
android:summary="@string/pref_set_theme_sum"
- android:defaultValue="0"
+ android:defaultValue="system"
app:useStockLayout="true"/>
<Preference
android:key="prefHiddenDrawerItems"
diff --git a/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java b/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
index 87304b3d6..caca8a6e3 100644
--- a/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ b/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
@@ -29,17 +29,34 @@ public abstract class CastEnabledActivity extends AppCompatActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String TAG = "CastEnabledActivity";
- protected CastManager castManager;
- protected SwitchableMediaRouteActionProvider mediaRouteActionProvider;
+ private CastConsumer castConsumer;
+ private CastManager castManager;
+
+ private SwitchableMediaRouteActionProvider mediaRouteActionProvider;
private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (!CastManager.isInitialized()) {
+ return;
+ }
+
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).
registerOnSharedPreferenceChangeListener(this);
+ castConsumer = new DefaultCastConsumer() {
+ @Override
+ public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
+ onCastConnectionChanged(true);
+ }
+
+ @Override
+ public void onDisconnected() {
+ onCastConnectionChanged(false);
+ }
+ };
castManager = CastManager.getInstance();
castManager.addCastConsumer(castConsumer);
castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
@@ -48,6 +65,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@Override
protected void onDestroy() {
+ if (!CastManager.isInitialized()) {
+ super.onDestroy();
+ return;
+ }
PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
.unregisterOnSharedPreferenceChangeListener(this);
castManager.removeCastConsumer(castConsumer);
@@ -58,6 +79,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@CallSuper
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
+ if (!CastManager.isInitialized()) {
+ return true;
+ }
getMenuInflater().inflate(R.menu.cast_enabled, menu);
castButtonVisibilityManager.setMenu(menu);
return true;
@@ -67,6 +91,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@CallSuper
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
+ if (!CastManager.isInitialized()) {
+ return true;
+ }
+
MenuItem mediaRouteButton = menu.findItem(R.id.media_route_menu_item);
if (mediaRouteButton == null) {
Log.wtf(TAG, "MediaRoute item could not be found on the menu!", new Exception());
@@ -83,15 +111,22 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@Override
protected void onResume() {
super.onResume();
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.setResumed(true);
}
@Override
protected void onPause() {
super.onPause();
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.setResumed(false);
}
+
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
@@ -105,18 +140,6 @@ public abstract class CastEnabledActivity extends AppCompatActivity
}
}
- CastConsumer castConsumer = new DefaultCastConsumer() {
- @Override
- public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
- onCastConnectionChanged(true);
- }
-
- @Override
- public void onDisconnected() {
- onCastConnectionChanged(false);
- }
- };
-
private void onCastConnectionChanged(boolean connected) {
if (connected) {
castButtonVisibilityManager.onConnected();
@@ -133,6 +156,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
* @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
*/
public final void requestCastButton(int showAsAction) {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.requestCastButton(showAsAction);
}
diff --git a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
index c9d52df0c..0e69da61e 100644
--- a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
+++ b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
@@ -1,8 +1,13 @@
package de.danoeh.antennapod.preferences;
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AlertDialog;
+
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment;
@@ -18,6 +23,7 @@ public class PreferenceControllerFlavorHelper {
final int googlePlayServicesCheck = GoogleApiAvailability.getInstance()
.isGooglePlayServicesAvailable(ui.getActivity());
if (googlePlayServicesCheck == ConnectionResult.SUCCESS) {
+ displayRestartRequiredDialog(ui.requireContext());
return true;
} else {
GoogleApiAvailability.getInstance()
@@ -29,4 +35,13 @@ public class PreferenceControllerFlavorHelper {
return true;
});
}
+
+ private static void displayRestartRequiredDialog(@NonNull Context context) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(context);
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.setCancelable(false);
+ dialog.show();
+ }
}
diff --git a/build.gradle b/build.gradle
index 2ca680ccb..87a335dcc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -57,8 +57,8 @@ project.ext {
iconifyVersion = "2.2.2"
jsoupVersion = "1.11.2"
materialDialogsVersion = "0.9.0.2"
- okhttpVersion = "3.9.0"
- okioVersion = "1.14.0"
+ okhttpVersion = "3.12.5"
+ okioVersion = "1.17.4"
recyclerviewFlexibledividerVersion = "1.4.0"
robotiumSoloVersion = "5.6.3"
rxAndroidVersion = "2.1.0"
@@ -82,3 +82,14 @@ wrapper {
def doFreeBuild() {
return hasProperty("freeBuild")
}
+
+apply plugin: "checkstyle"
+checkstyle {
+ toolVersion '8.24'
+}
+
+task checkstyle(type: Checkstyle) {
+ classpath = files()
+ source "${project.rootDir}"
+ exclude("**/gen/**")
+}
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
new file mode 100644
index 000000000..0a5b47eb8
--- /dev/null
+++ b/config/checkstyle/checkstyle.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+ "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
+ "https://checkstyle.org/dtds/configuration_1_3.dtd">
+<module name = "Checker">
+ <property name="charset" value="UTF-8"/>
+
+ <property name="severity" value="error"/>
+
+ <property name="fileExtensions" value="java, xml"/>
+
+ <module name="TreeWalker">
+ <module name="OuterTypeFilename"/>
+ <module name="AvoidEscapedUnicodeCharacters">
+ <property name="allowEscapesForControlCharacters" value="true"/>
+ <property name="allowByTailComment" value="true"/>
+ <property name="allowNonPrintableEscapes" value="true"/>
+ </module>
+ <module name="AvoidStarImport"/>
+ <module name="OneTopLevelClass"/>
+ <module name="NoLineWrap"/>
+ <module name="EmptyBlock">
+ <property name="option" value="TEXT"/>
+ <property name="tokens"
+ value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
+ </module>
+ <module name="OneStatementPerLine"/>
+ <module name="FallThrough"/>
+ <module name="UpperEll"/>
+ <module name="ModifierOrder"/>
+ <module name="PackageName">
+ <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
+ <message key="name.invalidPattern"
+ value="Package name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="TypeName">
+ <message key="name.invalidPattern"
+ value="Type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="CatchParameterName">
+ <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
+ <message key="name.invalidPattern"
+ value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="ClassTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern"
+ value="Class type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="MethodTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern"
+ value="Method type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="InterfaceTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern"
+ value="Interface type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="NoFinalizer"/>
+ <module name="GenericWhitespace">
+ <message key="ws.followed"
+ value="GenericWhitespace ''{0}'' is followed by whitespace."/>
+ <message key="ws.preceded"
+ value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
+ <message key="ws.illegalFollow"
+ value="GenericWhitespace ''{0}'' should followed by whitespace."/>
+ <message key="ws.notPreceded"
+ value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
+ </module>
+ <module name="AnnotationLocation">
+ <property name="id" value="AnnotationLocationMostCases"/>
+ <property name="tokens"
+ value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
+ </module>
+ <module name="AnnotationLocation">
+ <property name="id" value="AnnotationLocationVariables"/>
+ <property name="tokens" value="VARIABLE_DEF"/>
+ <property name="allowSamelineMultipleAnnotations" value="true"/>
+ </module>
+ </module>
+</module>
diff --git a/core/build.gradle b/core/build.gradle
index 133f1b262..8614d5589 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -76,7 +76,6 @@ dependencies {
annotationProcessor "org.greenrobot:eventbus-annotation-processor:$eventbusVersion"
implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
- implementation "org.awaitility:awaitility:$awaitilityVersion"
implementation "com.google.android.exoplayer:exoplayer:$exoPlayerVersion"
implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
@@ -92,6 +91,7 @@ dependencies {
System.out.println("core: free build hack, skipping some dependencies")
}
+ testImplementation "org.awaitility:awaitility:$awaitilityVersion"
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:1.10.19'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java
index 124fd3e64..24a71ec96 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java
@@ -25,4 +25,8 @@ public class DownloadEvent {
"update=" + update +
'}';
}
+
+ public boolean hasChangedFeedUpdateStatus(boolean oldStatus) {
+ return oldStatus != update.feedIds.length > 0;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java
deleted file mode 100644
index 4a591c996..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-import de.danoeh.antennapod.core.feed.FeedMedia;
-
-public class FeedMediaEvent {
-
- public enum Action {
- UPDATE
- }
-
- private final Action action;
- private final FeedMedia media;
-
- private FeedMediaEvent(Action action, FeedMedia media) {
- this.action = action;
- this.media = media;
- }
-
- public static FeedMediaEvent update(FeedMedia media) {
- return new FeedMediaEvent(Action.UPDATE, media);
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
index b75e1630c..bb34e2c0f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
@@ -21,8 +21,12 @@ import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.service.download.HttpDownloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.NetworkUtils;
-import okhttp3.*;
-import okhttp3.internal.http.RealResponseBody;
+import okhttp3.Interceptor;
+import okhttp3.OkHttpClient;
+import okhttp3.Protocol;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
/**
* @see com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
index b8ab1c888..787e32ccc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.preferences;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.preference.PreferenceManager;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
@@ -164,7 +165,7 @@ public class UserPreferences {
* @return R.style.Theme_AntennaPod_Light or R.style.Theme_AntennaPod_Dark
*/
public static int getTheme() {
- return readThemeValue(prefs.getString(PREF_THEME, "0"));
+ return readThemeValue(prefs.getString(PREF_THEME, "system"));
}
public static int getNoTitleTheme() {
@@ -621,7 +622,7 @@ public class UserPreferences {
.apply();
// when updating with an interval, we assume the user wants
// to update *now* and then every 'hours' interval thereafter.
- restartUpdateAlarm();
+ AutoUpdateManager.restartUpdateAlarm();
}
/**
@@ -631,7 +632,7 @@ public class UserPreferences {
prefs.edit()
.putString(PREF_UPDATE_INTERVAL, hourOfDay + ":" + minute)
.apply();
- restartUpdateAlarm();
+ AutoUpdateManager.restartUpdateAlarm();
}
public static void disableAutoUpdate() {
@@ -672,14 +673,18 @@ public class UserPreferences {
}
private static int readThemeValue(String valueFromPrefs) {
- switch (Integer.parseInt(valueFromPrefs)) {
- case 0:
+ switch (valueFromPrefs) {
+ case "0":
return R.style.Theme_AntennaPod_Light;
- case 1:
+ case "1":
return R.style.Theme_AntennaPod_Dark;
- case 2:
+ case "2":
return R.style.Theme_AntennaPod_TrueBlack;
default:
+ int nightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ if (nightMode == Configuration.UI_MODE_NIGHT_YES) {
+ return R.style.Theme_AntennaPod_Dark;
+ }
return R.style.Theme_AntennaPod_Light;
}
}
@@ -877,26 +882,6 @@ public class UserPreferences {
return getUpdateTimeOfDay().length == 2;
}
- public static void restartUpdateAlarm() {
- if (isAutoUpdateDisabled()) {
- AutoUpdateManager.disableAutoUpdate();
- } else if (isAutoUpdateTimeOfDay()) {
- int[] timeOfDay = getUpdateTimeOfDay();
- Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay));
- AutoUpdateManager.restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]);
- } else {
- long milliseconds = getUpdateInterval();
- AutoUpdateManager.restartUpdateIntervalAlarm(milliseconds);
- }
- }
-
- /**
- * Reads episode cache size as it is saved in the episode_cache_size_values array.
- */
- public static int readEpisodeCacheSize(String valueFromPrefs) {
- return readEpisodeCacheSizeInternal(valueFromPrefs);
- }
-
/**
* Evaluates whether Cast support (Chromecast, Audio Cast, etc) is enabled on the preferences.
*/
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
index 126f12247..af0a0d426 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
@@ -6,8 +6,7 @@ import android.content.Intent;
import android.util.Log;
import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.FeedUpdateUtils;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
/**
* Refreshes all feeds when it receives an intent
@@ -20,7 +19,8 @@ public class FeedUpdateReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Received intent");
ClientConfig.initialize(context);
- FeedUpdateUtils.startAutoUpdate(context, null);
+
+ AutoUpdateManager.runOnce();
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java
index efdb96dc1..87c18227b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java
@@ -2,17 +2,23 @@ package de.danoeh.antennapod.core.service;
import android.content.Context;
import android.support.annotation.NonNull;
+import android.util.Log;
+
import androidx.work.Worker;
import androidx.work.WorkerParameters;
+
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.FeedUpdateUtils;
-import org.awaitility.Awaitility;
-
-import java.util.concurrent.atomic.AtomicBoolean;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.util.NetworkUtils;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
public class FeedUpdateWorker extends Worker {
+ private static final String TAG = "FeedUpdateWorker";
+
+ public static final String PARAM_RUN_ONCE = "runOnce";
+
public FeedUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@@ -20,16 +26,20 @@ public class FeedUpdateWorker extends Worker {
@Override
@NonNull
public Result doWork() {
+ final boolean isRunOnce = getInputData().getBoolean(PARAM_RUN_ONCE, false);
+ Log.d(TAG, "doWork() : isRunOnce = " + isRunOnce);
ClientConfig.initialize(getApplicationContext());
- AtomicBoolean finished = new AtomicBoolean(false);
- FeedUpdateUtils.startAutoUpdate(getApplicationContext(), () -> finished.set(true));
- Awaitility.await().until(finished::get);
+ if (NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed()) {
+ DBTasks.refreshAllFeeds(getApplicationContext());
+ } else {
+ Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
+ }
- if (UserPreferences.isAutoUpdateTimeOfDay()) {
+ if (!isRunOnce && UserPreferences.isAutoUpdateTimeOfDay()) {
// WorkManager does not allow to set specific time for repeated tasks.
// We repeatedly schedule a OneTimeWorkRequest instead.
- UserPreferences.restartUpdateAlarm();
+ AutoUpdateManager.restartUpdateAlarm();
}
return Result.success();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
index 97007a214..04a6d5882 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
@@ -16,7 +16,9 @@ import java.net.Socket;
import java.net.SocketAddress;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
@@ -28,6 +30,8 @@ import javax.net.ssl.X509TrustManager;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBWriter;
+import okhttp3.CipherSuite;
+import okhttp3.ConnectionSpec;
import okhttp3.Credentials;
import okhttp3.HttpUrl;
import okhttp3.JavaNetCookieJar;
@@ -138,9 +142,24 @@ public class AntennapodHttpClient {
});
}
}
- if(16 <= Build.VERSION.SDK_INT && Build.VERSION.SDK_INT < 21) {
+ if (16 <= Build.VERSION.SDK_INT && Build.VERSION.SDK_INT < 21) {
builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager());
}
+
+ if (Build.VERSION.SDK_INT < 21) {
+ // workaround for Android 4.x for certain web sites.
+ // see: https://github.com/square/okhttp/issues/4053#issuecomment-402579554
+ List<CipherSuite> cipherSuites = new ArrayList<>();
+ cipherSuites.addAll(ConnectionSpec.MODERN_TLS.cipherSuites());
+ cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
+ cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
+
+ ConnectionSpec legacyTls = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .cipherSuites(cipherSuites.toArray(new CipherSuite[0]))
+ .build();
+ builder.connectionSpecs(Arrays.asList(legacyTls, ConnectionSpec.CLEARTEXT));
+ }
+
return builder;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
index 787d465d8..ce61bff68 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
@@ -13,6 +13,7 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
@@ -190,10 +191,8 @@ public class DownloadService extends Service {
handleFailedDownload(status, downloader.getDownloadRequest());
if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- long id = status.getFeedfileId();
- FeedMedia media = DBReader.getFeedMedia(id);
- FeedItem item;
- if (media == null || (item = media.getItem()) == null) {
+ FeedItem item = getFeedItemFromId(status.getFeedfileId());
+ if (item == null) {
return;
}
boolean httpNotFound = status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
@@ -213,9 +212,8 @@ public class DownloadService extends Service {
// if FeedMedia download has been canceled, fake FeedItem update
// so that lists reload that it
if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
- FeedItem item;
- if (media == null || (item = media.getItem()) == null) {
+ FeedItem item = getFeedItemFromId(status.getFeedfileId());
+ if (item == null) {
return;
}
EventBus.getDefault().post(FeedItemEvent.updated(item));
@@ -386,6 +384,12 @@ public class DownloadService extends Service {
Downloader d = getDownloader(url);
if (d != null) {
d.cancel();
+ DownloadRequester.getInstance().removeDownload(d.getDownloadRequest());
+
+ FeedItem item = getFeedItemFromId(d.getDownloadRequest().getFeedfileId());
+ if (item != null) {
+ EventBus.getDefault().post(FeedItemEvent.updated(item));
+ }
} else {
Log.e(TAG, "Could not cancel download with url " + url);
}
@@ -546,7 +550,7 @@ public class DownloadService extends Service {
.setContentText(getText(R.string.authentication_notification_msg))
.setStyle(new NotificationCompat.BigTextStyle().bigText(getText(R.string.authentication_notification_msg)
+ ": " + resourceTitle))
- .setSmallIcon(R.drawable.ic_stat_authentication)
+ .setSmallIcon(R.drawable.ic_notification_key)
.setAutoCancel(true)
.setContentIntent(ClientConfig.downloadServiceCallbacks.getAuthentificationNotificationContentIntent(DownloadService.this, downloadRequest));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -578,6 +582,16 @@ public class DownloadService extends Service {
syncExecutor.execute(new FailedDownloadHandler(status, request));
}
+ @Nullable
+ private FeedItem getFeedItemFromId(long id) {
+ FeedMedia media = DBReader.getFeedMedia(id);
+ if (media != null) {
+ return media.getItem();
+ } else {
+ return null;
+ }
+ }
+
/**
* Takes a single Feed, parses the corresponding file and refreshes
* information in the manager
@@ -1058,7 +1072,13 @@ public class DownloadService extends Service {
private final Runnable postDownloaderTask = new Runnable() {
@Override
public void run() {
- List<Downloader> list = Collections.unmodifiableList(downloads);
+ List<Downloader> runningDownloads = new ArrayList<>();
+ for (Downloader downloader : downloads) {
+ if (!downloader.cancelled) {
+ runningDownloads.add(downloader);
+ }
+ }
+ List<Downloader> list = Collections.unmodifiableList(runningDownloads);
EventBus.getDefault().postSticky(DownloadEvent.refresh(list));
postHandler.postDelayed(postDownloaderTask, 1500);
}
@@ -1076,7 +1096,10 @@ public class DownloadService extends Service {
private static String compileNotificationString(List<Downloader> downloads) {
List<String> lines = new ArrayList<>(downloads.size());
for (Downloader downloader : downloads) {
- StringBuilder line = new StringBuilder("\u2022 ");
+ if (downloader.cancelled) {
+ continue;
+ }
+ StringBuilder line = new StringBuilder("• ");
DownloadRequest request = downloader.getDownloadRequest();
switch (request.getFeedfileType()) {
case Feed.FEEDFILETYPE_FEED:
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java
index 5debc6d05..d88eb63f4 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java
@@ -193,10 +193,6 @@ public class DownloadStatus {
this.cancelled = true;
}
- public void setCompletionDate(Date completionDate) {
- this.completionDate = (Date) completionDate.clone();
- }
-
public void setId(long id) {
this.id = id;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
index 0fc165183..c3b412012 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
@@ -26,26 +26,45 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
import org.antennapod.audio.MediaPlayer;
import de.danoeh.antennapod.core.util.playback.IPlayer;
+import java.util.concurrent.TimeUnit;
public class ExoPlayerWrapper implements IPlayer {
private final Context mContext;
+ private final Disposable bufferingUpdateDisposable;
private SimpleExoPlayer mExoPlayer;
private MediaSource mediaSource;
private MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener;
private MediaPlayer.OnCompletionListener audioCompletionListener;
private MediaPlayer.OnErrorListener audioErrorListener;
+ private MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener;
+
ExoPlayerWrapper(Context context) {
mContext = context;
mExoPlayer = createPlayer();
+
+ bufferingUpdateDisposable = Observable.interval(2, TimeUnit.SECONDS)
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(aLong -> {
+ if (bufferingUpdateListener != null) {
+ bufferingUpdateListener.onBufferingUpdate(null, mExoPlayer.getBufferedPercentage());
+ }
+ });
}
private SimpleExoPlayer createPlayer() {
+ DefaultLoadControl.Builder loadControl = new DefaultLoadControl.Builder();
+ loadControl.setBufferDurationsMs(30000, 120000,
+ DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
+ DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS);
SimpleExoPlayer p = ExoPlayerFactory.newSimpleInstance(mContext, new DefaultRenderersFactory(mContext),
- new DefaultTrackSelector(), new DefaultLoadControl());
+ new DefaultTrackSelector(), loadControl.createDefaultLoadControl());
p.setSeekParameters(SeekParameters.PREVIOUS_SYNC);
p.addListener(new Player.EventListener() {
@Override
@@ -150,12 +169,14 @@ public class ExoPlayerWrapper implements IPlayer {
@Override
public void release() {
+ bufferingUpdateDisposable.dispose();
if (mExoPlayer != null) {
mExoPlayer.release();
}
audioSeekCompleteListener = null;
audioCompletionListener = null;
audioErrorListener = null;
+ bufferingUpdateListener = null;
}
@Override
@@ -253,4 +274,8 @@ public class ExoPlayerWrapper implements IPlayer {
}
return mExoPlayer.getVideoFormat().height;
}
+
+ void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener) {
+ this.bufferingUpdateListener = bufferingUpdateListener;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
index 5177fa6be..9164f561f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
@@ -1025,6 +1025,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
ExoPlayerWrapper ap = (ExoPlayerWrapper) mp;
ap.setOnCompletionListener(audioCompletionListener);
ap.setOnSeekCompleteListener(audioSeekCompleteListener);
+ ap.setOnBufferingUpdateListener(audioBufferingUpdateListener);
ap.setOnErrorListener(audioErrorListener);
} else {
Log.w(TAG, "Unknown media player: " + mp);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index 7d23b4c4c..a3535616e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -1086,7 +1086,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
editor.putInt(
PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
- editor.commit();
+ editor.apply();
}
private void writePlayerStatusPlaybackPreferences() {
@@ -1095,11 +1095,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()).edit();
int playerStatus = getCurrentPlayerStatusAsInt(mediaPlayer.getPlayerStatus());
-
- editor.putInt(
- PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
-
- editor.commit();
+ editor.putInt(PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
+ editor.apply();
}
private void sendNotificationBroadcast(int type, int code) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
index 0e7339ac4..1a13fe5a7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
@@ -115,7 +115,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
stopCastingIntent.putExtra(PlaybackService.EXTRA_CAST_DISCONNECT, true);
PendingIntent stopCastingPendingIntent = PendingIntent.getService(context,
numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- addAction(R.drawable.ic_media_cast_disconnect,
+ addAction(R.drawable.ic_notification_cast_off,
context.getString(R.string.cast_disconnect_label),
stopCastingPendingIntent);
numActions++;
@@ -124,7 +124,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
// always let them rewind
PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_REWIND, numActions);
- addAction(android.R.drawable.ic_media_rew, context.getString(R.string.rewind_label), rewindButtonPendingIntent);
+ addAction(R.drawable.ic_notification_fast_rewind, context.getString(R.string.rewind_label), rewindButtonPendingIntent);
if (UserPreferences.showRewindOnCompactNotification()) {
compactActionList.add(numActions);
}
@@ -133,14 +133,14 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
if (playerStatus == PlayerStatus.PLAYING) {
PendingIntent pauseButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_PAUSE, numActions);
- addAction(android.R.drawable.ic_media_pause, //pause action
+ addAction(R.drawable.ic_notification_pause, //pause action
context.getString(R.string.pause_label),
pauseButtonPendingIntent);
compactActionList.add(numActions++);
} else {
PendingIntent playButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_PLAY, numActions);
- addAction(android.R.drawable.ic_media_play, //play action
+ addAction(R.drawable.ic_notification_play, //play action
context.getString(R.string.play_label),
playButtonPendingIntent);
compactActionList.add(numActions++);
@@ -149,7 +149,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
// ff follows play, then we have skip (if it's present)
PendingIntent ffButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions);
- addAction(android.R.drawable.ic_media_ff, context.getString(R.string.fast_forward_label), ffButtonPendingIntent);
+ addAction(R.drawable.ic_notification_fast_forward, context.getString(R.string.fast_forward_label), ffButtonPendingIntent);
if (UserPreferences.showFastForwardOnCompactNotification()) {
compactActionList.add(numActions);
}
@@ -158,7 +158,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
if (UserPreferences.isFollowQueue()) {
PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_NEXT, numActions);
- addAction(android.R.drawable.ic_media_next,
+ addAction(R.drawable.ic_notification_skip,
context.getString(R.string.skip_episode_label),
skipButtonPendingIntent);
if (UserPreferences.showSkipOnCompactNotification()) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
index e68bff16e..4c15f5f00 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
@@ -3,7 +3,7 @@ package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
-import android.support.annotation.Nullable;
+import android.os.Looper;
import android.util.Log;
import java.util.ArrayList;
@@ -144,58 +144,36 @@ public final class DBTasks {
private static final AtomicBoolean isRefreshing = new AtomicBoolean(false);
/**
- * Refreshes a given list of Feeds in a separate Thread. This method might ignore subsequent calls if it is still
+ * Refreshes all feeds.
+ * It must not be from the main thread.
+ * This method might ignore subsequent calls if it is still
* enqueuing Feeds for download from a previous call
*
* @param context Might be used for accessing the database
- * @param feeds List of Feeds that should be refreshed.
*/
- public static void refreshAllFeeds(final Context context, final List<Feed> feeds) {
- refreshAllFeeds(context, feeds, null);
- }
-
- /**
- * Refreshes a given list of Feeds in a separate Thread. This method might ignore subsequent calls if it is still
- * enqueuing Feeds for download from a previous call
- *
- * @param context Might be used for accessing the database
- * @param feeds List of Feeds that should be refreshed.
- * @param callback Called after everything was added enqueued for download. Might be null.
- */
- public static void refreshAllFeeds(final Context context, final List<Feed> feeds, @Nullable Runnable callback) {
+ public static void refreshAllFeeds(final Context context) {
if (!isRefreshing.compareAndSet(false, true)) {
Log.d(TAG, "Ignoring request to refresh all feeds: Refresh lock is locked");
return;
}
- new Thread(() -> {
- if (feeds != null) {
- refreshFeeds(context, feeds);
- } else {
- refreshFeeds(context, DBReader.getFeedList());
- }
- isRefreshing.set(false);
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ throw new IllegalStateException("DBTasks.refreshAllFeeds() must not be called from the main thread.");
+ }
- SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
- prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
+ refreshFeeds(context, DBReader.getFeedList());
+ isRefreshing.set(false);
- if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) {
- GpodnetSyncService.sendSyncIntent(context);
- }
- // Note: automatic download of episodes will be done but not here.
- // Instead it is done after all feeds have been refreshed (asynchronously),
- // in DownloadService.onDestroy()
- // See Issue #2577 for the details of the rationale
+ SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+ prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
- if (callback != null) {
- callback.run();
- }
- }).start();
- }
-
- public static long getLastRefreshAllFeedsTimeMillis(final Context context) {
- SharedPreferences prefs = context.getSharedPreferences(DBTasks.PREF_NAME, MODE_PRIVATE);
- return prefs.getLong(DBTasks.PREF_LAST_REFRESH, 0);
+ if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) {
+ GpodnetSyncService.sendSyncIntent(context);
+ }
+ // Note: automatic download of episodes will be done but not here.
+ // Instead it is done after all feeds have been refreshed (asynchronously),
+ // in DownloadService.onDestroy()
+ // See Issue #2577 for the details of the rationale
}
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
index 892a4675a..9c48f31dd 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
@@ -34,7 +34,6 @@ import de.danoeh.antennapod.core.util.URLChecker;
public class DownloadRequester {
private static final String TAG = "DownloadRequester";
- public static final String IMAGE_DOWNLOADPATH = "images/";
private static final String FEED_DOWNLOADPATH = "cache/";
private static final String MEDIA_DOWNLOADPATH = "media/";
@@ -274,10 +273,6 @@ public class DownloadRequester {
return item.getDownload_url() != null && downloads.containsKey(item.getDownload_url());
}
- public synchronized DownloadRequest getDownload(String downloadUrl) {
- return downloads.get(downloadUrl);
- }
-
/**
* Checks if feedfile with the given download url is in the downloads list
*/
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
index 13ea9daf0..a3271bcf9 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
@@ -55,16 +55,10 @@ public class PodDBAdapter {
*/
private static final int IN_OPERATOR_MAXIMUM = 800;
- /**
- * Maximum number of entries per search request.
- */
- public static final int SEARCH_LIMIT = 30;
-
// Key-constants
public static final String KEY_ID = "id";
public static final String KEY_TITLE = "title";
public static final String KEY_CUSTOM_TITLE = "custom_title";
- public static final String KEY_NAME = "name";
public static final String KEY_LINK = "link";
public static final String KEY_DESCRIPTION = "description";
public static final String KEY_FILE_URL = "file_url";
@@ -1400,13 +1394,6 @@ public class PodDBAdapter {
return db.rawQuery(query, null);
}
-
- public static final int IDX_FEEDSTATISTICS_FEED = 0;
- public static final int IDX_FEEDSTATISTICS_NUM_ITEMS = 1;
- public static final int IDX_FEEDSTATISTICS_NEW_ITEMS = 2;
- public static final int IDX_FEEDSTATISTICS_LATEST_EPISODE = 3;
- public static final int IDX_FEEDSTATISTICS_IN_PROGRESS_EPISODES = 4;
-
/**
* Select number of items, new items, the date of the latest episode and the number of episodes in progress. The result
* is sorted by the title of the feed.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java b/core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java
deleted file mode 100644
index 69dc38895..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/* Adapted from: http://thinking-in-code.blogspot.com/2008/11/duck-typing-in-java-using-dynamic.html */
-
-package de.danoeh.antennapod.core.util;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import de.danoeh.antennapod.core.BuildConfig;
-
-/**
- * Allows "duck typing" or dynamic invocation based on method signature rather
- * than type hierarchy. In other words, rather than checking whether something
- * IS-a duck, check whether it WALKS-like-a duck or QUACKS-like a duck.
- *
- * To use first use the coerce static method to indicate the object you want to
- * do Duck Typing for, then specify an interface to the to method which you want
- * to coerce the type to, e.g:
- *
- * public interface Foo { void aMethod(); } class Bar { ... public void
- * aMethod() { ... } ... } Bar bar = ...; Foo foo =
- * DuckType.coerce(bar).to(Foo.class); foo.aMethod();
- *
- *
- */
-public class DuckType {
-
- private final Object objectToCoerce;
-
- private DuckType(Object objectToCoerce) {
- this.objectToCoerce = objectToCoerce;
- }
-
- private class CoercedProxy implements InvocationHandler {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Method delegateMethod = findMethodBySignature(method);
- assert delegateMethod != null;
- return delegateMethod.invoke(DuckType.this.objectToCoerce, args);
- }
- }
-
- /**
- * Specify the duck typed object to coerce.
- *
- * @param object
- * the object to coerce
- * @return
- */
- public static DuckType coerce(Object object) {
- return new DuckType(object);
- }
-
- /**
- * Coerce the Duck Typed object to the given interface providing it
- * implements all the necessary methods.
- *
- * @param
- * @param iface
- * @return an instance of the given interface that wraps the duck typed
- * class
- * @throws ClassCastException
- * if the object being coerced does not implement all the
- * methods in the given interface.
- */
- @SuppressWarnings({ "rawtypes", "unchecked" })
- public <T> T to(Class iface) {
- if (BuildConfig.DEBUG && !iface.isInterface()) throw new AssertionError("cannot coerce object to a class, must be an interface");
- if (isA(iface)) {
- return (T) iface.cast(objectToCoerce);
- }
- if (quacksLikeA(iface)) {
- return generateProxy(iface);
- }
- throw new ClassCastException("Could not coerce object of type " + objectToCoerce.getClass() + " to " + iface);
- }
-
- @SuppressWarnings("rawtypes")
- private boolean isA(Class iface) {
- return objectToCoerce.getClass().isInstance(iface);
- }
-
- /**
- * Determine whether the duck typed object can be used with the given
- * interface.
- *
- * @param Type
- * of the interface to check.
- * @param iface
- * Interface class to check
- * @return true if the object will support all the methods in the interface,
- * false otherwise.
- */
- @SuppressWarnings("rawtypes")
- private boolean quacksLikeA(Class iface) {
- for (Method method : iface.getMethods()) {
- if (findMethodBySignature(method) == null) {
- return false;
- }
- }
- return true;
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private <T> T generateProxy(Class iface) {
- return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new CoercedProxy());
- }
-
- private Method findMethodBySignature(Method method) {
- try {
- return objectToCoerce.getClass().getMethod(method.getName(), method.getParameterTypes());
- } catch (NoSuchMethodException e) {
- return null;
- }
- }
-
-} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
index 826c06822..a8206d3bd 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
@@ -7,19 +7,6 @@ import de.danoeh.antennapod.core.feed.FeedItem;
public class FeedItemUtil {
private FeedItemUtil(){}
- public static int indexOfItemWithDownloadUrl(List<FeedItem> items, String downloadUrl) {
- if(items == null) {
- return -1;
- }
- for(int i=0; i < items.size(); i++) {
- FeedItem item = items.get(i);
- if(item.hasMedia() && item.getMedia().getDownload_url().equals(downloadUrl)) {
- return i;
- }
- }
- return -1;
- }
-
public static int indexOfItemWithId(List<FeedItem> items, long id) {
for(int i=0; i < items.size(); i++) {
FeedItem item = items.get(i);
@@ -40,17 +27,6 @@ public class FeedItemUtil {
return -1;
}
- public static long[] getIds(FeedItem... items) {
- if(items == null || items.length == 0) {
- return new long[0];
- }
- long[] result = new long[items.length];
- for(int i=0; i < items.length; i++) {
- result[i] = items[i].getId();
- }
- return result;
- }
-
public static long[] getIds(List<FeedItem> items) {
if(items == null || items.size() == 0) {
return new long[0];
@@ -62,20 +38,6 @@ public class FeedItemUtil {
return result;
}
- public static boolean containsAnyId(List<FeedItem> items, long[] ids) {
- if(items == null || items.size() == 0) {
- return false;
- }
- for(FeedItem item : items) {
- for(long id : ids) {
- if(item.getId() == id) {
- return true;
- }
- }
- }
- return false;
- }
-
/**
* Get the link for the feed item for the purpose of Share. It fallbacks to
* use the feed's link if the named feed item has no link.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java
deleted file mode 100644
index b425687ae..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import android.content.Context;
-import android.util.Log;
-
-import org.awaitility.core.ConditionTimeoutException;
-
-import java.util.concurrent.TimeUnit;
-
-import de.danoeh.antennapod.core.storage.DBTasks;
-
-import static org.awaitility.Awaitility.with;
-
-public class FeedUpdateUtils {
- private static final String TAG = "FeedUpdateUtils";
-
- private FeedUpdateUtils() {}
-
- public static void startAutoUpdate(Context context, Runnable callback) {
- // the network check is blocking for possibly a long time: so run the logic
- // in a separate thread to prevent the code blocking the callers
- final Runnable runnable = () -> {
- try {
- with().pollInterval(1, TimeUnit.SECONDS)
- .await()
- .atMost(10, TimeUnit.SECONDS)
- .until(() -> NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed());
- DBTasks.refreshAllFeeds(context, null, callback);
- } catch (ConditionTimeoutException ignore) {
- Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
- }
- };
- new Thread(runnable).start();
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java
deleted file mode 100644
index 29d095cd2..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import java.util.Comparator;
-
-import de.danoeh.antennapod.core.feed.Feed;
-
-/** Compares the title of two feeds for sorting. */
-class FeedtitleComparator implements Comparator<Feed> {
-
- @Override
- public int compare(Feed lhs, Feed rhs) {
- return lhs.getTitle().compareToIgnoreCase(rhs.getTitle());
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
index e81ab47ed..656b518bf 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
@@ -1,13 +1,20 @@
package de.danoeh.antennapod.core.util;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.util.Log;
+import android.widget.Toast;
+import de.danoeh.antennapod.core.R;
import java.util.List;
public class IntentUtils {
+ private static final String TAG = "IntentUtils";
+
private IntentUtils(){}
/*
@@ -28,4 +35,13 @@ public class IntentUtils {
context.sendBroadcast(new Intent(action).setPackage(context.getPackageName()));
}
+ public static void openInBrowser(Context context, String url) {
+ try {
+ Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ context.startActivity(myIntent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(context, R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java b/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
index 0fe11ec53..37f12c01c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
@@ -77,7 +77,7 @@ public final class Optional<T> {
* @param <T> Type of the non-existent value
* @return an empty {@code Optional}
*/
- public static<T> Optional<T> empty() {
+ public static <T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java
deleted file mode 100644
index 56a684475..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package de.danoeh.antennapod.core.util.comparator;
-
-import java.util.Comparator;
-
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.SearchResult;
-
-public class SearchResultValueComparator implements Comparator<SearchResult> {
-
- /**
- * Compare items based, first, on where they were found (ie. title, chapters, or show notes).
- * If they were found in the same section, then compare based on the title, in lexicographic
- * order. This is still not ideal since, for example, "#12 Example A" would be considered
- * before "#8 Example B" due to the fact that "8" has a larger unicode value than "1"
- */
- @Override
- public int compare(SearchResult lhs, SearchResult rhs) {
- int value = rhs.getValue() - lhs.getValue();
- if (value == 0 && lhs.getComponent() instanceof FeedItem && rhs.getComponent() instanceof FeedItem) {
- String lhsTitle = ((FeedItem) lhs.getComponent()).getTitle();
- String rhsTitle = ((FeedItem) rhs.getComponent()).getTitle();
- return lhsTitle.compareTo(rhsTitle);
- }
- return value;
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java
index 412b150fa..ebeec058d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java
@@ -1,21 +1,29 @@
package de.danoeh.antennapod.core.util.download;
+import android.content.Context;
+import android.support.annotation.NonNull;
import android.util.Log;
+
import androidx.work.Constraints;
+import androidx.work.Data;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.FeedUpdateWorker;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.FeedUpdateWorker;
+import de.danoeh.antennapod.core.storage.DBTasks;
+
public class AutoUpdateManager {
- private static final String WORK_ID_FEED_UPDATE = FeedUpdateWorker.class.getName();
+ private static final String WORK_ID_FEED_UPDATE = "de.danoeh.antennapod.core.service.FeedUpdateWorker";
+ private static final String WORK_ID_FEED_UPDATE_ONCE = WORK_ID_FEED_UPDATE + "Once";
private static final String TAG = "AutoUpdateManager";
private AutoUpdateManager() {
@@ -23,9 +31,25 @@ public class AutoUpdateManager {
}
/**
+ * Start / restart periodic auto feed refresh
+ */
+ public static void restartUpdateAlarm() {
+ if (UserPreferences.isAutoUpdateDisabled()) {
+ disableAutoUpdate();
+ } else if (UserPreferences.isAutoUpdateTimeOfDay()) {
+ int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
+ Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay));
+ restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]);
+ } else {
+ long milliseconds = UserPreferences.getUpdateInterval();
+ restartUpdateIntervalAlarm(milliseconds);
+ }
+ }
+
+ /**
* Sets the interval in which the feeds are refreshed automatically
*/
- public static void restartUpdateIntervalAlarm(long intervalMillis) {
+ private static void restartUpdateIntervalAlarm(long intervalMillis) {
Log.d(TAG, "Restarting update alarm.");
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(FeedUpdateWorker.class,
@@ -40,7 +64,7 @@ public class AutoUpdateManager {
/**
* Sets time of day the feeds are refreshed automatically
*/
- public static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) {
+ private static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) {
Log.d(TAG, "Restarting update alarm.");
Calendar now = Calendar.getInstance();
@@ -60,6 +84,41 @@ public class AutoUpdateManager {
WorkManager.getInstance().enqueueUniqueWork(WORK_ID_FEED_UPDATE, ExistingWorkPolicy.REPLACE, workRequest);
}
+ /**
+ * Run auto feed refresh once in background, as soon as what OS scheduling allows.
+ *
+ * Callers from UI should use {@link #runImmediate(Context)}, as it will guarantee
+ * the refresh be run immediately.
+ */
+ public static void runOnce() {
+ Log.d(TAG, "Run auto update once, as soon as OS allows.");
+
+ OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(FeedUpdateWorker.class)
+ .setConstraints(getConstraints())
+ .setInitialDelay(0L, TimeUnit.MILLISECONDS)
+ .setInputData(new Data.Builder()
+ .putBoolean(FeedUpdateWorker.PARAM_RUN_ONCE, true)
+ .build()
+ )
+ .build();
+
+ WorkManager.getInstance().enqueueUniqueWork(WORK_ID_FEED_UPDATE_ONCE, ExistingWorkPolicy.REPLACE, workRequest);
+
+ }
+
+ /**
+ /**
+ * Run auto feed refresh once in background immediately, using its own thread.
+ *
+ * Callers where the additional threads is not suitable should use {@link #runOnce()}
+ */
+ public static void runImmediate(@NonNull Context context) {
+ Log.d(TAG, "Run auto update immediately in background.");
+ new Thread(() -> {
+ DBTasks.refreshAllFeeds(context.getApplicationContext());
+ }, "ManualRefreshAllFeeds").start();
+ }
+
public static void disableAutoUpdate() {
WorkManager.getInstance().cancelUniqueWork(WORK_ID_FEED_UPDATE);
}
@@ -74,4 +133,5 @@ public class AutoUpdateManager {
}
return constraints.build();
}
+
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
index d22d08e09..a3f747e09 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
@@ -72,7 +72,7 @@ public class ChapterReader extends ID3Reader {
String decodedLink = URLDecoder.decode(link.toString(), "UTF-8");
currentChapter.setLink(decodedLink);
Log.d(TAG, "Found link: " + currentChapter.getLink());
- } catch (IllegalArgumentException _iae) {
+ } catch (IllegalArgumentException iae) {
Log.w(TAG, "Bad URL found in ID3 data");
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java
index 645bae5f3..9b644c3ba 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java
@@ -182,7 +182,7 @@ public class ExternalMedia implements Playable {
editor.putLong(PREF_LAST_PLAYED_TIME, timestamp);
position = newPosition;
lastPlayedTime = timestamp;
- editor.commit();
+ editor.apply();
}
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
index 0cfaaab3c..ac5418dd0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
@@ -728,6 +728,8 @@ public class PlaybackController {
public void setPlaybackSpeed(float speed) {
if (playbackService != null) {
playbackService.setSpeed(speed);
+ } else {
+ onPlaybackSpeedChange();
}
}
public void setSkipSilence(boolean skipSilence) {
diff --git a/core/src/main/res/drawable-hdpi/ic_baseline_question_answer_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_baseline_question_answer_white_24dp.png
deleted file mode 100755
index 67924a5a2..000000000
--- a/core/src/main/res/drawable-hdpi/ic_baseline_question_answer_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_cast_disabled_light.png b/core/src/main/res/drawable-hdpi/ic_cast_disabled_light.png
deleted file mode 100644
index c0a55d555..000000000
--- a/core/src/main/res/drawable-hdpi/ic_cast_disabled_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_cast_light.png b/core/src/main/res/drawable-hdpi/ic_cast_light.png
deleted file mode 100644
index b0c581a0e..000000000
--- a/core/src/main/res/drawable-hdpi/ic_cast_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_cast_off_light.png b/core/src/main/res/drawable-hdpi/ic_cast_off_light.png
deleted file mode 100644
index 5f3c0179c..000000000
--- a/core/src/main/res/drawable-hdpi/ic_cast_off_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_cast_on_0_light.png b/core/src/main/res/drawable-hdpi/ic_cast_on_0_light.png
deleted file mode 100644
index e872693a4..000000000
--- a/core/src/main/res/drawable-hdpi/ic_cast_on_0_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_cast_on_1_light.png b/core/src/main/res/drawable-hdpi/ic_cast_on_1_light.png
deleted file mode 100644
index d8be1ebc6..000000000
--- a/core/src/main/res/drawable-hdpi/ic_cast_on_1_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_cast_on_2_light.png b/core/src/main/res/drawable-hdpi/ic_cast_on_2_light.png
deleted file mode 100644
index 27cda9e9d..000000000
--- a/core/src/main/res/drawable-hdpi/ic_cast_on_2_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_cast_on_light.png b/core/src/main/res/drawable-hdpi/ic_cast_on_light.png
deleted file mode 100644
index 4ee525875..000000000
--- a/core/src/main/res/drawable-hdpi/ic_cast_on_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_close_light.png b/core/src/main/res/drawable-hdpi/ic_close_light.png
deleted file mode 100644
index 93187e450..000000000
--- a/core/src/main/res/drawable-hdpi/ic_close_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_grey600_24dp.png
deleted file mode 100644
index 3668c9a00..000000000
--- a/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_white_24dp.png
deleted file mode 100644
index a1a2c5b68..000000000
--- a/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_forum_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_forum_grey600_24dp.png
deleted file mode 100644
index da5398d15..000000000
--- a/core/src/main/res/drawable-hdpi/ic_forum_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_media_cast_disconnect.png b/core/src/main/res/drawable-hdpi/ic_media_cast_disconnect.png
deleted file mode 100644
index 700c116e5..000000000
--- a/core/src/main/res/drawable-hdpi/ic_media_cast_disconnect.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_pause_light.png b/core/src/main/res/drawable-hdpi/ic_pause_light.png
deleted file mode 100644
index 0c505d1c8..000000000
--- a/core/src/main/res/drawable-hdpi/ic_pause_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_play_light.png b/core/src/main/res/drawable-hdpi/ic_play_light.png
deleted file mode 100644
index 7957dff5b..000000000
--- a/core/src/main/res/drawable-hdpi/ic_play_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_stat_antenna_default.png b/core/src/main/res/drawable-hdpi/ic_stat_antenna_default.png
deleted file mode 100644
index af99f4b3b..000000000
--- a/core/src/main/res/drawable-hdpi/ic_stat_antenna_default.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_stat_authentication.png b/core/src/main/res/drawable-hdpi/ic_stat_authentication.png
deleted file mode 100644
index 398dffa4b..000000000
--- a/core/src/main/res/drawable-hdpi/ic_stat_authentication.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-ldpi/ic_stat_antenna_default.png b/core/src/main/res/drawable-ldpi/ic_stat_antenna_default.png
deleted file mode 100644
index ddf545c0b..000000000
--- a/core/src/main/res/drawable-ldpi/ic_stat_antenna_default.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_baseline_question_answer_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_baseline_question_answer_white_24dp.png
deleted file mode 100755
index e87df752e..000000000
--- a/core/src/main/res/drawable-mdpi/ic_baseline_question_answer_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_cast_disabled_light.png b/core/src/main/res/drawable-mdpi/ic_cast_disabled_light.png
deleted file mode 100644
index 7940a0332..000000000
--- a/core/src/main/res/drawable-mdpi/ic_cast_disabled_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_cast_light.png b/core/src/main/res/drawable-mdpi/ic_cast_light.png
deleted file mode 100644
index 1f5bec20b..000000000
--- a/core/src/main/res/drawable-mdpi/ic_cast_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_cast_off_light.png b/core/src/main/res/drawable-mdpi/ic_cast_off_light.png
deleted file mode 100644
index 963db27d4..000000000
--- a/core/src/main/res/drawable-mdpi/ic_cast_off_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_cast_on_0_light.png b/core/src/main/res/drawable-mdpi/ic_cast_on_0_light.png
deleted file mode 100644
index a90d9e305..000000000
--- a/core/src/main/res/drawable-mdpi/ic_cast_on_0_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_cast_on_1_light.png b/core/src/main/res/drawable-mdpi/ic_cast_on_1_light.png
deleted file mode 100644
index bb2cf30bf..000000000
--- a/core/src/main/res/drawable-mdpi/ic_cast_on_1_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_cast_on_2_light.png b/core/src/main/res/drawable-mdpi/ic_cast_on_2_light.png
deleted file mode 100644
index 3ed59e55b..000000000
--- a/core/src/main/res/drawable-mdpi/ic_cast_on_2_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_cast_on_light.png b/core/src/main/res/drawable-mdpi/ic_cast_on_light.png
deleted file mode 100644
index 713427b97..000000000
--- a/core/src/main/res/drawable-mdpi/ic_cast_on_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_close_light.png b/core/src/main/res/drawable-mdpi/ic_close_light.png
deleted file mode 100644
index 2c52c9b0f..000000000
--- a/core/src/main/res/drawable-mdpi/ic_close_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_grey600_24dp.png
deleted file mode 100644
index 726eae499..000000000
--- a/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_white_24dp.png
deleted file mode 100644
index 0cc401dff..000000000
--- a/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_forum_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_forum_grey600_24dp.png
deleted file mode 100644
index d3bcfe7b6..000000000
--- a/core/src/main/res/drawable-mdpi/ic_forum_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_media_cast_disconnect.png b/core/src/main/res/drawable-mdpi/ic_media_cast_disconnect.png
deleted file mode 100644
index 767f420df..000000000
--- a/core/src/main/res/drawable-mdpi/ic_media_cast_disconnect.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_pause_light.png b/core/src/main/res/drawable-mdpi/ic_pause_light.png
deleted file mode 100644
index 6218a774f..000000000
--- a/core/src/main/res/drawable-mdpi/ic_pause_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_play_light.png b/core/src/main/res/drawable-mdpi/ic_play_light.png
deleted file mode 100644
index 1e0ccaf80..000000000
--- a/core/src/main/res/drawable-mdpi/ic_play_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_stat_antenna_default.png b/core/src/main/res/drawable-mdpi/ic_stat_antenna_default.png
deleted file mode 100644
index 41fd20655..000000000
--- a/core/src/main/res/drawable-mdpi/ic_stat_antenna_default.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_stat_authentication.png b/core/src/main/res/drawable-mdpi/ic_stat_authentication.png
deleted file mode 100644
index 550b56b33..000000000
--- a/core/src/main/res/drawable-mdpi/ic_stat_authentication.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_baseline_question_answer_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_baseline_question_answer_white_24dp.png
deleted file mode 100755
index 731f89c83..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_baseline_question_answer_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_cast_disabled_light.png b/core/src/main/res/drawable-xhdpi/ic_cast_disabled_light.png
deleted file mode 100644
index fbb3e062c..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_cast_disabled_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_cast_light.png b/core/src/main/res/drawable-xhdpi/ic_cast_light.png
deleted file mode 100644
index f2713e20e..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_cast_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_cast_off_light.png b/core/src/main/res/drawable-xhdpi/ic_cast_off_light.png
deleted file mode 100644
index f4f8aaea8..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_cast_off_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_cast_on_0_light.png b/core/src/main/res/drawable-xhdpi/ic_cast_on_0_light.png
deleted file mode 100644
index 247fc95ba..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_cast_on_0_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_cast_on_1_light.png b/core/src/main/res/drawable-xhdpi/ic_cast_on_1_light.png
deleted file mode 100644
index ecf4b4723..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_cast_on_1_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_cast_on_2_light.png b/core/src/main/res/drawable-xhdpi/ic_cast_on_2_light.png
deleted file mode 100644
index 60e3afa5d..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_cast_on_2_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_cast_on_light.png b/core/src/main/res/drawable-xhdpi/ic_cast_on_light.png
deleted file mode 100644
index 40ce9d4f2..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_cast_on_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_close_light.png b/core/src/main/res/drawable-xhdpi/ic_close_light.png
deleted file mode 100644
index 49faa429a..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_close_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_grey600_24dp.png
deleted file mode 100644
index 322adb6e0..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_white_24dp.png
deleted file mode 100644
index c25860017..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_forum_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_forum_grey600_24dp.png
deleted file mode 100644
index ac6876140..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_forum_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_media_cast_disconnect.png b/core/src/main/res/drawable-xhdpi/ic_media_cast_disconnect.png
deleted file mode 100644
index 740867129..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_media_cast_disconnect.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_pause_light.png b/core/src/main/res/drawable-xhdpi/ic_pause_light.png
deleted file mode 100644
index 40cd79f14..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_pause_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_play_light.png b/core/src/main/res/drawable-xhdpi/ic_play_light.png
deleted file mode 100644
index 33f6a5919..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_play_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_stat_antenna_default.png b/core/src/main/res/drawable-xhdpi/ic_stat_antenna_default.png
deleted file mode 100644
index 30431ed6a..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_stat_antenna_default.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_stat_authentication.png b/core/src/main/res/drawable-xhdpi/ic_stat_authentication.png
deleted file mode 100644
index e83cbc333..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_stat_authentication.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_baseline_question_answer_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_baseline_question_answer_white_24dp.png
deleted file mode 100755
index 255b82707..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_baseline_question_answer_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_cast_disabled_light.png b/core/src/main/res/drawable-xxhdpi/ic_cast_disabled_light.png
deleted file mode 100644
index e94df3889..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_cast_disabled_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_cast_light.png b/core/src/main/res/drawable-xxhdpi/ic_cast_light.png
deleted file mode 100644
index c5722a6eb..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_cast_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_cast_off_light.png b/core/src/main/res/drawable-xxhdpi/ic_cast_off_light.png
deleted file mode 100644
index 92ac67b34..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_cast_off_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_cast_on_0_light.png b/core/src/main/res/drawable-xxhdpi/ic_cast_on_0_light.png
deleted file mode 100644
index 2742fcb4a..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_cast_on_0_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_cast_on_1_light.png b/core/src/main/res/drawable-xxhdpi/ic_cast_on_1_light.png
deleted file mode 100644
index 405178e64..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_cast_on_1_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_cast_on_2_light.png b/core/src/main/res/drawable-xxhdpi/ic_cast_on_2_light.png
deleted file mode 100644
index dfe52428d..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_cast_on_2_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_cast_on_light.png b/core/src/main/res/drawable-xxhdpi/ic_cast_on_light.png
deleted file mode 100644
index 7e69a0864..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_cast_on_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_close_light.png b/core/src/main/res/drawable-xxhdpi/ic_close_light.png
deleted file mode 100644
index be519bfcb..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_close_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_grey600_24dp.png
deleted file mode 100644
index 87f8073ea..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_white_24dp.png
deleted file mode 100644
index da3433c61..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_forum_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_forum_grey600_24dp.png
deleted file mode 100644
index 7a3204693..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_forum_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_media_cast_disconnect.png b/core/src/main/res/drawable-xxhdpi/ic_media_cast_disconnect.png
deleted file mode 100644
index 2d2ec9035..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_media_cast_disconnect.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_pause_light.png b/core/src/main/res/drawable-xxhdpi/ic_pause_light.png
deleted file mode 100644
index a36d4d11e..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_pause_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_play_light.png b/core/src/main/res/drawable-xxhdpi/ic_play_light.png
deleted file mode 100644
index b1424874a..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_play_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_stat_authentication.png b/core/src/main/res/drawable-xxhdpi/ic_stat_authentication.png
deleted file mode 100755
index 965fabc57..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_stat_authentication.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_baseline_question_answer_white_24db.png b/core/src/main/res/drawable-xxxhdpi/ic_baseline_question_answer_white_24db.png
deleted file mode 100755
index 0d697e0f9..000000000
--- a/core/src/main/res/drawable-xxxhdpi/ic_baseline_question_answer_white_24db.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_close_light.png b/core/src/main/res/drawable-xxxhdpi/ic_close_light.png
deleted file mode 100644
index 679c2a4d5..000000000
--- a/core/src/main/res/drawable-xxxhdpi/ic_close_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_grey600_24dp.png
deleted file mode 100644
index c56590fe0..000000000
--- a/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_white_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_white_24dp.png
deleted file mode 100644
index 5deea3286..000000000
--- a/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_forum_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_forum_grey600_24dp.png
deleted file mode 100644
index 0ae33696b..000000000
--- a/core/src/main/res/drawable-xxxhdpi/ic_forum_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_pause_light.png b/core/src/main/res/drawable-xxxhdpi/ic_pause_light.png
deleted file mode 100644
index 7de2ef4ed..000000000
--- a/core/src/main/res/drawable-xxxhdpi/ic_pause_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_play_light.png b/core/src/main/res/drawable-xxxhdpi/ic_play_light.png
deleted file mode 100644
index 4428c8477..000000000
--- a/core/src/main/res/drawable-xxxhdpi/ic_play_light.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable/ic_antenna.xml b/core/src/main/res/drawable/ic_antenna.xml
new file mode 100644
index 000000000..9fcfab000
--- /dev/null
+++ b/core/src/main/res/drawable/ic_antenna.xml
@@ -0,0 +1,6 @@
+<vector android:height="24dp" android:viewportHeight="12.7"
+ android:viewportWidth="12.7" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillAlpha="1" android:fillColor="#ffffff"
+ android:pathData="m6.0631,0.4728v0.3274c1.1582,0.0249 1.911,0.4225 2.5991,1.1189 0.6881,0.6964 1.0924,1.7043 1.1125,2.9246h0.3211c0.0078,-1.3792 -0.5291,-2.4905 -1.1981,-3.1576C8.2288,1.019 7.3415,0.4734 6.0631,0.4728ZM6.0631,1.4283v0.3453c0.859,0.0361 1.3465,0.2123 1.9398,0.8081 0.5933,0.5957 0.843,1.3669 0.8598,2.2621L9.2029,4.8438c-0.0088,-1.2333 -0.5414,-2.0907 -0.9568,-2.5047 -0.4154,-0.4139 -0.9948,-0.9065 -2.183,-0.9108zM6.0625,2.4323 L6.0631,2.7495c0.3968,0.007 0.8308,0.1395 1.2089,0.5642 0.3781,0.4247 0.495,1.0244 0.51,1.53h0.3255c-0.0016,-0.669 -0.2787,-1.3891 -0.6153,-1.747 -0.3366,-0.358 -0.7368,-0.6621 -1.4298,-0.6645zM6.0906,3.7766c-0.4059,0.0002 -0.7349,0.3294 -0.7347,0.7353 0.0001,0.2677 0.1459,0.5142 0.3804,0.6434l-3.0102,6.2227 0.5151,0.3351 0.607,-1.2485 5.3821,1.5453 0.083,0.1609 0.5732,-0.2508 -3.4927,-6.7397c0.2624,-0.1189 0.4311,-0.3802 0.4315,-0.6683 0.0002,-0.4059 -0.3287,-0.7352 -0.7347,-0.7353zM6.065,5.8631 L6.5929,6.8882 5.2882,7.4761zM6.6976,7.0918 L7.6065,8.8561 5.137,7.8016zM5.0259,8.0199 L7.611,9.1184 4.0314,10.0854zM7.8395,9.3086 L9.081,11.7201 4.1489,10.3069z"
+ android:strokeAlpha="1" android:strokeColor="#00000000" android:strokeWidth="0.32680494"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_chat_grey600.xml b/core/src/main/res/drawable/ic_chat_grey600.xml
new file mode 100644
index 000000000..ebae2dbed
--- /dev/null
+++ b/core/src/main/res/drawable/ic_chat_grey600.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FF757575" android:pathData="M21,6h-2v9L6,15v2c0,0.55 0.45,1 1,1h11l4,4L22,7c0,-0.55 -0.45,-1 -1,-1zM17,12L17,3c0,-0.55 -0.45,-1 -1,-1L3,2c-0.55,0 -1,0.45 -1,1v14l4,-4h10c0.55,0 1,-0.45 1,-1z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_chat_white.xml b/core/src/main/res/drawable/ic_chat_white.xml
new file mode 100644
index 000000000..45691c525
--- /dev/null
+++ b/core/src/main/res/drawable/ic_chat_white.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M21,6h-2v9L6,15v2c0,0.55 0.45,1 1,1h11l4,4L22,7c0,-0.55 -0.45,-1 -1,-1zM17,12L17,3c0,-0.55 -0.45,-1 -1,-1L3,2c-0.55,0 -1,0.45 -1,1v14l4,-4h10c0.55,0 1,-0.45 1,-1z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification.png b/core/src/main/res/drawable/ic_notification.png
deleted file mode 100644
index 8bd22b54a..000000000
--- a/core/src/main/res/drawable/ic_notification.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable/ic_notification_cast_off.xml b/core/src/main/res/drawable/ic_notification_cast_off.xml
new file mode 100644
index 000000000..63a21fbe2
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_cast_off.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M1.6,1.27L0.25,2.75L1.41,3.8C1.16,4.13 1,4.55 1,5V8H3V5.23L18.2,19H14V21H20.41L22.31,22.72L23.65,21.24M6.5,3L8.7,5H21V16.14L23,17.95V5C23,3.89 22.1,3 21,3M1,10V12A9,9 0 0,1 10,21H12C12,14.92 7.08,10 1,10M1,14V16A5,5 0 0,1 6,21H8A7,7 0 0,0 1,14M1,18V21H4A3,3 0 0,0 1,18Z" />
+</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_notification_fast_forward.xml b/core/src/main/res/drawable/ic_notification_fast_forward.xml
new file mode 100644
index 000000000..bf564977c
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_fast_forward.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification_fast_rewind.xml b/core/src/main/res/drawable/ic_notification_fast_rewind.xml
new file mode 100644
index 000000000..847159cc5
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_fast_rewind.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification_key.xml b/core/src/main/res/drawable/ic_notification_key.xml
new file mode 100644
index 000000000..c8a817eeb
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_key.xml
@@ -0,0 +1,6 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF"
+ android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification_pause.xml b/core/src/main/res/drawable/ic_notification_pause.xml
new file mode 100644
index 000000000..d46efb2f5
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_pause.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification_play.xml b/core/src/main/res/drawable/ic_notification_play.xml
new file mode 100644
index 000000000..d571460c6
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_play.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification_skip.xml b/core/src/main/res/drawable/ic_notification_skip.xml
new file mode 100644
index 000000000..0c65448cc
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_skip.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_playback_speed_dark.xml b/core/src/main/res/drawable/ic_playback_speed_dark.xml
new file mode 100644
index 000000000..7c7d1cf8c
--- /dev/null
+++ b/core/src/main/res/drawable/ic_playback_speed_dark.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="#FF757575" android:pathData="M 12,14.888154 A 2.2284437,2.2284437 0 0 1 9.7715563,12.659711 c 0,-0.831952 0.4531167,-1.559911 1.1142217,-1.938746 L 18.098507,6.5463469 13.990743,13.66251 C 13.619336,14.390469 12.869093,14.888154 12,14.888154 m 0,-9.6565888 c 1.344494,0 2.599851,0.3714073 3.691789,0.9805151 L 14.131878,7.110886 C 13.485629,6.858329 12.742815,6.7171943 12,6.7171943 A 5.9425165,5.9425165 0 0 0 6.0574835,12.659711 c 0,1.64162 0.661105,3.127249 1.7381861,4.196902 h 0.00743 c 0.2896977,0.289697 0.2896977,0.757671 0,1.047369 -0.2896977,0.289697 -0.7650991,0.289697 -1.0547967,0.0075 v 0 C 5.4038067,16.566915 4.5718544,14.709879 4.5718544,12.659711 A 7.4281456,7.4281456 0 0 1 12,5.2315652 m 7.428145,7.4281458 c 0,2.050168 -0.831952,3.907204 -2.176446,5.251699 v 0 c -0.289698,0.282269 -0.757671,0.282269 -1.047369,-0.0075 -0.289697,-0.289698 -0.289697,-0.757671 0,-1.047368 v 0 c 1.077082,-1.077082 1.738186,-2.555282 1.738186,-4.196902 0,-0.742815 -0.141134,-1.48563 -0.401119,-2.154163 l 0.898805,-1.5599106 c 0.616537,1.1142216 0.987943,2.3621496 0.987943,3.7140736 z" />
+</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_playback_speed_white.xml b/core/src/main/res/drawable/ic_playback_speed_white.xml
new file mode 100644
index 000000000..cc6af0d55
--- /dev/null
+++ b/core/src/main/res/drawable/ic_playback_speed_white.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="48dp"
+ android:width="48dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="#ffffffff" android:pathData="M 12,14.888154 A 2.2284437,2.2284437 0 0 1 9.7715563,12.659711 c 0,-0.831952 0.4531167,-1.559911 1.1142217,-1.938746 L 18.098507,6.5463469 13.990743,13.66251 C 13.619336,14.390469 12.869093,14.888154 12,14.888154 m 0,-9.6565888 c 1.344494,0 2.599851,0.3714073 3.691789,0.9805151 L 14.131878,7.110886 C 13.485629,6.858329 12.742815,6.7171943 12,6.7171943 A 5.9425165,5.9425165 0 0 0 6.0574835,12.659711 c 0,1.64162 0.661105,3.127249 1.7381861,4.196902 h 0.00743 c 0.2896977,0.289697 0.2896977,0.757671 0,1.047369 -0.2896977,0.289697 -0.7650991,0.289697 -1.0547967,0.0075 v 0 C 5.4038067,16.566915 4.5718544,14.709879 4.5718544,12.659711 A 7.4281456,7.4281456 0 0 1 12,5.2315652 m 7.428145,7.4281458 c 0,2.050168 -0.831952,3.907204 -2.176446,5.251699 v 0 c -0.289698,0.282269 -0.757671,0.282269 -1.047369,-0.0075 -0.289697,-0.289698 -0.289697,-0.757671 0,-1.047368 v 0 c 1.077082,-1.077082 1.738186,-2.555282 1.738186,-4.196902 0,-0.742815 -0.141134,-1.48563 -0.401119,-2.154163 l 0.898805,-1.5599106 c 0.616537,1.1142216 0.987943,2.3621496 0.987943,3.7140736 z" />
+</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_questionmark_grey600.xml b/core/src/main/res/drawable/ic_questionmark_grey600.xml
new file mode 100644
index 000000000..0907fcdec
--- /dev/null
+++ b/core/src/main/res/drawable/ic_questionmark_grey600.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FF757575" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_questionmark_white.xml b/core/src/main/res/drawable/ic_questionmark_white.xml
new file mode 100644
index 000000000..69b42fef8
--- /dev/null
+++ b/core/src/main/res/drawable/ic_questionmark_white.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_warning_red.xml b/core/src/main/res/drawable/ic_warning_red.xml
new file mode 100644
index 000000000..475a41bbb
--- /dev/null
+++ b/core/src/main/res/drawable/ic_warning_red.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#FF0000"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FF000000" android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
+</vector>
diff --git a/core/src/main/res/drawable/white_circle.xml b/core/src/main/res/drawable/white_circle.xml
deleted file mode 100644
index 597b70a2d..000000000
--- a/core/src/main/res/drawable/white_circle.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval" >
-
- <solid android:color="@color/white" />
-
- <size
- android:height="4dp"
- android:width="4dp" />
-
-</shape> \ No newline at end of file
diff --git a/core/src/main/res/values-id/strings.xml b/core/src/main/res/values-in/strings.xml
index 2f1ffecea..2f1ffecea 100644
--- a/core/src/main/res/values-id/strings.xml
+++ b/core/src/main/res/values-in/strings.xml
diff --git a/core/src/main/res/values-land/styles.xml b/core/src/main/res/values-land/styles.xml
deleted file mode 100644
index d964ef3d4..000000000
--- a/core/src/main/res/values-land/styles.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <style name="Theme.MediaPlayer" parent="@style/Theme.AppCompat.Light">
- <item name="android:windowActionBarOverlay">true</item>
- </style>
-</resources> \ No newline at end of file
diff --git a/core/src/main/res/values-large/dimens.xml b/core/src/main/res/values-large/dimens.xml
index 2da283c5b..2d107eef0 100644
--- a/core/src/main/res/values-large/dimens.xml
+++ b/core/src/main/res/values-large/dimens.xml
@@ -4,5 +4,4 @@
<dimen name="thumbnail_length">170dp</dimen>
<dimen name="thumbnail_length_queue_item">64dp</dimen>
<dimen name="thumbnail_length_downloaded_item">64dp</dimen>
- <dimen name="queue_title_text_size">@dimen/text_size_medium</dimen>
</resources> \ No newline at end of file
diff --git a/core/src/main/res/values-v16/styles.xml b/core/src/main/res/values-v16/styles.xml
index a92790152..947e43f38 100644
--- a/core/src/main/res/values-v16/styles.xml
+++ b/core/src/main/res/values-v16/styles.xml
@@ -6,12 +6,4 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:fontFamily">sans-serif-light</item>
</style>
-
- <style name="AntennaPod.Dialog.Title" parent="@android:style/TextAppearance.Medium">
- <item name="android:textSize">@dimen/text_size_medium</item>
- <item name="android:textColor">@color/holo_blue_light</item>
- <item name="android:maxLines">2</item>
- <item name="android:ellipsize">end</item>
- <item name="android:fontFamily">sans-serif-light</item>
- </style>
</resources> \ No newline at end of file
diff --git a/core/src/main/res/values-v19/colors.xml b/core/src/main/res/values-v19/colors.xml
index 16c065d75..4154280e8 100644
--- a/core/src/main/res/values-v19/colors.xml
+++ b/core/src/main/res/values-v19/colors.xml
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="selection_background_color_dark">#484B4D</color>
- <color name="selection_background_color_light">#E3E3E3</color>
</resources> \ No newline at end of file
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index 0eaf0e5e7..5e7eab1ea 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -156,19 +156,15 @@
<item>4.00</item>
</string-array>
- <string-array name="autodl_select_networks_default_entries">
- <item>N/A</item>
- </string-array>
- <string-array name="autodl_select_networks_default_values">
- <item>0</item>
- </string-array>
-
<string-array name="theme_options">
+ <item>@string/pref_theme_title_use_system</item>
<item>@string/pref_theme_title_light</item>
<item>@string/pref_theme_title_dark</item>
<item>@string/pref_theme_title_trueblack</item>
</string-array>
+
<string-array name="theme_values">
+ <item>system</item>
<item>0</item>
<item>1</item>
<item>2</item>
diff --git a/core/src/main/res/values/attrs.xml b/core/src/main/res/values/attrs.xml
index 5b0bf5717..530b40d46 100644
--- a/core/src/main/res/values/attrs.xml
+++ b/core/src/main/res/values/attrs.xml
@@ -9,6 +9,7 @@
<attr name="av_fast_forward" format="reference"/>
<attr name="av_pause" format="reference"/>
<attr name="av_play" format="reference"/>
+ <attr name="av_speed" format="reference"/>
<attr name="av_rewind" format="reference"/>
<attr name="content_discard" format="reference"/>
<attr name="content_new" format="reference"/>
@@ -56,9 +57,9 @@
<attr name="ic_cast_disconnect" format="reference"/>
<attr name="ic_swap" format="reference"/>
<attr name="ic_cellphone_text" format="reference"/>
- <attr name="ic_question_answer" format="reference" />
+ <attr name="ic_questionmark" format="reference" />
+ <attr name="ic_chat" format="reference"/>
<attr name="ic_bug" format="reference" />
- <attr name="ic_known_issues" format="reference" />
<attr name="master_switch_background" format="color"/>
<attr name="currently_playing_background" format="color"/>
<attr name="ic_bookmark" format="reference"/>
diff --git a/core/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml
index 0e4533977..fea7da4a4 100644
--- a/core/src/main/res/values/colors.xml
+++ b/core/src/main/res/values/colors.xml
@@ -2,22 +2,16 @@
<resources>
<color name="white">#FFFFFF</color>
- <color name="gray">#808080</color>
<color name="grey600">#757575</color>
<color name="light_gray">#bfbfbf</color>
<color name="black">#000000</color>
<color name="holo_blue_light">#33B5E5</color>
<color name="holo_blue_dark">#0099CC</color>
- <color name="ics_gray">#858585</color>
- <color name="actionbar_gray">#DDDDDD</color>
<color name="download_success_green">#669900</color>
<color name="download_failed_red">#CC0000</color>
<color name="status_progress">#E033B5E5</color>
- <color name="status_playing">#E0EE5F52</color>
<color name="overlay_dark">#2C2C2C</color>
<color name="overlay_light">#FFFFFF</color>
- <color name="swipe_refresh_secondary_color_light">#EDEDED</color>
- <color name="swipe_refresh_secondary_color_dark">#060708</color>
<color name="new_indicator_green">#669900</color>
<color name="image_readability_tint">#80000000</color>
<color name="feed_image_bg">#50000000</color>
diff --git a/core/src/main/res/values/dimens.xml b/core/src/main/res/values/dimens.xml
index cdde0027d..02c398b62 100644
--- a/core/src/main/res/values/dimens.xml
+++ b/core/src/main/res/values/dimens.xml
@@ -10,7 +10,6 @@
<dimen name="text_size_navdrawer">16sp</dimen>
<dimen name="text_size_medium">18sp</dimen>
<dimen name="text_size_large">22sp</dimen>
- <dimen name="status_indicator_width">32dp</dimen>
<dimen name="thumbnail_length_itemlist">64dp</dimen>
<dimen name="thumbnail_length_queue_item">64dp</dimen>
<dimen name="thumbnail_length_downloaded_item">64dp</dimen>
@@ -21,7 +20,6 @@
<dimen name="drawer_width">280dp</dimen>
<dimen name="listitem_iconwithtext_height">48dp</dimen>
<dimen name="listitem_iconwithtext_textleftpadding">16dp</dimen>
- <dimen name="listitem_iconwithtext_textverticalpadding">16dp</dimen>
<dimen name="listitem_threeline_textleftpadding">16dp</dimen>
<dimen name="listitem_threeline_textrightpadding">8dp</dimen>
@@ -29,7 +27,6 @@
<dimen name="listitem_threeline_horizontalpadding">16dp</dimen>
<dimen name="list_vertical_padding">8dp</dimen>
- <dimen name="minimum_text_margin">8dp</dimen>
<dimen name="listitem_icon_leftpadding">16dp</dimen>
<dimen name="listitem_icon_rightpadding">16dp</dimen>
diff --git a/core/src/main/res/values/integers.xml b/core/src/main/res/values/integers.xml
index 33501d9fb..73d90cf98 100644
--- a/core/src/main/res/values/integers.xml
+++ b/core/src/main/res/values/integers.xml
@@ -1,4 +1,3 @@
<resources>
- <integer name="undobar_hide_delay">5000</integer>
<integer name="episode_cache_size_unlimited">-1</integer>
</resources>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index c9c6506a9..adf938d75 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -27,10 +27,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Synchronize with other devices</string>
<string name="gpodnet_auth_label">gpodder.net Login</string>
- <string name="free_space_label">%1$s free</string>
<string name="episode_cache_full_title">Episode cache full</string>
<string name="episode_cache_full_message">The episode cache limit has been reached. You can increase the cache size in the Settings.</string>
- <string name="synchronizing">Synchronizing…</string>
<!-- Statistics fragment -->
<string name="total_time_listened_to_podcasts">Total time of podcasts played:</string>
@@ -73,7 +71,6 @@
<string name="author_label">Author(s)</string>
<string name="language_label">Language</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Settings</string>
<string name="cover_label">Picture</string>
<string name="error_label">Error</string>
<string name="error_msg_prefix">An error occurred:</string>
@@ -82,14 +79,9 @@
<string name="external_storage_error_msg">No external storage is available. Please make sure that external storage is mounted so that the app can work properly.</string>
<string name="chapters_label">Chapters</string>
<string name="chapter_duration">Duration: %1$s</string>
- <string name="shownotes_label">Shownotes</string>
<string name="description_label">Description</string>
- <string name="most_recent_prefix">Most recent episode:\u0020</string>
<string name="episodes_suffix">\u0020episodes</string>
- <string name="length_prefix">Length:\u0020</string>
- <string name="size_prefix">Size:\u0020</string>
<string name="processing_label">Processing</string>
- <string name="loading_label">Loading&#8230;</string>
<string name="save_username_password_label">Save username and password</string>
<string name="close_label">Close</string>
<string name="retry_label">Retry</string>
@@ -119,8 +111,6 @@
<string name="feedurl_label">Feed URL</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Add Podcast by URL</string>
- <string name="podcastdirectories_label">Find Podcast in Directory</string>
- <string name="podcastdirectories_descr">For new podcasts, you can search iTunes or fyyd, or browse gpodder.net by name, category or popularity.</string>
<string name="browse_gpoddernet_label">Browse gpodder.net</string>
<string name="discover">Discover</string>
<string name="discover_more">more »</string>
@@ -143,13 +133,13 @@
<string name="share_link_label">Share Episode URL</string>
<string name="share_link_with_position_label">Share Episode URL with Position</string>
<string name="share_file_label">Share File</string>
+ <string name="share_website_url_label">Share Website URL</string>
<string name="share_feed_url_label">Share Feed URL</string>
<string name="share_item_url_label">Share Media File URL</string>
<string name="share_item_url_with_position_label">Share Media File URL with Position</string>
<string name="feed_delete_confirmation_msg">Please confirm that you want to delete the podcast \"%1$s\" and ALL its episodes (including downloaded episodes).</string>
<string name="feed_remover_msg">Removing podcast</string>
<string name="load_complete_feed">Refresh complete podcast</string>
- <string name="hide_episodes_title">Hide Episodes</string>
<string name="batch_edit">Batch edit</string>
<string name="select_all_above">Select all above</string>
<string name="select_all_below">Select all below</string>
@@ -174,9 +164,7 @@
</plurals>
<string name="play_label">Play</string>
<string name="pause_label">Pause</string>
- <string name="stop_label">Stop</string>
<string name="stream_label">Stream</string>
- <string name="remove_label">Remove</string>
<string name="delete_label">Delete</string>
<string name="delete_failed">Unable to delete file. Rebooting the device could help.</string>
<string name="delete_episode_label">Delete Episode</string>
@@ -221,14 +209,12 @@
<!-- Download messages and labels -->
<string name="download_successful">successful</string>
- <string name="download_failed">failed</string>
<string name="download_pending">Download pending</string>
<string name="download_running">Download running</string>
<string name="download_error_details">Details</string>
<string name="download_error_details_message">%1$s \n\nFile URL:\n%2$s</string>
<string name="download_error_device_not_found">Storage Device not found</string>
<string name="download_error_insufficient_space">Insufficient Space</string>
- <string name="download_error_file_error">File Error</string>
<string name="download_error_http_data_error">HTTP Data Error</string>
<string name="download_error_error_unknown">Unknown Error</string>
<string name="download_error_parser_exception">Parser Exception</string>
@@ -238,7 +224,6 @@
<string name="download_error_unauthorized">Authentication Error</string>
<string name="download_error_file_type_type">File Type Error</string>
<string name="download_error_forbidden">Forbidden</string>
- <string name="cancel_all_downloads_label">Cancel all downloads</string>
<string name="download_canceled_msg">Download canceled</string>
<string name="download_canceled_autodownload_enabled_msg">Download canceled\nDisabled <i>Auto Download</i> for this item</string>
<string name="download_report_title">Downloads completed with error(s)</string>
@@ -257,7 +242,6 @@
<string name="download_log_title_unknown">Unknown Title</string>
<string name="download_type_feed">Feed</string>
<string name="download_type_media">Media file</string>
- <string name="download_type_image">Image</string>
<string name="download_request_error_dialog_message_prefix">An error occurred when trying to download the file:\u0020</string>
<string name="null_value_podcast_error">No podcast was provided that could be shown.</string>
<string name="authentication_notification_title">Authentication required</string>
@@ -285,7 +269,6 @@
<string name="position_default_label" translate="false">00:00:00</string>
<string name="player_buffering_msg">Buffering</string>
<string name="player_go_to_picture_in_picture">Picture-in-picture mode</string>
- <string name="playbackservice_notification_title">Playing podcast</string>
<string name="unknown_media_key">AntennaPod - Unknown media key: %1$d</string>
<!-- Queue operations -->
@@ -293,9 +276,10 @@
<string name="unlock_queue">Unlock Queue</string>
<string name="queue_locked">Queue locked</string>
<string name="queue_unlocked">Queue unlocked</string>
+ <string name="queue_lock_warning">If you lock the queue, you can no longer swipe or reorder episodes.</string>
+ <string name="checkbox_do_not_show_again">Do not show again</string>
<string name="clear_queue_label">Clear Queue</string>
<string name="undo">Undo</string>
- <string name="removed_from_queue">Item removed</string>
<string name="move_to_top_label">Move to top</string>
<string name="move_to_bottom_label">Move to bottom</string>
<string name="sort">Sort</string>
@@ -322,7 +306,6 @@
<!-- Empty list labels -->
<string name="no_items_header_label">No queued episodes</string>
<string name="no_items_label">Add an episode by downloading it, or long press an episode and select \"Add to queue\".</string>
- <string name="no_feeds_label">You haven\'t subscribed to any podcasts yet.</string>
<string name="no_shownotes_label">This episode has no shownotes.</string>
<string name="no_run_downloads_head_label">No downloads running</string>
<string name="no_run_downloads_label">You can download episodes on the podcast details screen.</string>
@@ -344,7 +327,6 @@
<!-- Preferences -->
<string name="storage_pref">Storage</string>
<string name="project_pref">Project</string>
- <string name="other_pref">Other</string>
<string name="about_pref">About</string>
<string name="queue_label">Queue</string>
<string name="integrations_label">Integrations</string>
@@ -388,9 +370,7 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Set Time of Day</string>
<string name="pref_autoUpdateIntervallOrTime_every">every %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">at %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Download media files only over WiFi</string>
<string name="pref_followQueue_title">Continuous Playback</string>
- <string name="pref_downloadMediaOnWifiOnly_title">WiFi media download</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Headphones Disconnect</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Headphones Reconnect</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth Reconnect</string>
@@ -401,11 +381,8 @@
<string name="pref_mobileUpdate_auto_download">Auto download</string>
<string name="pref_mobileUpdate_episode_download">Episode download</string>
<string name="pref_mobileUpdate_streaming">Streaming</string>
- <string name="refreshing_label">Refreshing</string>
<string name="user_interface_label">User Interface</string>
<string name="pref_set_theme_title">Select Theme</string>
- <string name="pref_nav_drawer_title">Customize Navigation Drawer</string>
- <string name="pref_nav_drawer_sum">Customize the appearance of the navigation drawer.</string>
<string name="pref_nav_drawer_items_title">Set Navigation Drawer items</string>
<string name="pref_nav_drawer_items_sum">Change which items appear in the navigation drawer.</string>
<string name="pref_nav_drawer_feed_order_title">Set Subscription Order</string>
@@ -417,13 +394,14 @@
<string name="pref_automatic_download_sum">Configure the automatic download of episodes.</string>
<string name="pref_autodl_wifi_filter_title">Enable Wi-Fi filter</string>
<string name="pref_autodl_wifi_filter_sum">Allow automatic download only for selected Wi-Fi networks.</string>
- <string name="pref_autodl_allow_on_mobile_title">Download on mobile connection</string>
- <string name="pref_autodl_allow_on_mobile_sum">Allow automatic download over the mobile data connection.</string>
+ <string name="autodl_wifi_filter_permission_title">Permission required</string>
+ <string name="autodl_wifi_filter_permission_message">Location permission is required for Wi-Fi filter. Tap to grant the permission.</string>
<string name="pref_automatic_download_on_battery_title">Download when not charging</string>
<string name="pref_automatic_download_on_battery_sum">Allow automatic download when the battery is not charging</string>
<string name="pref_parallel_downloads_title">Parallel Downloads</string>
<string name="pref_episode_cache_title">Episode Cache</string>
<string name="pref_episode_cache_summary">Total number of downloaded episodes cached on the device. Automatic download will be suspended if this number is reached.</string>
+ <string name="pref_theme_title_use_system">Use system theme</string>
<string name="pref_theme_title_light">Light</string>
<string name="pref_theme_title_dark">Dark</string>
<string name="pref_theme_title_trueblack">Black (AMOLED ready)</string>
@@ -443,7 +421,6 @@
<string name="pref_gpodnet_full_sync_sum">Sync all subscriptions and episode states with gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Last sync attempt: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Sync started</string>
- <string name="pref_gpodnet_full_sync_started">Full sync started</string>
<string name="pref_gpodnet_login_status"><![CDATA[Logged in as <i>%1$s</i> with device <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Show sync error notifications</string>
<string name="pref_gpodnet_notifications_sum">This setting does not apply to authentication errors.</string>
@@ -475,16 +452,17 @@
<string name="pref_smart_mark_as_played_disabled">Disabled</string>
<string name="pref_image_cache_size_title">Image Cache Size</string>
<string name="pref_image_cache_size_sum">Size of the disk cache for images.</string>
- <string name="crash_report_title">Crash Report</string>
- <string name="crash_report_sum">Send the latest crash report via e-mail</string>
- <string name="send_email">Send e-mail</string>
+ <string name="view_mailing_list">View mailing list</string>
+ <string name="bug_report_title">Report bug</string>
+ <string name="open_bug_tracker">Open bug tracker</string>
+ <string name="copy_to_clipboard">Copy to clipboard</string>
+ <string name="copied_to_clipboard">Copied to clipboard</string>
<string name="experimental_pref">Experimental</string>
<string name="pref_media_player_message">Select which media player to use to play files</string>
<string name="pref_current_value">Current value: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Set a network proxy</string>
- <string name="pref_faq">FAQ</string>
- <string name="pref_known_issues">Known issues</string>
+ <string name="pref_faq">Frequently Asked Questions</string>
<string name="pref_no_browser_found">No web browser found.</string>
<string name="pref_cast_title">Chromecast support</string>
<string name="pref_cast_message_play_flavor">Enable support for remote media playback on Cast devices (such as Chromecast, Audio Speakers or Android TV)</string>
@@ -525,21 +503,16 @@
<string name="no_results_for_query">No results were found for \"%1$s\"</string>
<!-- OPML import and export -->
- <string name="opml_import_txtv_button_lable">OPML files allow you to move your podcasts from one podcatcher to another.</string>
<string name="opml_import_option">Option %1$d</string>
<string name="opml_import_explanation_1">Choose a specific file path from the local filesystem.</string>
- <string name="opml_import_explanation_2">Use an external applications like Dropbox, Google Drive or your favourite file manager to open an OPML file.</string>
- <string name="opml_import_explanation_3">Many applications like Google Mail, Dropbox, Google Drive and most file managers can <i>open</i> OPML files <i>with</i> AntennaPod.</string> <string name="start_import_label">Start import</string>
+ <string name="opml_import_explanation_3">Many applications like Google Mail, Dropbox, Google Drive and most file managers can <i>open</i> OPML files <i>with</i> AntennaPod.</string>
<string name="opml_import_label">OPML Import</string>
- <string name="opml_directory_error">ERROR!</string>
<string name="reading_opml_label">Reading OPML file</string>
<string name="opml_reader_error">An error has occurred while reading the OPML document:</string>
<string name="opml_import_error_no_file">No file selected!</string>
<string name="select_all_label">Select all</string>
<string name="deselect_all_label">Deselect all</string>
- <string name="select_options_label">Select&#8230;</string>
<string name="choose_file_from_filesystem">From local filesystem</string>
- <string name="choose_file_from_external_application">Use external application</string>
<string name="opml_export_label">OPML export</string>
<string name="html_export_label">HTML export</string>
<string name="exporting_label">Exporting&#8230;</string>
@@ -638,7 +611,6 @@
<!-- Online feed view -->
<string name="subscribe_label">Subscribe</string>
- <string name="subscribed_label">Subscribed</string>
<string name="downloading_label">Downloading&#8230;</string>
<!-- Content descriptions for image buttons -->
@@ -761,7 +733,6 @@
<string name="cast_failed_setting_volume">Failed to set the volume</string>
<string name="cast_failed_no_connection">No connection to the cast device is present</string>
<string name="cast_failed_no_connection_trans">Connection to the cast device has been lost. Application is trying to re-establish the connection, if possible. Please wait for a few seconds and try again.</string>
- <string name="cast_failed_perform_action">Failed to perform the action</string>
<string name="cast_failed_status_request">Failed to sync up with the cast device</string>
<string name="cast_failed_seek">Failed to seek to the new position on the cast device</string>
<string name="cast_failed_receiver_player_error">Receiver player has encountered a severe error</string>
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
index fb69e54e7..d2ba4bb50 100644
--- a/core/src/main/res/values/styles.xml
+++ b/core/src/main/res/values/styles.xml
@@ -23,6 +23,7 @@
<item name="av_fast_forward">@drawable/ic_fast_forward_grey600_24dp</item>
<item name="av_pause">@drawable/ic_pause_grey600_24dp</item>
<item name="av_play">@drawable/ic_play_arrow_grey600_24dp</item>
+ <item name="av_speed">@drawable/ic_playback_speed_dark</item>
<item name="av_rewind">@drawable/ic_fast_rewind_grey600_24dp</item>
<item name="content_discard">@drawable/ic_delete_grey600_24dp</item>
<item name="content_new">@drawable/ic_add_grey600_24dp</item>
@@ -68,9 +69,9 @@
<item name="ic_create_new_folder">@drawable/ic_create_new_folder_grey600_24dp</item>
<item name="ic_cast_disconnect">@drawable/ic_cast_disconnect_grey600_36dp</item>
<item name="ic_cellphone_text">@drawable/ic_cellphone_text_grey600_24dp</item>
- <item name="ic_question_answer">@drawable/ic_forum_grey600_24dp</item>
+ <item name="ic_questionmark">@drawable/ic_questionmark_grey600</item>
+ <item name="ic_chat">@drawable/ic_chat_grey600</item>
<item name="ic_bug">@drawable/ic_bug_grey600_24dp</item>
- <item name="ic_known_issues">@drawable/ic_format_list_bulleted_grey600_24dp</item>
<item name="ic_bookmark">@drawable/ic_bookmark_grey600_24dp</item>
<item name="batch_edit_fab_icon">@drawable/ic_fab_edit_white</item>
<item name="master_switch_background">@color/master_switch_background_light</item>
@@ -108,6 +109,7 @@
<item name="av_fast_forward">@drawable/ic_fast_forward_white_24dp</item>
<item name="av_pause">@drawable/ic_pause_white_24dp</item>
<item name="av_play">@drawable/ic_play_arrow_white_24dp</item>
+ <item name="av_speed">@drawable/ic_playback_speed_white</item>
<item name="av_rewind">@drawable/ic_fast_rewind_white_24dp</item>
<item name="content_discard">@drawable/ic_delete_white_24dp</item>
<item name="content_new">@drawable/ic_add_white_24dp</item>
@@ -153,9 +155,9 @@
<item name="ic_create_new_folder">@drawable/ic_create_new_folder_white_24dp</item>
<item name="ic_cast_disconnect">@drawable/ic_cast_disconnect_white_36dp</item>
<item name="ic_cellphone_text">@drawable/ic_cellphone_text_white_24dp</item>
- <item name="ic_question_answer">@drawable/ic_baseline_question_answer_white_24dp</item>
+ <item name="ic_questionmark">@drawable/ic_questionmark_white</item>
+ <item name="ic_chat">@drawable/ic_chat_white</item>
<item name="ic_bug">@drawable/ic_bug_white_24dp</item>
- <item name="ic_known_issues">@drawable/ic_format_list_bulleted_white_24dp</item>
<item name="ic_bookmark">@drawable/ic_bookmark_white_24dp</item>
<item name="batch_edit_fab_icon">@drawable/ic_fab_edit_white</item>
<item name="master_switch_background">@color/master_switch_background_dark</item>
@@ -258,13 +260,6 @@
<item name="android:ellipsize">end</item>
</style>
- <style name="AntennaPod.Dialog.Title" parent="@android:style/TextAppearance.Medium">
- <item name="android:textSize">@dimen/text_size_medium</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:maxLines">2</item>
- <item name="android:ellipsize">end</item>
- </style>
-
<style name="AntennaPod.TextView.UnreadIndicator" parent="@android:style/TextAppearance.Small">
<item name="android:textSize">@dimen/text_size_micro</item>
<item name="android:textColor">@color/new_indicator_green</item>
@@ -276,16 +271,6 @@
<item name="textAllCaps">false</item>
</style>
- <style name="Divider">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">1dp</item>
- <item name="android:layout_marginTop">8dp</item>
- <item name="android:layout_marginLeft">16dp</item>
- <item name="android:layout_marginRight">16dp</item>
- <item name="android:layout_marginBottom">8dp</item>
- <item name="android:background">?android:attr/listDivider</item>
- </style>
-
<style name="AntennaPod.Dialog.Light" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/holo_blue_light</item>
</style>
diff --git a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
index 244fb0254..800222ada 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.core;
import android.content.Context;
+import android.util.Log;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
@@ -15,6 +16,8 @@ import de.danoeh.antennapod.core.util.exception.RxJavaErrorHandlerSetup;
* Apps using the core module of AntennaPod should register implementations of all interfaces here.
*/
public class ClientConfig {
+ private static final String TAG = "ClientConfig";
+
private ClientConfig(){}
/**
@@ -44,7 +47,15 @@ public class ClientConfig {
UserPreferences.init(context);
PlaybackPreferences.init(context);
NetworkUtils.init(context);
- CastManager.init(context);
+ // Don't initialize Cast-related logic unless it is enabled, to avoid the unnecessary
+ // Google Play Service usage.
+ // Down side: when the user decides to enable casting, AntennaPod needs to be restarted
+ // for it to take effect.
+ if (UserPreferences.isCastEnabled()) {
+ CastManager.init(context);
+ } else {
+ Log.v(TAG, "Cast is disabled. All Cast-related initialization will be skipped.");
+ }
SleepTimerPreferences.init(context);
RxJavaErrorHandlerSetup.setupRxJavaErrorHandler();
initialized = true;
diff --git a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
index 5198a76bd..414a7840c 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
@@ -163,6 +163,10 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
return INSTANCE;
}
+ public static boolean isInitialized() {
+ return INSTANCE != null;
+ }
+
/**
* Returns the active {@link RemoteMediaPlayer} instance. Since there are a number of media
* control APIs that this library do not provide a wrapper for, client applications can call
diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
index 7ab1be380..79c71f164 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
@@ -56,11 +56,18 @@ public class PlaybackServiceFlavorHelper {
PlaybackServiceFlavorHelper(Context context, PlaybackService.FlavorHelperCallback callback) {
this.callback = callback;
+ if (!CastManager.isInitialized()) {
+ return;
+ }
mediaRouter = MediaRouter.getInstance(context.getApplicationContext());
setCastConsumer(context);
}
void initializeMediaPlayer(Context context) {
+ if (!CastManager.isInitialized()) {
+ callback.setMediaPlayer(new LocalPSMP(context, callback.getMediaPlayerCallback()));
+ return;
+ }
castManager = CastManager.getInstance();
castManager.addCastConsumer(castConsumer);
boolean isCasting = castManager.isConnected();
@@ -77,10 +84,16 @@ public class PlaybackServiceFlavorHelper {
}
void removeCastConsumer() {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castManager.removeCastConsumer(castConsumer);
}
boolean castDisconnect(boolean castDisconnect) {
+ if (!CastManager.isInitialized()) {
+ return false;
+ }
if (castDisconnect) {
castManager.disconnect();
}
@@ -88,6 +101,9 @@ public class PlaybackServiceFlavorHelper {
}
boolean onMediaPlayerInfo(Context context, int code, @StringRes int resourceId) {
+ if (!CastManager.isInitialized()) {
+ return false;
+ }
switch (code) {
case RemotePSMP.CAST_ERROR:
callback.sendNotificationBroadcast(PlaybackService.NOTIFICATION_TYPE_SHOW_TOAST, resourceId);
@@ -218,6 +234,9 @@ public class PlaybackServiceFlavorHelper {
}
void registerWifiBroadcastReceiver() {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
if (wifiBroadcastReceiver != null) {
return;
}
@@ -243,6 +262,9 @@ public class PlaybackServiceFlavorHelper {
}
void unregisterWifiBroadcastReceiver() {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
if (wifiBroadcastReceiver != null) {
callback.unregisterReceiver(wifiBroadcastReceiver);
wifiBroadcastReceiver = null;
@@ -250,6 +272,9 @@ public class PlaybackServiceFlavorHelper {
}
boolean onSharedPreference(String key) {
+ if (!CastManager.isInitialized()) {
+ return false;
+ }
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
if (!UserPreferences.isCastEnabled()) {
if (castManager.isConnecting() || castManager.isConnected()) {
@@ -263,6 +288,9 @@ public class PlaybackServiceFlavorHelper {
}
void sessionStateAddActionForWear(PlaybackStateCompat.Builder sessionState, String actionName, CharSequence name, int icon) {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
PlaybackStateCompat.CustomAction.Builder actionBuilder =
new PlaybackStateCompat.CustomAction.Builder(actionName, name, icon);
Bundle actionExtras = new Bundle();
@@ -273,6 +301,9 @@ public class PlaybackServiceFlavorHelper {
}
void mediaSessionSetExtraForWear(MediaSessionCompat mediaSession) {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
Bundle sessionExtras = new Bundle();
sessionExtras.putBoolean(MediaControlConstants.EXTRA_RESERVE_SLOT_SKIP_TO_PREVIOUS, true);
sessionExtras.putBoolean(MediaControlConstants.EXTRA_RESERVE_SLOT_SKIP_TO_NEXT, true);