summaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/assets/developers.csv10
-rw-r--r--app/src/main/assets/translators.csv29
-rw-r--r--app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java25
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java665
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java89
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java52
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java686
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java134
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java123
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedSearchResultAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java332
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java68
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DeleteActionButton.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PauseActionButton.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayLocalActionButton.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/FeedRefreshIntervalDialog.java106
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java103
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ShareDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/StreamingConfirmationDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/TagSettingsDialog.java120
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/error/CrashReportWriter.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java197
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java61
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java240
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java164
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java25
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java42
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java59
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java29
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java19
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java253
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PagedToolbarFragment.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java30
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java154
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java93
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SuggestionListFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java29
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java108
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/AboutFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/DevelopersFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/LicensesFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/SpecialThanksFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/TranslatorsFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/ChapterSeekBar.java148
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java165
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/PlayButton.java52
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/viewholder/DownloadLogItemViewHolder.java (renamed from app/src/main/java/de/danoeh/antennapod/view/viewholder/DownloadItemViewHolder.java)13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java13
-rw-r--r--app/src/main/res/drawable-v21/grey_border.xml29
-rw-r--r--app/src/main/res/drawable/grey_border.xml21
-rw-r--r--app/src/main/res/drawable/ic_animate_pause_play.xml27
-rw-r--r--app/src/main/res/drawable/ic_animate_play.xml14
-rw-r--r--app/src/main/res/drawable/ic_animate_play_pause.xml27
-rw-r--r--app/src/main/res/layout/activity_widget_config.xml3
-rw-r--r--app/src/main/res/layout/addfeed.xml30
-rw-r--r--app/src/main/res/layout/alternate_urls_dropdown_item.xml8
-rw-r--r--app/src/main/res/layout/alternate_urls_item.xml9
-rw-r--r--app/src/main/res/layout/audioplayer_fragment.xml82
-rw-r--r--app/src/main/res/layout/cover_fragment.xml134
-rw-r--r--app/src/main/res/layout/downloadlist_item.xml39
-rw-r--r--app/src/main/res/layout/downloadlog_item.xml60
-rw-r--r--app/src/main/res/layout/edit_tags_dialog.xml41
-rw-r--r--app/src/main/res/layout/empty_view_layout.xml2
-rw-r--r--app/src/main/res/layout/episodes_apply_action_fragment.xml2
-rw-r--r--app/src/main/res/layout/external_player_fragment.xml6
-rw-r--r--app/src/main/res/layout/feed_refresh_dialog.xml39
-rw-r--r--app/src/main/res/layout/feedinfo.xml22
-rw-r--r--app/src/main/res/layout/feeditem_fragment.xml8
-rw-r--r--app/src/main/res/layout/feeditemlist_header.xml11
-rw-r--r--app/src/main/res/layout/feeditemlist_item.xml11
-rw-r--r--app/src/main/res/layout/feedsettings.xml2
-rw-r--r--app/src/main/res/layout/fragment_subscriptions.xml3
-rw-r--r--app/src/main/res/layout/gpodnet_podcast_listitem.xml2
-rw-r--r--app/src/main/res/layout/gpodnetauth_credentials.xml3
-rw-r--r--app/src/main/res/layout/gpodnetauth_finish.xml3
-rw-r--r--app/src/main/res/layout/item_description_fragment.xml21
-rw-r--r--app/src/main/res/layout/nav_list.xml6
-rw-r--r--app/src/main/res/layout/nav_listitem.xml9
-rw-r--r--app/src/main/res/layout/onlinefeedview_activity.xml1
-rw-r--r--app/src/main/res/layout/queue_fragment.xml19
-rw-r--r--app/src/main/res/layout/settings_activity.xml5
-rw-r--r--app/src/main/res/layout/storage_error.xml2
-rw-r--r--app/src/main/res/layout/videoplayer_activity.xml43
-rw-r--r--app/src/main/res/menu/downloads.xml6
-rw-r--r--app/src/main/res/menu/episodes.xml10
-rw-r--r--app/src/main/res/menu/episodes_apply_action_options.xml4
-rw-r--r--app/src/main/res/menu/episodes_apply_action_speeddial.xml12
-rw-r--r--app/src/main/res/menu/feedinfo.xml4
-rw-r--r--app/src/main/res/menu/feeditem_options.xml2
-rw-r--r--app/src/main/res/menu/feedlist.xml14
-rw-r--r--app/src/main/res/menu/mediaplayer.xml14
-rw-r--r--app/src/main/res/menu/online_search.xml3
-rw-r--r--app/src/main/res/menu/opml_selection_options.xml4
-rw-r--r--app/src/main/res/menu/playback_history.xml2
-rw-r--r--app/src/main/res/menu/queue.xml6
-rw-r--r--app/src/main/res/menu/search.xml2
-rw-r--r--app/src/main/res/menu/statistics.xml2
-rw-r--r--app/src/main/res/menu/subscriptions.xml2
-rw-r--r--app/src/main/res/values/attrs.xml12
-rw-r--r--app/src/main/res/values/svg.xml5
-rw-r--r--app/src/main/res/xml/feed_settings.xml20
-rw-r--r--app/src/main/res/xml/preferences.xml26
-rw-r--r--app/src/main/res/xml/preferences_about.xml8
-rw-r--r--app/src/main/res/xml/preferences_network.xml4
159 files changed, 3202 insertions, 2600 deletions
diff --git a/app/src/main/assets/developers.csv b/app/src/main/assets/developers.csv
index 77269ba97..c989bb22e 100644
--- a/app/src/main/assets/developers.csv
+++ b/app/src/main/assets/developers.csv
@@ -4,8 +4,8 @@ mfietz;6860662;Maintainer (retired)
TomHennen;5216560;Maintainer (retired)
orionlee;250644;Contributor
domingos86;9538859;Contributor
-damoasda;46045854;Contributor
tonytamsf;149837;Contributor
+damoasda;46045854;Contributor
andersonvom;69922;Contributor
TacoTheDank;32376686;Contributor
shortspider;5712543;Contributor
@@ -47,6 +47,7 @@ saqura;1935380;Contributor
binarytoto;75904760;Contributor
bibz;5141956;Contributor
hzulla;1705654;Contributor
+jonasburian;15125616;Contributor
deandreamatias;21011641;Contributor
MeirAtIMDDE;4421079;Contributor
egsavage;126165;Contributor
@@ -59,6 +60,7 @@ rezanejati;16049370;Contributor
twiceyuan;2619800;Contributor
JessieVela;33134794;Contributor
HaBaLeS;730902;Contributor
+peakvalleytech;65185819;Contributor
volhol;11587858;Contributor
michaelmwhite;28901334;Contributor
CameronBanga;611354;Contributor
@@ -66,7 +68,6 @@ HrBDev;25826502;Contributor
HolgerJeromin;2410353;Contributor
xisberto;1914956;Contributor
jmue;898577;Contributor
-jonasburian;15125616;Contributor
katrinleinweber;9948149;Contributor
LatinSuD;451487;Contributor
24hours;650407;Contributor
@@ -92,14 +93,17 @@ mamehacker;16738348;Contributor
skitt;2128935;Contributor
Thom-Merrilin;76849828;Contributor
wseemann;2296196;Contributor
+datavizard;44409076;Contributor
markamaze;17114678;Contributor
mohitshah3111999;42018918;Contributor
moralesg;14352147;Contributor
mr-intj;6268767;Contributor
+tamizh143;50977879;Contributor
tuxayo;2678215;Contributor
alimemonzx;44647595;Contributor
dev-darrell;52300159;Contributor
jmdouglas;10855634;Contributor
+jhenninger;197274;Contributor
olivoto;15932680;Contributor
PtilopsisLeucotis;54054883;Contributor
abhinavg1997;60095795;Contributor
@@ -123,6 +127,7 @@ Geist5000;37940313;Contributor
jklippel;8657220;Contributor
jannic;232606;Contributor
Foso;5015532;Contributor
+CreamyCookie;3063858;Contributor
Kaligule;3586246;Contributor
kvithayathil;1056073;Contributor
luiscruz;1080714;Contributor
@@ -169,4 +174,5 @@ lightonflux;1377943;Contributor
minusf;3632883;Contributor
s3lph;5564491;Contributor
tamizh138;26201258;Contributor
+trevortabaka;1552990;Contributor
zawad2221;32180355;Contributor
diff --git a/app/src/main/assets/translators.csv b/app/src/main/assets/translators.csv
index 4c9dc0c8e..a182435a5 100644
--- a/app/src/main/assets/translators.csv
+++ b/app/src/main/assets/translators.csv
@@ -1,45 +1,46 @@
Arabic;abuzar3.khalid, badarotti, keunes, MustafaAlgurabi, nabilMaghura, rex07, shubbar
-Asturian (ast_ES);enolp
+Asturian (ast_ES);enolp, keunes
Basque;gaztainalde, keunes, Osoitz, pospolos
-Breton;Belvar, keunes
+Breton;Belvar, Eorn, keunes
Bulgarian;keunes, ma4ko, solusitor
Catalan;carles.llacer, dvd1985, exort12, IvanAmarante, javiercoll, keunes, Kintu, lambdani, marcmetallextrem, xc70
-Chinese (zh_CN);brnme, cyril3, Felix2yu, gaohongyuan, Guaidaodl, Huck0, iconteral, jhxie, jxj2zzz79pfp9bpo, keunes, kyleehee, molisiye, owen8877, RainSlide, RangerNJU, Sak94664, spice2wolf, tupunco, wongsyrone, yangyang, yiqiok
+Chinese (zh_CN);Biacke, brnme, cyril3, Felix2yu, gaohongyuan, Guaidaodl, Huck0, iconteral, jhxie, jxj2zzz79pfp9bpo, keunes, kyleehee, molisiye, owen8877, RainSlide, RangerNJU, Sak94664, spice2wolf, tupunco, wongsyrone, yangyang, yiqiok
Chinese (zh_TW);bobchao, ijliao, keunes, mapobi, pggdt, ymhuang0808
Czech (cs_CZ);anotheranonymoususer, elich, Hanzmeister, md.share, svetlemodry, Thomaash
-Danish;JFreak, jhertel, keunes, petterbejo, SebastianKiwiDk
+Danish;JFreak, jhertel, keunes, mikini, petterbejo, SebastianKiwiDk
Dutch;e2jk, keunes, rwv, Vistaus
Estonian;Eraser, keunes, mahfiaz
-Finnish;Ban3, keunes, Sahtor
-French;ChaoticMind, clombion, Cornegidouille, e2jk, keunes, lacouture, LouFex, Matth78, petterbejo, Poussinou, RomainTT, sterylmreep
+Finnish;Ban3, keunes, ktstmu, noppa, Sahtor
+French;ayiniho, ChaoticMind, clombion, Cornegidouille, e2jk, keunes, lacouture, LouFex, Matth78, petterbejo, Poussinou, RomainTT, sterylmreep
Galician;antiparvos, pikamoku, Raichely
-German;_Er, ByteHamster, ceving, dadosch, DerSilly, elkangaroo, enz, f_grubm, finsterwalder, forght, hbilke, HolgerJeromin, JoeMcFly, kalei, keunes, max.wittig, mfietz, Michael_Strecke, petterbejo, pudeeh, Quiss42, repat, toaskoas, tomte, tweimer, Willhelm, ypid
-Modern Greek (1453-);AnimaRain, antonist, keunes, pavlosv
+German;_Er, axre, ByteHamster, ceving, dadosch, DerSilly, elkangaroo, enz, f_grubm, finsterwalder, forght, hbilke, HolgerJeromin, JoeMcFly, JoniArida, kalei, keunes, max.wittig, mfietz, Michael_Strecke, petterbejo, pudeeh, Quiss42, repat, timo.rohwedder, toaskoas, tomte, tweimer, Willhelm, ypid
Hebrew (he_IL);amir.dafnyman, E1i9, mongoose4004, pinkasey, rellieberman, Yaron
Hindi (hi_IN);keunes, purple.coder, siddhusengar, thelazyoxymoron
Hu;hurrikan, keunes, lna91, marthynw, meskobalazs, naren93
Icelandic;keunes, marthjod
Indonesian;dbrw, keunes, levirs565
Italian (it_IT);aalex70, allin, alvami, Bonnee, dontknowcris, giuseppep, Guybrush88, ilmanzo, keunes, m.chinni, marco_pag, neonsoftware, niccord, salorock, theloca95
-Japanese;keunes, KotaKato, Naofumi, sh3llc4t, TranslatorG
+Japanese;ayiniho, keunes, KotaKato, Naofumi, sh3llc4t, TranslatorG
Kannada (kn_IN);chiraag.nataraj, keunes, thejeshgn
Ko;changwoo, keunes, libliboom
Lithuanian;keunes, naglis, Sharper
Macedonian;krisfremen
Malayalam;joice, keunes, rashivkp
-Norwegian Bokmål (nb_NO);abstrakct, ahysing, bablecopherye, corkie, forteller, heraldo, jakobkg, keunes, kongk, sevenmaster, timbast
+Modern Greek (1453-);AnimaRain, antonist, keunes, pavlosv
+Norwegian Bokmål (nb_NO);abstrakct, ahysing, bablecopherye, corkie, forteller, heraldo, jakobkg, keunes, kongk, sevenmaster, tc5, timbast
Persian;ahangarha, danialbehzadi, ebadi, ebraminio, F7D, hamidrezabayat76, keunes, sinamoghaddas
Polish (pl_PL);befeleme, hiro2020, Iwangelion, kamila.miodek1991, keunes, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
-Portuguese;emansije, keunes, smarquespt
+Portuguese;emansije, keunes, smarquespt, WalkerPt
Portuguese (pt_BR);alexupits, alysonborges, andersonvom, aracnus, arua, bandreghetti, caioau, carlo_valente, castrors, edman, keunes, lipefire, mbaltar, olivoto, rogervezaro, RubeensVinicius, SamWilliam
Romanian (ro_RO);corneliu.e, fuzzmz, keunes, ralienpp
Russian (ru_RU);ashed, btimofeev, Duke_Raven, gammja, homocomputeris, IgorPolyakov, keunes, mercutiy, null, overmind88, Platun0v, PtilopsisLeucotis, s.chebotar, tepxd, un_logic, Vladryyu, whereisthetea
Slovak;ati3, jose1711, keunes, marulinko, tiborepcek
Slovenian (sl_SI);asovic, keunes, panter23, trus2
-Spanish;AleksSyntek, andersonvom, andrespelaezp, Atreyu94, CaeM0R, deandreamatias, dvd1985, elojodepajaro, Fitoschido, frandavid100, hard_ware, javiercoll, keunes, LatinSuD, leogrignafini, rafael.osuna, tres.14159, vfmatzkin, wakutiteo
+Spanish;AleksSyntek, andersonvom, andrespelaezp, Atreyu94, CaeM0R, carlos.levy, deandreamatias, dvd1985, elojodepajaro, Fitoschido, frandavid100, hard_ware, javiercoll, keunes, LatinSuD, leogrignafini, meanderingDot, rafael.osuna, tres.14159, vfmatzkin, wakutiteo
Swahili (macrolanguage);1silvester, keunes, kmtra
Swedish (sv_SE);bpnilsson, keunes, nilso, TwoD
+Tatar;seber
Telugu;keunes, veeven
-Turkish;AhmedDuran, brsata, Erdy, keunes, overbite, Slsdem
-Ukrainian (uk_UA);keunes, older, paul_sm, sergiyr, zhenya97
+Turkish;AhmedDuran, androtuna, brsata, Erdy, keunes, overbite, Slsdem
+Ukrainian (uk_UA);keunes, older, paul_sm, sergiyr, voinovich_vyacheslav, zhenya97
Vietnamese;abnvolk, keunes, ppanhh
diff --git a/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java b/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java
index 9ed4897d2..d9b912634 100644
--- a/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java
+++ b/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java
@@ -53,6 +53,9 @@ public class ViewPagerBottomSheetBehavior<V extends View> extends BottomSheetBeh
}
public void updateScrollingChild() {
+ if (viewRef == null) {
+ return;
+ }
final View scrollingChild = findScrollingChild(viewRef.get());
nestedScrollingChildRef = new WeakReference<>(scrollingChild);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
index 721291597..50794ba5b 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
@@ -99,7 +99,10 @@ public class BugReportActivity extends AppCompatActivity {
private void exportLog() {
try {
File filename = new File(UserPreferences.getDataFolder(null), "full-logs.txt");
- filename.createNewFile();
+ boolean success = filename.createNewFile();
+ if (!success) {
+ throw new IOException("Unable to create output file");
+ }
String cmd = "logcat -d -f " + filename.getAbsolutePath();
Runtime.getRuntime().exec(cmd);
//share file
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
index 0f1d38db6..92a0909d6 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
@@ -4,8 +4,8 @@ import android.os.Bundle;
import android.text.TextUtils;
import androidx.appcompat.app.AppCompatActivity;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.storage.DBReader;
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 e18002903..f07ad6ad5 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -20,6 +20,7 @@ import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.Toast;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBarDrawerToggle;
@@ -32,16 +33,23 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.recyclerview.widget.RecyclerView;
+
import com.bumptech.glide.Glide;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
+
+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 de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.StorageUtils;
-import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.fragment.AddFeedFragment;
@@ -57,12 +65,8 @@ import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.fragment.TransitionEffect;
import de.danoeh.antennapod.preferences.PreferenceUpgrader;
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.view.LockableBottomSheetBehavior;
-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;
/**
* The activity that is shown when the user launches the app.
@@ -184,6 +188,11 @@ public class MainActivity extends CastEnabledActivity {
if (audioPlayer == null) {
return;
}
+
+ if (slideOffset == 0.0f) { //STATE_COLLAPSED
+ audioPlayer.scrollToPage(AudioPlayerFragment.POS_COVER);
+ }
+
float condensedSlideOffset = Math.max(0.0f, Math.min(0.2f, slideOffset - 0.2f)) / 0.2f;
audioPlayer.getExternalPlayerHolder().setAlpha(1 - condensedSlideOffset);
audioPlayer.getExternalPlayerHolder().setVisibility(
@@ -193,7 +202,9 @@ public class MainActivity extends CastEnabledActivity {
public void setupToolbarToggle(@NonNull Toolbar toolbar, boolean displayUpArrow) {
if (drawerLayout != null) { // Tablet layout does not have a drawer
- drawerLayout.removeDrawerListener(drawerToggle);
+ if (drawerToggle != null) {
+ drawerLayout.removeDrawerListener(drawerToggle);
+ }
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar,
R.string.drawer_open, R.string.drawer_close);
drawerLayout.addDrawerListener(drawerToggle);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
deleted file mode 100644
index 56a66ba93..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ /dev/null
@@ -1,665 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.annotation.TargetApi;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.graphics.PixelFormat;
-import android.os.Build;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.TextView;
-
-import com.bumptech.glide.Glide;
-
-import org.apache.commons.lang3.StringUtils;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.text.NumberFormat;
-
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.cardview.widget.CardView;
-import androidx.core.app.ActivityOptionsCompat;
-import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-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.playback.PlaybackService;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.util.Converter;
-import de.danoeh.antennapod.core.util.FeedItemUtil;
-import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.ShareUtils;
-import de.danoeh.antennapod.core.util.StorageUtils;
-import de.danoeh.antennapod.core.util.TimeSpeedConverter;
-import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
-import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
-import de.danoeh.antennapod.core.util.playback.Playable;
-import de.danoeh.antennapod.core.util.playback.PlaybackController;
-import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
-import de.danoeh.antennapod.dialog.ShareDialog;
-import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
-import de.danoeh.antennapod.dialog.SleepTimerDialog;
-import io.reactivex.Observable;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.Disposable;
-import io.reactivex.schedulers.Schedulers;
-
-/**
- * Provides general features which are both needed for playing audio and video
- * files.
- */
-public abstract class MediaplayerActivity extends CastEnabledActivity implements OnSeekBarChangeListener {
- private static final String TAG = "MediaplayerActivity";
- private static final String PREFS = "MediaPlayerActivityPreferences";
-
- PlaybackController controller;
-
- private TextView txtvPosition;
- private TextView txtvLength;
- SeekBar sbPosition;
- private ImageButton butRev;
- private TextView txtvRev;
- private ImageButton butPlay;
- private ImageButton butFF;
- private TextView txtvFF;
- private ImageButton butSkip;
- private CardView cardViewSeek;
- private TextView txtvSeek;
-
- private boolean showTimeLeft = false;
-
- private boolean isFavorite = false;
-
- private Disposable disposable;
-
- private PlaybackController newPlaybackController() {
- return new PlaybackController(this) {
- @Override
- public void onPositionObserverUpdate() {
- MediaplayerActivity.this.onPositionObserverUpdate();
- }
-
- @Override
- public void onBufferStart() {
- MediaplayerActivity.this.onBufferStart();
- }
-
- @Override
- public void onBufferEnd() {
- MediaplayerActivity.this.onBufferEnd();
- }
-
- @Override
- public void onBufferUpdate(float progress) {
- MediaplayerActivity.this.onBufferUpdate(progress);
- }
-
- @Override
- public void handleError(int code) {
- MediaplayerActivity.this.handleError(code);
- }
-
- @Override
- public void onReloadNotification(int code) {
- MediaplayerActivity.this.onReloadNotification(code);
- }
-
- @Override
- public void onSleepTimerUpdate() {
- supportInvalidateOptionsMenu();
- }
-
- @Override
- public ImageButton getPlayButton() {
- return butPlay;
- }
-
- @Override
- public void loadMediaInfo() {
- MediaplayerActivity.this.loadMediaInfo();
- }
-
- @Override
- public void onAwaitingVideoSurface() {
- MediaplayerActivity.this.onAwaitingVideoSurface();
- }
-
- @Override
- public void onShutdownNotification() {
- finish();
- }
-
- @Override
- public void onPlaybackEnd() {
- finish();
- }
-
- @Override
- public void onPlaybackSpeedChange() {
- MediaplayerActivity.this.onPlaybackSpeedChange();
- }
-
- @Override
- protected void setScreenOn(boolean enable) {
- super.setScreenOn(enable);
- MediaplayerActivity.this.setScreenOn(enable);
- }
-
- @Override
- public void onSetSpeedAbilityChanged() {
- MediaplayerActivity.this.onSetSpeedAbilityChanged();
- }
- };
- }
-
- @Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(PlaybackPositionEvent event) {
- onPositionObserverUpdate();
- }
-
- private void onSetSpeedAbilityChanged() {
- Log.d(TAG, "onSetSpeedAbilityChanged()");
- updatePlaybackSpeedButton();
- }
-
- private void onPlaybackSpeedChange() {
- updatePlaybackSpeedButtonText();
- }
-
- void chooseTheme() {
- setTheme(UserPreferences.getTheme());
- }
-
- void setScreenOn(boolean enable) {
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- chooseTheme();
- super.onCreate(savedInstanceState);
-
- Log.d(TAG, "onCreate()");
- StorageUtils.checkStorageAvailability(this);
-
- getWindow().setFormat(PixelFormat.TRANSPARENT);
- setupGUI();
- }
-
- @Override
- protected void onPause() {
- if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
- if (controller != null) {
- controller.reinitServiceIfPaused();
- controller.pause();
- }
- }
- super.onPause();
- }
-
- /**
- * Should be used to switch to another player activity if the mime type is
- * not the correct one for the current activity.
- */
- protected abstract void onReloadNotification(int notificationCode);
-
- /**
- * Should be used to inform the user that the PlaybackService is currently
- * buffering.
- */
- protected void onBufferStart() {
-
- }
-
- /**
- * Should be used to hide the view that was showing the 'buffering'-message.
- */
- protected void onBufferEnd() {
-
- }
-
- private void onBufferUpdate(float progress) {
- if (sbPosition != null) {
- sbPosition.setSecondaryProgress((int) (progress * sbPosition.getMax()));
- }
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- controller = newPlaybackController();
- controller.init();
- loadMediaInfo();
- onPositionObserverUpdate();
- EventBus.getDefault().register(this);
- }
-
- @Override
- protected void onStop() {
- Log.d(TAG, "onStop()");
- if (controller != null) {
- controller.release();
- controller = null; // prevent leak
- }
- if (disposable != null) {
- disposable.dispose();
- }
- EventBus.getDefault().unregister(this);
- super.onStop();
- }
-
- @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
- @Override
- public void onTrimMemory(int level) {
- super.onTrimMemory(level);
- Glide.get(this).trimMemory(level);
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- Glide.get(this).clearMemory();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- requestCastButton(menu);
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.mediaplayer, menu);
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- super.onPrepareOptionsMenu(menu);
- if (controller == null) {
- return false;
- }
- Playable media = controller.getMedia();
- boolean isFeedMedia = (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);
-
- boolean isItemAndHasLink = isFeedMedia &&
- ShareUtils.hasLinkToShare(((FeedMedia) media).getItem());
-
- boolean isItemHasDownloadLink = isFeedMedia && ((FeedMedia) media).getDownload_url() != null;
-
- menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink);
-
- menu.findItem(R.id.add_to_favorites_item).setVisible(false);
- menu.findItem(R.id.remove_from_favorites_item).setVisible(false);
- if (isFeedMedia) {
- menu.findItem(R.id.add_to_favorites_item).setVisible(!isFavorite);
- menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite);
- }
-
- menu.findItem(R.id.set_sleeptimer_item).setVisible(!controller.sleepTimerActive());
- menu.findItem(R.id.disable_sleeptimer_item).setVisible(controller.sleepTimerActive());
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (controller == null) {
- return false;
- }
- Playable media = controller.getMedia();
- if (item.getItemId() == android.R.id.home) {
- Intent intent = new Intent(MediaplayerActivity.this,
- MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
- | Intent.FLAG_ACTIVITY_NEW_TASK);
-
- View cover = findViewById(R.id.imgvCover);
- if (cover != null) {
- ActivityOptionsCompat options = ActivityOptionsCompat
- .makeSceneTransitionAnimation(MediaplayerActivity.this, cover, "coverTransition");
- startActivity(intent, options.toBundle());
- } else {
- startActivity(intent);
- }
- finish();
- 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 (feedItem != null) {
- DBWriter.addFavoriteItem(feedItem);
- isFavorite = true;
- invalidateOptionsMenu();
- }
- break;
- case R.id.remove_from_favorites_item:
- if (feedItem != null) {
- DBWriter.removeFavoriteItem(feedItem);
- isFavorite = false;
- invalidateOptionsMenu();
- }
- break;
- case R.id.disable_sleeptimer_item: // Fall-through
- case R.id.set_sleeptimer_item:
- new SleepTimerDialog().show(getSupportFragmentManager(), "SleepTimerDialog");
- break;
- case R.id.audio_controls:
- PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance();
- 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:
- IntentUtils.openInBrowser(MediaplayerActivity.this, getWebsiteLinkWithFallback(media));
- break;
- case R.id.share_item:
- if (feedItem != null) {
- ShareDialog shareDialog = ShareDialog.newInstance(feedItem);
- shareDialog.show(getSupportFragmentManager(), "ShareEpisodeDialog");
- }
- break;
- default:
- return false;
- }
- return true;
- } else {
- return false;
- }
- }
- }
-
- private static String getWebsiteLinkWithFallback(Playable media) {
- if (media == null) {
- return null;
- } else if (StringUtils.isNotBlank(media.getWebsiteLink())) {
- return media.getWebsiteLink();
- } else if (media instanceof FeedMedia) {
- return FeedItemUtil.getLinkWithFallback(((FeedMedia)media).getItem());
- }
- return null;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- Log.d(TAG, "onResume()");
- StorageUtils.checkStorageAvailability(this);
- }
-
- /**
- * Called by 'handleStatus()' when the PlaybackService is waiting for
- * a video surface.
- */
- protected abstract void onAwaitingVideoSurface();
-
- void onPositionObserverUpdate() {
- if (controller == null || txtvPosition == null || txtvLength == null) {
- return;
- }
-
- TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
- int currentPosition = converter.convert(controller.getPosition());
- int duration = converter.convert(controller.getDuration());
- int remainingTime = converter.convert(
- controller.getDuration() - controller.getPosition());
- Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition));
- if (currentPosition == PlaybackService.INVALID_TIME ||
- duration == PlaybackService.INVALID_TIME) {
- Log.w(TAG, "Could not react to position observer update because of invalid time");
- return;
- }
- txtvPosition.setText(Converter.getDurationStringLong(currentPosition));
- if (showTimeLeft) {
- txtvLength.setText("-" + Converter.getDurationStringLong(remainingTime));
- } else {
- txtvLength.setText(Converter.getDurationStringLong(duration));
- }
- updateProgressbarPosition(currentPosition, duration);
- }
-
- private void updateProgressbarPosition(int position, int duration) {
- Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration + ")");
- if(sbPosition == null) {
- return;
- }
- float progress = ((float) position) / duration;
- sbPosition.setProgress((int) (progress * sbPosition.getMax()));
- }
-
- /**
- * Load information about the media that is going to be played or currently
- * being played. This method will be called when the activity is connected
- * to the PlaybackService to ensure that the activity has the right
- * FeedMedia object.
- */
- void loadMediaInfo() {
- Log.d(TAG, "loadMediaInfo()");
- if (controller == null || controller.getMedia() == null) {
- return;
- }
- showTimeLeft = UserPreferences.shouldShowRemainingTime();
- onPositionObserverUpdate();
- checkFavorite();
- updatePlaybackSpeedButton();
- }
-
- void updatePlaybackSpeedButton() {
- // Only meaningful on AudioplayerActivity, where it is overridden.
- }
-
- void updatePlaybackSpeedButtonText() {
- // Only meaningful on AudioplayerActivity, where it is overridden.
- }
-
- void setupGUI() {
- setContentView(getContentViewResourceId());
- sbPosition = findViewById(R.id.sbPosition);
- txtvPosition = findViewById(R.id.txtvPosition);
- cardViewSeek = findViewById(R.id.cardViewSeek);
- txtvSeek = findViewById(R.id.txtvSeek);
-
- SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
- showTimeLeft = UserPreferences.shouldShowRemainingTime();
- Log.d("timeleft", showTimeLeft ? "true" : "false");
- txtvLength = findViewById(R.id.txtvLength);
- if (txtvLength != null) {
- txtvLength.setOnClickListener(v -> {
- showTimeLeft = !showTimeLeft;
- Playable media = controller.getMedia();
- if (media == null) {
- return;
- }
-
- TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
- String length;
- if (showTimeLeft) {
- int remainingTime = converter.convert(
- media.getDuration() - media.getPosition());
-
- length = "-" + Converter.getDurationStringLong(remainingTime);
- } else {
- int duration = converter.convert(media.getDuration());
- length = Converter.getDurationStringLong(duration);
- }
- txtvLength.setText(length);
-
- UserPreferences.setShowRemainTimeSetting(showTimeLeft);
- Log.d("timeleft on click", showTimeLeft ? "true" : "false");
- });
- }
-
- butRev = findViewById(R.id.butRev);
- txtvRev = findViewById(R.id.txtvRev);
- if (txtvRev != null) {
- txtvRev.setText(NumberFormat.getInstance().format(UserPreferences.getRewindSecs()));
- }
- butPlay = findViewById(R.id.butPlay);
- butFF = findViewById(R.id.butFF);
- txtvFF = findViewById(R.id.txtvFF);
- if (txtvFF != null) {
- txtvFF.setText(NumberFormat.getInstance().format(UserPreferences.getFastForwardSecs()));
- }
- butSkip = findViewById(R.id.butSkip);
-
- // SEEKBAR SETUP
-
- sbPosition.setOnSeekBarChangeListener(this);
-
- // BUTTON SETUP
-
- if (butRev != null) {
- butRev.setOnClickListener(v -> onRewind());
- butRev.setOnLongClickListener(v -> {
- SkipPreferenceDialog.showSkipPreference(MediaplayerActivity.this,
- SkipPreferenceDialog.SkipDirection.SKIP_REWIND, txtvRev);
- return true;
- });
- }
-
- butPlay.setOnClickListener(v -> onPlayPause());
-
- if (butFF != null) {
- butFF.setOnClickListener(v -> onFastForward());
- butFF.setOnLongClickListener(v -> {
- SkipPreferenceDialog.showSkipPreference(MediaplayerActivity.this,
- SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF);
- return false;
- });
- }
-
- if (butSkip != null) {
- butSkip.setOnClickListener(v ->
- IntentUtils.sendLocalBroadcast(MediaplayerActivity.this, PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
- }
- }
-
- void onRewind() {
- if (controller == null) {
- return;
- }
- int curr = controller.getPosition();
- controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000);
- }
-
- void onPlayPause() {
- if(controller == null) {
- return;
- }
- controller.init();
- controller.playPause();
- }
-
- void onFastForward() {
- if (controller == null) {
- return;
- }
- int curr = controller.getPosition();
- controller.seekTo(curr + UserPreferences.getFastForwardSecs() * 1000);
- }
-
- protected abstract int getContentViewResourceId();
-
- private void handleError(int errorCode) {
- final AlertDialog.Builder errorDialog = new AlertDialog.Builder(this);
- errorDialog.setTitle(R.string.error_label);
- errorDialog.setMessage(MediaPlayerError.getErrorString(this, errorCode));
- errorDialog.setNeutralButton("OK",
- (dialog, which) -> {
- dialog.dismiss();
- finish();
- }
- );
- errorDialog.create().show();
- }
-
- private float prog;
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (controller == null || txtvLength == null) {
- return;
- }
- if (fromUser) {
- prog = progress / ((float) seekBar.getMax());
- TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
- int position = converter.convert((int) (prog * controller.getDuration()));
- txtvSeek.setText(Converter.getDurationStringLong(position));
- }
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- cardViewSeek.setScaleX(.8f);
- cardViewSeek.setScaleY(.8f);
- cardViewSeek.animate()
- .setInterpolator(new FastOutSlowInInterpolator())
- .alpha(1f).scaleX(1f).scaleY(1f)
- .setDuration(200)
- .start();
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- if (controller != null) {
- controller.seekTo((int) (prog * controller.getDuration()));
- }
- cardViewSeek.setScaleX(1f);
- cardViewSeek.setScaleY(1f);
- cardViewSeek.animate()
- .setInterpolator(new FastOutSlowInInterpolator())
- .alpha(0f).scaleX(.8f).scaleY(.8f)
- .setDuration(200)
- .start();
- }
-
- private void checkFavorite() {
- FeedItem feedItem = getFeedItem(controller.getMedia());
- if (feedItem == null) {
- return;
- }
- if (disposable != null) {
- disposable.dispose();
- }
- disposable = Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId()))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(
- item -> {
- boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE);
- if (isFavorite != isFav) {
- isFavorite = isFav;
- invalidateOptionsMenu();
- }
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
- }
-
- @Nullable
- private static FeedItem getFeedItem(@Nullable Playable playable) {
- if (playable instanceof FeedMedia) {
- return ((FeedMedia) playable).getItem();
- } else {
- return null;
- }
- }
-}
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 c6f2cc84b..575e94f8c 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -15,10 +15,12 @@ import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
@@ -31,10 +33,6 @@ import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
-import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
@@ -54,18 +52,22 @@ import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeExceptio
import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.FileNameGenerator;
import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.Optional;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.URLChecker;
-import de.danoeh.antennapod.core.util.playback.RemoteMedia;
import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.databinding.OnlinefeedviewActivityBinding;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.danoeh.antennapod.discovery.PodcastSearcherRegistry;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.model.playback.RemoteMedia;
+import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
+import io.reactivex.observers.DisposableMaybeObserver;
import io.reactivex.schedulers.Schedulers;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
@@ -256,7 +258,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
url = URLChecker.prepareURL(url);
feed = new Feed(url, null);
if (username != null && password != null) {
- feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password));
+ feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL,
+ VolumeAdaptionSetting.OFF, username, password));
}
String fileUrl = new File(getExternalCacheDir(),
FileNameGenerator.generateFileName(feed.getDownload_url())).toString();
@@ -320,33 +323,45 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
Log.d(TAG, "Parsing feed");
- parser = Observable.fromCallable(this::doParseFeed)
+ parser = Maybe.fromCallable(this::doParseFeed)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(
- optionalResult -> {
- if (optionalResult.isPresent()) {
- FeedHandlerResult result = optionalResult.get();
- beforeShowFeedInformation(result.feed);
- showFeedInformation(result.feed, result.alternateFeedUrls);
- }
- }, error -> {
+ .subscribeWith(new DisposableMaybeObserver<FeedHandlerResult>() {
+ @Override
+ public void onSuccess(@NonNull FeedHandlerResult result) {
+ showFeedInformation(result.feed, result.alternateFeedUrls);
+ }
+
+ @Override
+ public void onComplete() {
+ // Ignore null result: We showed the discovery dialog.
+ }
+
+ @Override
+ public void onError(@NonNull Throwable error) {
showErrorDialog(error.getMessage(), "");
Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error));
- });
+ }
+ });
}
- @NonNull
- private Optional<FeedHandlerResult> doParseFeed() throws Exception {
+ /**
+ * Try to parse the feed.
+ * @return The FeedHandlerResult if successful.
+ * Null if unsuccessful but we started another attempt.
+ * @throws Exception If unsuccessful but we do not know a resolution.
+ */
+ @Nullable
+ private FeedHandlerResult doParseFeed() throws Exception {
FeedHandler handler = new FeedHandler();
try {
- return Optional.of(handler.parseFeed(feed));
+ return handler.parseFeed(feed);
} catch (UnsupportedFeedtypeException e) {
Log.d(TAG, "Unsupported feed type detected");
if ("html".equalsIgnoreCase(e.getRootElement())) {
boolean dialogShown = showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url());
if (dialogShown) {
- return Optional.empty();
+ return null; // Should not display an error message
} else {
throw new UnsupportedFeedtypeException(getString(R.string.download_error_unsupported_type_html));
}
@@ -363,23 +378,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
/**
- * Called after the feed has been downloaded and parsed and before showFeedInformation is called.
- * This method is executed on a background thread
- */
- private void beforeShowFeedInformation(Feed feed) {
- Log.d(TAG, "Removing HTML from feed description");
-
- feed.setDescription(HtmlToPlainText.getPlainText(feed.getDescription()));
-
- Log.d(TAG, "Removing HTML from shownotes");
- if (feed.getItems() != null) {
- for (FeedItem item : feed.getItems()) {
- item.setDescription(HtmlToPlainText.getPlainText(item.getDescription()));
- }
- }
- }
-
- /**
* Called when feed parsed successfully.
* This method is executed on the GUI thread.
*/
@@ -422,7 +420,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
viewBinding.titleLabel.setText(feed.getTitle());
viewBinding.authorLabel.setText(feed.getAuthor());
- description.setText(feed.getDescription());
+ description.setText(HtmlToPlainText.getPlainText(feed.getDescription()));
viewBinding.subscribeButton.setOnClickListener(v -> {
if (feedInFeedlist(feed)) {
@@ -479,8 +477,17 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
for (String url : alternateFeedUrls.keySet()) {
alternateUrlsTitleList.add(alternateFeedUrls.get(url));
}
- ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, alternateUrlsTitleList);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
+ R.layout.alternate_urls_item, alternateUrlsTitleList) {
+ @Override
+ public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+ // reusing the old view causes a visual bug on Android <= 10
+ return super.getDropDownView(position, null, parent);
+ }
+ };
+
+ adapter.setDropDownViewResource(R.layout.alternate_urls_dropdown_item);
viewBinding.alternateUrlsSpinner.setAdapter(adapter);
viewBinding.alternateUrlsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
index 6e526911b..cd72e34e8 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -1,19 +1,23 @@
package de.danoeh.antennapod.activity;
+import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceFragmentCompat;
+
+import android.provider.Settings;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
import com.bytehamster.lib.preferencesearch.SearchPreferenceResult;
import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.databinding.SettingsActivityBinding;
import de.danoeh.antennapod.fragment.preferences.AutoDownloadPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.GpodderPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.ImportExportPreferencesFragment;
@@ -30,6 +34,7 @@ import de.danoeh.antennapod.fragment.preferences.UserInterfacePreferencesFragmen
*/
public class PreferenceActivity extends AppCompatActivity implements SearchPreferenceResultListener {
private static final String FRAGMENT_TAG = "tag_preferences";
+ public static final String OPEN_AUTO_DOWNLOAD_SETTINGS = "OpenAutoDownloadSettings";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -41,17 +46,18 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe
ab.setDisplayHomeAsUpEnabled(true);
}
- FrameLayout root = new FrameLayout(this);
- root.setId(R.id.content);
- root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- setContentView(root);
+ final SettingsActivityBinding binding = SettingsActivityBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
if (getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG) == null) {
getSupportFragmentManager().beginTransaction()
- .replace(R.id.content, new MainPreferencesFragment(), FRAGMENT_TAG)
+ .replace(R.id.settingsContainer, new MainPreferencesFragment(), FRAGMENT_TAG)
.commit();
}
+ Intent intent = getIntent();
+ if (intent.getBooleanExtra(OPEN_AUTO_DOWNLOAD_SETTINGS, false)) {
+ openScreen(R.xml.preferences_autodownload);
+ }
}
private PreferenceFragmentCompat getPreferenceScreen(int screen) {
@@ -95,6 +101,8 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe
return R.string.gpodnet_main_label;
case R.xml.preferences_notifications:
return R.string.notification_pref_fragment;
+ case R.xml.feed_settings:
+ return R.string.feed_settings_label;
default:
return R.string.settings_label;
}
@@ -102,8 +110,17 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe
public PreferenceFragmentCompat openScreen(int screen) {
PreferenceFragmentCompat fragment = getPreferenceScreen(screen);
- getSupportFragmentManager().beginTransaction().replace(R.id.content, fragment)
- .addToBackStack(getString(getTitleOfPage(screen))).commit();
+ if (screen == R.xml.preferences_notifications && Build.VERSION.SDK_INT >= 26) {
+ Intent intent = new Intent();
+ intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
+ startActivity(intent);
+ } else {
+ getSupportFragmentManager().beginTransaction().replace(R.id.settingsContainer, fragment)
+ .addToBackStack(getString(getTitleOfPage(screen))).commit();
+ }
+
+
return fragment;
}
@@ -128,7 +145,18 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe
@Override
public void onSearchResultClicked(SearchPreferenceResult result) {
- PreferenceFragmentCompat fragment = openScreen(result.getResourceFile());
- result.highlight(fragment);
+ int screen = result.getResourceFile();
+ if (screen == R.xml.feed_settings) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.feed_settings_label);
+ builder.setMessage(R.string.pref_feed_settings_dialog_msg);
+ builder.setPositiveButton(android.R.string.ok, null);
+ builder.show();
+ } else if (screen == R.xml.preferences_notifications) {
+ openScreen(screen);
+ } else {
+ PreferenceFragmentCompat fragment = openScreen(result.getResourceFile());
+ result.highlight(fragment);
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
index 15d0bec4a..d436acf0d 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -1,53 +1,79 @@
package de.danoeh.antennapod.activity;
import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.content.Intent;
+import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.AnimationSet;
-import android.view.animation.ScaleAnimation;
-import android.widget.EditText;
-import android.widget.ImageView;
-
-import androidx.core.view.WindowCompat;
-import androidx.appcompat.app.ActionBar;
+import android.os.Looper;
import android.util.Log;
import android.util.Pair;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.WindowManager;
+import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
+import android.view.animation.ScaleAnimation;
+import android.widget.EditText;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
import android.widget.SeekBar;
-
-import java.lang.ref.WeakReference;
-import java.util.concurrent.atomic.AtomicBoolean;
-
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.view.WindowCompat;
+import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
+import com.bumptech.glide.Glide;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.ShareUtils;
+import de.danoeh.antennapod.core.util.StorageUtils;
+import de.danoeh.antennapod.core.util.TimeSpeedConverter;
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.databinding.VideoplayerActivityBinding;
+import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
+import de.danoeh.antennapod.dialog.ShareDialog;
+import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
+import de.danoeh.antennapod.dialog.SleepTimerDialog;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
-import de.danoeh.antennapod.view.AspectRatioVideoView;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+import org.apache.commons.lang3.StringUtils;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Activity for playing video files.
*/
-public class VideoplayerActivity extends MediaplayerActivity {
+public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.OnSeekBarChangeListener {
private static final String TAG = "VideoplayerActivity";
/**
@@ -57,36 +83,39 @@ public class VideoplayerActivity extends MediaplayerActivity {
private boolean videoSurfaceCreated = false;
private boolean destroyingDueToReload = false;
private long lastScreenTap = 0;
-
- private VideoControlsHider videoControlsHider = new VideoControlsHider(this);
-
- private final AtomicBoolean isSetup = new AtomicBoolean(false);
-
- private LinearLayout controls;
- private LinearLayout videoOverlay;
- private AspectRatioVideoView videoview;
- private ProgressBar progressIndicator;
- private FrameLayout videoframe;
- private ImageView skipAnimationView;
-
- @Override
- protected void chooseTheme() {
- setTheme(R.style.Theme_AntennaPod_VideoPlayer);
- }
+ private Handler videoControlsHider = new Handler(Looper.getMainLooper());
+ private VideoplayerActivityBinding viewBinding;
+ private PlaybackController controller;
+ private boolean showTimeLeft = false;
+ private boolean isFavorite = false;
+ private Disposable disposable;
+ private float prog;
@SuppressLint("AppCompatMethod")
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
- supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY); // has to be called before setting layout content
+ // has to be called before setting layout content
+ supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY);
+ setTheme(R.style.Theme_AntennaPod_VideoPlayer);
super.onCreate(savedInstanceState);
+
+ Log.d(TAG, "onCreate()");
+ StorageUtils.checkStorageAvailability(this);
+
+ getWindow().setFormat(PixelFormat.TRANSPARENT);
+ viewBinding = VideoplayerActivityBinding.inflate(LayoutInflater.from(this));
+ setContentView(viewBinding.getRoot());
+ setupView();
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(0x80000000));
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override
protected void onResume() {
super.onResume();
+ StorageUtils.checkStorageAvailability(this);
if (PlaybackService.isCasting()) {
Intent intent = PlaybackService.getPlayerActivityIntent(this);
if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) {
@@ -99,11 +128,20 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onStop() {
+ if (controller != null) {
+ controller.release();
+ controller = null; // prevent leak
+ }
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ EventBus.getDefault().unregister(this);
super.onStop();
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
- videoControlsHider.stop();
+ videoControlsHider.removeCallbacks(hideVideoControls);
}
- progressIndicator.setVisibility(View.GONE); // Controller released; we will not receive buffering updates
+ // Controller released; we will not receive buffering updates
+ viewBinding.progressBar.setVisibility(View.GONE);
}
@Override
@@ -115,6 +153,16 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
@Override
+ protected void onStart() {
+ super.onStart();
+ controller = newPlaybackController();
+ controller.init();
+ loadMediaInfo();
+ onPositionObserverUpdate();
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
protected void onPause() {
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) {
@@ -124,16 +172,103 @@ public class VideoplayerActivity extends MediaplayerActivity {
super.onPause();
}
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
- protected void onDestroy() {
- videoControlsHider.stop();
- videoControlsHider = null;
- super.onDestroy();
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ Glide.get(this).trimMemory(level);
}
@Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ Glide.get(this).clearMemory();
+ }
+
+ private PlaybackController newPlaybackController() {
+ return new PlaybackController(this) {
+ @Override
+ public void onPositionObserverUpdate() {
+ VideoplayerActivity.this.onPositionObserverUpdate();
+ }
+
+ @Override
+ public void onBufferStart() {
+ viewBinding.progressBar.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onBufferEnd() {
+ viewBinding.progressBar.setVisibility(View.INVISIBLE);
+ }
+
+ @Override
+ public void onBufferUpdate(float progress) {
+ viewBinding.sbPosition.setSecondaryProgress((int) (progress * viewBinding.sbPosition.getMax()));
+ }
+
+ @Override
+ public void handleError(int code) {
+ final AlertDialog.Builder errorDialog = new AlertDialog.Builder(VideoplayerActivity.this);
+ errorDialog.setTitle(R.string.error_label);
+ errorDialog.setMessage(MediaPlayerError.getErrorString(VideoplayerActivity.this, code));
+ errorDialog.setNeutralButton(android.R.string.ok, (dialog, which) -> finish());
+ errorDialog.show();
+ }
+
+ @Override
+ public void onReloadNotification(int code) {
+ VideoplayerActivity.this.onReloadNotification(code);
+ }
+
+ @Override
+ public void onSleepTimerUpdate() {
+ supportInvalidateOptionsMenu();
+ }
+
+ @Override
+ protected void updatePlayButtonShowsPlay(boolean showPlay) {
+ viewBinding.playButton.setIsShowPlay(showPlay);
+ }
+
+ @Override
+ public void loadMediaInfo() {
+ VideoplayerActivity.this.loadMediaInfo();
+ }
+
+ @Override
+ public void onAwaitingVideoSurface() {
+ setupVideoAspectRatio();
+ if (videoSurfaceCreated && controller != null) {
+ Log.d(TAG, "Videosurface already created, setting videosurface now");
+ controller.setVideoSurface(viewBinding.videoView.getHolder());
+ }
+ }
+
+ @Override
+ public void onPlaybackEnd() {
+ finish();
+ }
+
+ @Override
+ protected void setScreenOn(boolean enable) {
+ if (enable) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ } else {
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+ }
+ };
+ }
+
protected void loadMediaInfo() {
- super.loadMediaInfo();
+ Log.d(TAG, "loadMediaInfo()");
+ if (controller == null || controller.getMedia() == null) {
+ return;
+ }
+ showTimeLeft = UserPreferences.shouldShowRemainingTime();
+ onPositionObserverUpdate();
+ checkFavorite();
Playable media = controller.getMedia();
if (media != null) {
getSupportActionBar().setSubtitle(media.getEpisodeTitle());
@@ -141,75 +276,103 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
}
- @Override
- protected void setupGUI() {
- if (isSetup.getAndSet(true)) {
- return;
- }
- super.setupGUI();
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- controls = findViewById(R.id.controls);
- videoOverlay = findViewById(R.id.overlay);
- videoview = findViewById(R.id.videoview);
- videoframe = findViewById(R.id.videoframe);
- progressIndicator = findViewById(R.id.progressIndicator);
- skipAnimationView = findViewById(R.id.skip_animation);
- videoview.getHolder().addCallback(surfaceHolderCallback);
- videoframe.setOnTouchListener(onVideoviewTouched);
- videoOverlay.setOnTouchListener((view, motionEvent) -> true); // To suppress touches directly below the slider
- videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- videoOverlay.setFitsSystemWindows(true);
+ protected void setupView() {
+ showTimeLeft = UserPreferences.shouldShowRemainingTime();
+ Log.d("timeleft", showTimeLeft ? "true" : "false");
+ viewBinding.durationLabel.setOnClickListener(v -> {
+ showTimeLeft = !showTimeLeft;
+ Playable media = controller.getMedia();
+ if (media == null) {
+ return;
+ }
+
+ TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
+ String length;
+ if (showTimeLeft) {
+ int remainingTime = converter.convert(media.getDuration() - media.getPosition());
+ length = "-" + Converter.getDurationStringLong(remainingTime);
+ } else {
+ int duration = converter.convert(media.getDuration());
+ length = Converter.getDurationStringLong(duration);
+ }
+ viewBinding.durationLabel.setText(length);
+
+ UserPreferences.setShowRemainTimeSetting(showTimeLeft);
+ Log.d("timeleft on click", showTimeLeft ? "true" : "false");
+ });
+
+ viewBinding.sbPosition.setOnSeekBarChangeListener(this);
+ viewBinding.rewindButton.setOnClickListener(v -> onRewind());
+ viewBinding.rewindButton.setOnLongClickListener(v -> {
+ SkipPreferenceDialog.showSkipPreference(VideoplayerActivity.this,
+ SkipPreferenceDialog.SkipDirection.SKIP_REWIND, null);
+ return true;
+ });
+ viewBinding.playButton.setIsVideoScreen(true);
+ viewBinding.playButton.setOnClickListener(v -> onPlayPause());
+ viewBinding.fastForwardButton.setOnClickListener(v -> onFastForward());
+ viewBinding.fastForwardButton.setOnLongClickListener(v -> {
+ SkipPreferenceDialog.showSkipPreference(VideoplayerActivity.this,
+ SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, null);
+ return false;
+ });
+ // To suppress touches directly below the slider
+ viewBinding.bottomControlsContainer.setOnTouchListener((view, motionEvent) -> true);
+ viewBinding.bottomControlsContainer.setFitsSystemWindows(true);
+ viewBinding.videoView.getHolder().addCallback(surfaceHolderCallback);
+ viewBinding.videoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
setupVideoControlsToggler();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
- videoframe.getViewTreeObserver().addOnGlobalLayoutListener(() ->
- videoview.setAvailableSize(videoframe.getWidth(), videoframe.getHeight()));
+ viewBinding.videoPlayerContainer.setOnTouchListener(onVideoviewTouched);
+ viewBinding.videoPlayerContainer.getViewTreeObserver().addOnGlobalLayoutListener(() ->
+ viewBinding.videoView.setAvailableSize(
+ viewBinding.videoPlayerContainer.getWidth(), viewBinding.videoPlayerContainer.getHeight()));
}
- @Override
- protected void onAwaitingVideoSurface() {
- setupVideoAspectRatio();
- if (videoSurfaceCreated && controller != null) {
- Log.d(TAG, "Videosurface already created, setting videosurface now");
- controller.setVideoSurface(videoview.getHolder());
+ private final Runnable hideVideoControls = () -> {
+ if (videoControlsShowing) {
+ Log.d(TAG, "Hiding video controls");
+ getSupportActionBar().hide();
+ hideVideoControls(true);
+ videoControlsShowing = false;
}
- }
+ };
private final View.OnTouchListener onVideoviewTouched = (v, event) -> {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- if (PictureInPictureUtil.isInPictureInPictureMode(this)) {
- return true;
- }
- videoControlsHider.stop();
+ if (event.getAction() != MotionEvent.ACTION_DOWN) {
+ return false;
+ }
+ if (PictureInPictureUtil.isInPictureInPictureMode(this)) {
+ return true;
+ }
+ videoControlsHider.removeCallbacks(hideVideoControls);
- if (System.currentTimeMillis() - lastScreenTap < 300) {
- if (event.getX() > v.getMeasuredWidth() / 2.0f) {
- onFastForward();
- showSkipAnimation(true);
- } else {
- onRewind();
- showSkipAnimation(false);
- }
- if (videoControlsShowing) {
- getSupportActionBar().hide();
- hideVideoControls(false);
- videoControlsShowing = false;
- }
- return true;
+ if (System.currentTimeMillis() - lastScreenTap < 300) {
+ if (event.getX() > v.getMeasuredWidth() / 2.0f) {
+ onFastForward();
+ showSkipAnimation(true);
+ } else {
+ onRewind();
+ showSkipAnimation(false);
}
-
- toggleVideoControlsVisibility();
if (videoControlsShowing) {
- setupVideoControlsToggler();
+ getSupportActionBar().hide();
+ hideVideoControls(false);
+ videoControlsShowing = false;
}
-
- lastScreenTap = System.currentTimeMillis();
return true;
- } else {
- return false;
}
+
+ toggleVideoControlsVisibility();
+ if (videoControlsShowing) {
+ setupVideoControlsToggler();
+ }
+
+ lastScreenTap = System.currentTimeMillis();
+ return true;
};
private void showSkipAnimation(boolean isForward) {
@@ -220,18 +383,18 @@ public class VideoplayerActivity extends MediaplayerActivity {
skipAnimation.setFillAfter(false);
skipAnimation.setDuration(800);
- FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) skipAnimationView.getLayoutParams();
+ FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) viewBinding.skipAnimationImage.getLayoutParams();
if (isForward) {
- skipAnimationView.setImageResource(R.drawable.ic_av_fast_forward_white_80dp);
+ viewBinding.skipAnimationImage.setImageResource(R.drawable.ic_fast_forward_video_white);
params.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
} else {
- skipAnimationView.setImageResource(R.drawable.ic_av_fast_rewind_white_80dp);
+ viewBinding.skipAnimationImage.setImageResource(R.drawable.ic_fast_rewind_video_white);
params.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
}
- skipAnimationView.setVisibility(View.VISIBLE);
- skipAnimationView.setLayoutParams(params);
- skipAnimationView.startAnimation(skipAnimation);
+ viewBinding.skipAnimationImage.setVisibility(View.VISIBLE);
+ viewBinding.skipAnimationImage.setLayoutParams(params);
+ viewBinding.skipAnimationImage.startAnimation(skipAnimation);
skipAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
@@ -239,7 +402,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
public void onAnimationEnd(Animation animation) {
- skipAnimationView.setVisibility(View.GONE);
+ viewBinding.skipAnimationImage.setVisibility(View.GONE);
}
@Override
@@ -249,10 +412,9 @@ public class VideoplayerActivity extends MediaplayerActivity {
});
}
- @SuppressLint("NewApi")
private void setupVideoControlsToggler() {
- videoControlsHider.stop();
- videoControlsHider.start();
+ videoControlsHider.removeCallbacks(hideVideoControls);
+ videoControlsHider.postDelayed(hideVideoControls, 2500);
}
private void setupVideoAspectRatio() {
@@ -260,7 +422,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
Pair<Integer, Integer> videoSize = controller.getVideoSize();
if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
- videoview.setVideoSize(videoSize.first, videoSize.second);
+ viewBinding.videoView.setVideoSize(videoSize.first, videoSize.second);
} else {
Log.e(TAG, "Could not determine video size");
}
@@ -270,7 +432,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
private void toggleVideoControlsVisibility() {
if (videoControlsShowing) {
getSupportActionBar().hide();
- hideVideoControls();
+ hideVideoControls(true);
} else {
getSupportActionBar().show();
showVideoControls();
@@ -278,29 +440,35 @@ public class VideoplayerActivity extends MediaplayerActivity {
videoControlsShowing = !videoControlsShowing;
}
- @Override
- protected void onRewind() {
- super.onRewind();
+ void onRewind() {
+ if (controller == null) {
+ return;
+ }
+ int curr = controller.getPosition();
+ controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000);
setupVideoControlsToggler();
}
- @Override
- protected void onPlayPause() {
- super.onPlayPause();
+ void onPlayPause() {
+ if (controller == null) {
+ return;
+ }
+ controller.playPause();
setupVideoControlsToggler();
}
- @Override
- protected void onFastForward() {
- super.onFastForward();
+ void onFastForward() {
+ if (controller == null) {
+ return;
+ }
+ int curr = controller.getPosition();
+ controller.seekTo(curr + UserPreferences.getFastForwardSecs() * 1000);
setupVideoControlsToggler();
}
-
private final SurfaceHolder.Callback surfaceHolderCallback = new SurfaceHolder.Callback() {
@Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width,
- int height) {
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
holder.setFixedSize(width, height);
}
@@ -326,8 +494,6 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
};
-
- @Override
protected void onReloadNotification(int notificationCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && PictureInPictureUtil.isInPictureInPictureMode(this)) {
if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO
@@ -344,84 +510,87 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
}
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- super.onStartTrackingTouch(seekBar);
- videoControlsHider.stop();
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- super.onStopTrackingTouch(seekBar);
- setupVideoControlsToggler();
- }
-
- @Override
- protected void onBufferStart() {
- progressIndicator.setVisibility(View.VISIBLE);
- }
-
- @Override
- protected void onBufferEnd() {
- progressIndicator.setVisibility(View.INVISIBLE);
- }
-
- @SuppressLint("NewApi")
private void showVideoControls() {
- videoOverlay.setVisibility(View.VISIBLE);
- controls.setVisibility(View.VISIBLE);
+ viewBinding.bottomControlsContainer.setVisibility(View.VISIBLE);
+ viewBinding.controlsContainer.setVisibility(View.VISIBLE);
final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_in);
if (animation != null) {
- videoOverlay.startAnimation(animation);
- controls.startAnimation(animation);
+ viewBinding.bottomControlsContainer.startAnimation(animation);
+ viewBinding.controlsContainer.startAnimation(animation);
}
- videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+ viewBinding.videoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
- @SuppressLint("NewApi")
private void hideVideoControls(boolean showAnimation) {
if (showAnimation) {
final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
if (animation != null) {
- videoOverlay.startAnimation(animation);
- controls.startAnimation(animation);
+ viewBinding.bottomControlsContainer.startAnimation(animation);
+ viewBinding.controlsContainer.startAnimation(animation);
}
}
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
| View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- videoOverlay.setFitsSystemWindows(true);
+ viewBinding.bottomControlsContainer.setFitsSystemWindows(true);
- videoOverlay.setVisibility(View.GONE);
- controls.setVisibility(View.GONE);
+ viewBinding.bottomControlsContainer.setVisibility(View.GONE);
+ viewBinding.controlsContainer.setVisibility(View.GONE);
}
- private void hideVideoControls() {
- hideVideoControls(true);
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ onPositionObserverUpdate();
}
- @Override
- protected int getContentViewResourceId() {
- return R.layout.videoplayer_activity;
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlaybackServiceChanged(ServiceEvent event) {
+ if (event.action == ServiceEvent.Action.SERVICE_SHUT_DOWN) {
+ finish();
+ }
}
@Override
- protected void setScreenOn(boolean enable) {
- super.setScreenOn(enable);
- if (enable) {
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- } else {
- getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ requestCastButton(menu);
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.mediaplayer, menu);
+ return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
+ if (controller == null) {
+ return false;
+ }
+ Playable media = controller.getMedia();
+ boolean isFeedMedia = (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);
+
+ boolean isItemAndHasLink = isFeedMedia && ShareUtils.hasLinkToShare(((FeedMedia) media).getItem());
+ boolean isItemHasDownloadLink = isFeedMedia && ((FeedMedia) media).getDownload_url() != null;
+ menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink);
+
+ menu.findItem(R.id.add_to_favorites_item).setVisible(false);
+ menu.findItem(R.id.remove_from_favorites_item).setVisible(false);
+ if (isFeedMedia) {
+ menu.findItem(R.id.add_to_favorites_item).setVisible(!isFavorite);
+ menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite);
+ }
+
+ menu.findItem(R.id.set_sleeptimer_item).setVisible(!controller.sleepTimerActive());
+ menu.findItem(R.id.disable_sleeptimer_item).setVisible(controller.sleepTimerActive());
+
if (PictureInPictureUtil.supportsPictureInPicture(this)) {
menu.findItem(R.id.player_go_to_picture_in_picture).setVisible(true);
}
- menu.findItem(R.id.audio_controls).setIcon(R.drawable.ic_sliders_white);
+ menu.findItem(R.id.audio_controls).setIcon(R.drawable.ic_sliders);
return true;
}
@@ -431,53 +600,170 @@ public class VideoplayerActivity extends MediaplayerActivity {
compatEnterPictureInPicture();
return true;
}
- return super.onOptionsItemSelected(item);
+ if (item.getItemId() == android.R.id.home) {
+ Intent intent = new Intent(VideoplayerActivity.this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ finish();
+ return true;
+ }
+
+ if (controller == null) {
+ return false;
+ }
+
+ Playable media = controller.getMedia();
+ if (media == null) {
+ return false;
+ }
+ final @Nullable FeedItem feedItem = getFeedItem(media); // some options option requires FeedItem
+ if (item.getItemId() == R.id.add_to_favorites_item && feedItem != null) {
+ DBWriter.addFavoriteItem(feedItem);
+ isFavorite = true;
+ invalidateOptionsMenu();
+ } else if (item.getItemId() == R.id.remove_from_favorites_item && feedItem != null) {
+ DBWriter.removeFavoriteItem(feedItem);
+ isFavorite = false;
+ invalidateOptionsMenu();
+ } else if (item.getItemId() == R.id.disable_sleeptimer_item
+ || item.getItemId() == R.id.set_sleeptimer_item) {
+ new SleepTimerDialog().show(getSupportFragmentManager(), "SleepTimerDialog");
+ } else if (item.getItemId() == R.id.audio_controls) {
+ PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance();
+ dialog.show(getSupportFragmentManager(), "playback_controls");
+ } else if (item.getItemId() == R.id.open_feed_item && feedItem != null) {
+ Intent intent = MainActivity.getIntentToOpenFeed(this, feedItem.getFeedId());
+ startActivity(intent);
+ } else if (item.getItemId() == R.id.visit_website_item) {
+ IntentUtils.openInBrowser(VideoplayerActivity.this, getWebsiteLinkWithFallback(media));
+ } else if (item.getItemId() == R.id.share_item && feedItem != null) {
+ ShareDialog shareDialog = ShareDialog.newInstance(feedItem);
+ shareDialog.show(getSupportFragmentManager(), "ShareEpisodeDialog");
+ } else {
+ return false;
+ }
+ return true;
}
- private void compatEnterPictureInPicture() {
- if (PictureInPictureUtil.supportsPictureInPicture(this) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- getSupportActionBar().hide();
- hideVideoControls(false);
- enterPictureInPictureMode();
+ private static String getWebsiteLinkWithFallback(Playable media) {
+ if (media == null) {
+ return null;
+ } else if (StringUtils.isNotBlank(media.getWebsiteLink())) {
+ return media.getWebsiteLink();
+ } else if (media instanceof FeedMedia) {
+ return FeedItemUtil.getLinkWithFallback(((FeedMedia) media).getItem());
}
+ return null;
}
- private static class VideoControlsHider extends Handler {
+ void onPositionObserverUpdate() {
+ if (controller == null) {
+ return;
+ }
- private static final int DELAY = 2500;
+ TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
+ int currentPosition = converter.convert(controller.getPosition());
+ int duration = converter.convert(controller.getDuration());
+ int remainingTime = converter.convert(
+ controller.getDuration() - controller.getPosition());
+ Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition));
+ if (currentPosition == PlaybackService.INVALID_TIME
+ || duration == PlaybackService.INVALID_TIME) {
+ Log.w(TAG, "Could not react to position observer update because of invalid time");
+ return;
+ }
+ viewBinding.positionLabel.setText(Converter.getDurationStringLong(currentPosition));
+ if (showTimeLeft) {
+ viewBinding.durationLabel.setText("-" + Converter.getDurationStringLong(remainingTime));
+ } else {
+ viewBinding.durationLabel.setText(Converter.getDurationStringLong(duration));
+ }
+ updateProgressbarPosition(currentPosition, duration);
+ }
- private WeakReference<VideoplayerActivity> activity;
+ private void updateProgressbarPosition(int position, int duration) {
+ Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration + ")");
+ float progress = ((float) position) / duration;
+ viewBinding.sbPosition.setProgress((int) (progress * viewBinding.sbPosition.getMax()));
+ }
- VideoControlsHider(VideoplayerActivity activity) {
- this.activity = new WeakReference<>(activity);
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (controller == null) {
+ return;
+ }
+ if (fromUser) {
+ prog = progress / ((float) seekBar.getMax());
+ TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
+ int position = converter.convert((int) (prog * controller.getDuration()));
+ viewBinding.seekPositionLabel.setText(Converter.getDurationStringLong(position));
}
+ }
- private final Runnable hideVideoControls = () -> {
- VideoplayerActivity vpa = activity != null ? activity.get() : null;
- if (vpa == null) {
- return;
- }
- if (vpa.videoControlsShowing) {
- Log.d(TAG, "Hiding video controls");
- ActionBar actionBar = vpa.getSupportActionBar();
- if (actionBar != null) {
- actionBar.hide();
- }
- vpa.hideVideoControls();
- vpa.videoControlsShowing = false;
- }
- };
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ viewBinding.seekCardView.setScaleX(.8f);
+ viewBinding.seekCardView.setScaleY(.8f);
+ viewBinding.seekCardView.animate()
+ .setInterpolator(new FastOutSlowInInterpolator())
+ .alpha(1f).scaleX(1f).scaleY(1f)
+ .setDuration(200)
+ .start();
+ videoControlsHider.removeCallbacks(hideVideoControls);
+ }
- public void start() {
- this.postDelayed(hideVideoControls, DELAY);
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ if (controller != null) {
+ controller.seekTo((int) (prog * controller.getDuration()));
}
+ viewBinding.seekCardView.setScaleX(1f);
+ viewBinding.seekCardView.setScaleY(1f);
+ viewBinding.seekCardView.animate()
+ .setInterpolator(new FastOutSlowInInterpolator())
+ .alpha(0f).scaleX(.8f).scaleY(.8f)
+ .setDuration(200)
+ .start();
+ setupVideoControlsToggler();
+ }
- void stop() {
- this.removeCallbacks(hideVideoControls);
+ private void checkFavorite() {
+ FeedItem feedItem = getFeedItem(controller.getMedia());
+ if (feedItem == null) {
+ return;
+ }
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId()))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ item -> {
+ boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE);
+ if (isFavorite != isFav) {
+ isFavorite = isFav;
+ invalidateOptionsMenu();
+ }
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+
+ @Nullable
+ private static FeedItem getFeedItem(@Nullable Playable playable) {
+ if (playable instanceof FeedMedia) {
+ return ((FeedMedia) playable).getItem();
+ } else {
+ return null;
}
-
}
+ private void compatEnterPictureInPicture() {
+ if (PictureInPictureUtil.supportsPictureInPicture(this) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ getSupportActionBar().hide();
+ hideVideoControls(false);
+ enterPictureInPictureMode();
+ }
+ }
//Hardware keyboard support
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
index 8380d8626..faa18434e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
@@ -15,13 +15,13 @@ import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.model.feed.Chapter;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.ui.common.ThemeUtils;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.ui.common.CircularProgressBar;
public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapter.ChapterHolder> {
@@ -77,7 +77,7 @@ public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapte
holder.link.setText(sc.getLink());
holder.link.setOnClickListener(v -> IntentUtils.openInBrowser(context, sc.getLink()));
}
- holder.secondaryActionIcon.setImageResource(ThemeUtils.getDrawableFromAttr(context, R.attr.av_play));
+ holder.secondaryActionIcon.setImageResource(R.drawable.ic_play_48dp);
holder.secondaryActionButton.setContentDescription(context.getString(R.string.play_chapter));
holder.secondaryActionButton.setOnClickListener(v -> {
if (callback != null) {
@@ -92,7 +92,7 @@ public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapte
progress = Math.max(progress, CircularProgressBar.MINIMUM_PERCENTAGE);
progress = Math.min(progress, CircularProgressBar.MAXIMUM_PERCENTAGE);
holder.progressBar.setPercentage(progress, position);
- holder.secondaryActionIcon.setImageResource(ThemeUtils.getDrawableFromAttr(context, R.attr.av_replay));
+ holder.secondaryActionIcon.setImageResource(R.drawable.ic_replay);
} else {
holder.itemView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.transparent));
holder.progressBar.setPercentage(0, null);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java
index d782d4ed5..aeaf526be 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java
@@ -21,6 +21,7 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
public class CoverLoader {
+ private int resource = 0;
private String uri;
private String fallbackUri;
private TextView txtvPlaceholder;
@@ -37,6 +38,11 @@ public class CoverLoader {
return this;
}
+ public CoverLoader withResource(int resource) {
+ this.resource = resource;
+ return this;
+ }
+
public CoverLoader withFallbackUri(String uri) {
fallbackUri = uri;
return this;
@@ -66,6 +72,12 @@ public class CoverLoader {
}
public void load() {
+ if (resource != 0) {
+ imgvCover.setImageResource(resource);
+ CoverTarget.setPlaceholderVisibility(txtvPlaceholder, textAndImageCombined);
+ return;
+ }
+
RequestOptions options = new RequestOptions()
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
@@ -106,15 +118,7 @@ public class CoverLoader {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
- TextView txtvPlaceholder = placeholder.get();
- if (txtvPlaceholder != null) {
- if (textAndImageCombined) {
- int bgColor = txtvPlaceholder.getContext().getResources().getColor(R.color.feed_text_bg);
- txtvPlaceholder.setBackgroundColor(bgColor);
- } else {
- txtvPlaceholder.setVisibility(View.INVISIBLE);
- }
- }
+ setPlaceholderVisibility(placeholder.get(), textAndImageCombined);
ImageView ivCover = cover.get();
ivCover.setImageDrawable(resource);
}
@@ -124,5 +128,16 @@ public class CoverLoader {
ImageView ivCover = cover.get();
ivCover.setImageDrawable(placeholder);
}
+
+ static void setPlaceholderVisibility(TextView placeholder, boolean textAndImageCombined) {
+ if (placeholder != null) {
+ if (textAndImageCombined) {
+ int bgColor = placeholder.getContext().getResources().getColor(R.color.feed_text_bg);
+ placeholder.setBackgroundColor(bgColor);
+ } else {
+ placeholder.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
}
} \ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
index 811e1e31b..740636c77 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -2,26 +2,32 @@ package de.danoeh.antennapod.adapter;
import android.app.Activity;
import android.text.format.DateUtils;
-import android.util.Log;
+import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
+import android.util.Log;
import android.widget.BaseAdapter;
-import android.widget.Toast;
+import android.widget.Toast;
import androidx.core.content.ContextCompat;
-
+import androidx.fragment.app.ListFragment;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.service.download.DownloadRequest;
+import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.ui.common.ThemeUtils;
-import de.danoeh.antennapod.view.viewholder.DownloadItemViewHolder;
+import de.danoeh.antennapod.view.viewholder.DownloadLogItemViewHolder;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* Displays a list of DownloadStatus entries.
@@ -30,37 +36,62 @@ public class DownloadLogAdapter extends BaseAdapter {
private static final String TAG = "DownloadLogAdapter";
private final Activity context;
- private final ItemAccess itemAccess;
+ private final ListFragment listFragment;
+ private List<DownloadStatus> downloadLog = new ArrayList<>();
+ private List<Downloader> runningDownloads = new ArrayList<>();
- public DownloadLogAdapter(Activity context, ItemAccess itemAccess) {
+ public DownloadLogAdapter(Activity context, ListFragment listFragment) {
super();
- this.itemAccess = itemAccess;
this.context = context;
+ this.listFragment = listFragment;
+ }
+
+ public void setDownloadLog(List<DownloadStatus> downloadLog) {
+ this.downloadLog = downloadLog;
+ notifyDataSetChanged();
+ }
+
+ public void setRunningDownloads(List<Downloader> runningDownloads) {
+ this.runningDownloads = runningDownloads;
+ notifyDataSetChanged();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- DownloadItemViewHolder holder;
+ DownloadLogItemViewHolder holder;
if (convertView == null) {
- holder = new DownloadItemViewHolder(context, parent);
+ holder = new DownloadLogItemViewHolder(context, parent);
+ holder.itemView.setTag(holder);
} else {
- holder = (DownloadItemViewHolder) convertView.getTag();
+ holder = (DownloadLogItemViewHolder) convertView.getTag();
}
- DownloadStatus status = getItem(position);
+ Object item = getItem(position);
+ if (item instanceof DownloadStatus) {
+ bind(holder, (DownloadStatus) item, position);
+ } else if (item instanceof Downloader) {
+ bind(holder, (Downloader) item, position);
+ }
+ return holder.itemView;
+ }
+
+ private void bind(DownloadLogItemViewHolder holder, DownloadStatus status, int position) {
+ String statusText = "";
if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
- holder.type.setText(R.string.download_type_feed);
+ statusText += context.getString(R.string.download_type_feed);
} else if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- holder.type.setText(R.string.download_type_media);
+ statusText += context.getString(R.string.download_type_media);
}
+ statusText += " · ";
+ statusText += DateUtils.getRelativeTimeSpanString(status.getCompletionDate().getTime(),
+ System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, 0);
+ holder.status.setText(statusText);
if (status.getTitle() != null) {
holder.title.setText(status.getTitle());
} else {
holder.title.setText(R.string.download_log_title_unknown);
}
- holder.date.setText(DateUtils.getRelativeTimeSpanString(status.getCompletionDate().getTime(),
- System.currentTimeMillis(), 0, 0));
if (status.isSuccessful()) {
holder.icon.setTextColor(ContextCompat.getColor(context, R.color.download_success_green));
@@ -77,13 +108,13 @@ public class DownloadLogAdapter extends BaseAdapter {
holder.reason.setVisibility(View.VISIBLE);
holder.tapForDetails.setVisibility(View.VISIBLE);
- if (newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
+ if (newerWasSuccessful(position - runningDownloads.size(),
+ status.getFeedfileType(), status.getFeedfileId())) {
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
holder.secondaryActionButton.setOnClickListener(null);
holder.secondaryActionButton.setTag(null);
} else {
- holder.secondaryActionIcon.setImageResource(
- ThemeUtils.getDrawableFromAttr(context, R.attr.navigation_refresh));
+ holder.secondaryActionIcon.setImageResource(R.drawable.ic_refresh);
holder.secondaryActionButton.setVisibility(View.VISIBLE);
if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
@@ -120,13 +151,51 @@ public class DownloadLogAdapter extends BaseAdapter {
}
}
}
+ }
- return holder.itemView;
+ private void bind(DownloadLogItemViewHolder holder, Downloader downloader, int position) {
+ DownloadRequest request = downloader.getDownloadRequest();
+ holder.title.setText(request.getTitle());
+ holder.secondaryActionIcon.setImageResource(R.drawable.ic_cancel);
+ holder.secondaryActionButton.setContentDescription(context.getString(R.string.cancel_download_label));
+ holder.secondaryActionButton.setVisibility(View.VISIBLE);
+ holder.secondaryActionButton.setTag(downloader);
+ holder.secondaryActionButton.setOnClickListener(v ->
+ listFragment.onListItemClick(null, holder.itemView, position, 0));
+ holder.reason.setVisibility(View.GONE);
+ holder.tapForDetails.setVisibility(View.GONE);
+ holder.icon.setTextColor(ThemeUtils.getColorFromAttr(context, R.attr.colorPrimary));
+ holder.icon.setText("{fa-arrow-circle-down}");
+ holder.icon.setContentDescription(context.getString(R.string.status_downloading_label));
+
+ boolean percentageWasSet = false;
+ String status = "";
+ if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
+ status += context.getString(R.string.download_type_feed);
+ } else if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ status += context.getString(R.string.download_type_media);
+ }
+ status += " · ";
+ if (request.getSoFar() <= 0) {
+ status += context.getString(R.string.download_pending);
+ } else {
+ status += Formatter.formatShortFileSize(context, request.getSoFar());
+ if (request.getSize() != DownloadStatus.SIZE_UNKNOWN) {
+ status += " / " + Formatter.formatShortFileSize(context, request.getSize());
+ holder.secondaryActionProgress.setPercentage(
+ 0.01f * Math.max(1, request.getProgressPercent()), request);
+ percentageWasSet = true;
+ }
+ }
+ if (!percentageWasSet) {
+ holder.secondaryActionProgress.setPercentage(0, request);
+ }
+ holder.status.setText(status);
}
- private boolean newerWasSuccessful(int position, int feedTypeId, long id) {
- for (int i = 0; i < position; i++) {
- DownloadStatus status = getItem(i);
+ private boolean newerWasSuccessful(int downloadStatusIndex, int feedTypeId, long id) {
+ for (int i = 0; i < downloadStatusIndex; i++) {
+ DownloadStatus status = downloadLog.get(i);
if (status.getFeedfileType() == feedTypeId && status.getFeedfileId() == id && status.isSuccessful()) {
return true;
}
@@ -136,12 +205,17 @@ public class DownloadLogAdapter extends BaseAdapter {
@Override
public int getCount() {
- return itemAccess.getCount();
+ return downloadLog.size() + runningDownloads.size();
}
@Override
- public DownloadStatus getItem(int position) {
- return itemAccess.getItem(position);
+ public Object getItem(int position) {
+ if (position < runningDownloads.size()) {
+ return runningDownloads.get(position);
+ } else if (position - runningDownloads.size() < downloadLog.size()) {
+ return downloadLog.get(position - runningDownloads.size());
+ }
+ return null;
}
@Override
@@ -149,10 +223,4 @@ public class DownloadLogAdapter extends BaseAdapter {
return position;
}
- public interface ItemAccess {
- int getCount();
-
- DownloadStatus getItem(int position);
- }
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
deleted file mode 100644
index 9363edc9f..000000000
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package de.danoeh.antennapod.adapter;
-
-import android.content.Context;
-import android.text.format.Formatter;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.service.download.DownloadRequest;
-import de.danoeh.antennapod.core.service.download.DownloadStatus;
-import de.danoeh.antennapod.core.service.download.Downloader;
-import de.danoeh.antennapod.ui.common.ThemeUtils;
-import de.danoeh.antennapod.ui.common.CircularProgressBar;
-
-public class DownloadlistAdapter extends BaseAdapter {
-
- private final ItemAccess itemAccess;
- private final Context context;
-
- public DownloadlistAdapter(Context context, ItemAccess itemAccess) {
- super();
- this.context = context;
- this.itemAccess = itemAccess;
- }
-
- @Override
- public int getCount() {
- return itemAccess.getCount();
- }
-
- @Override
- public Downloader getItem(int position) {
- return itemAccess.getItem(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Holder holder;
- Downloader downloader = getItem(position);
- DownloadRequest request = downloader.getDownloadRequest();
- if (convertView == null) {
- holder = new Holder();
- LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.downloadlist_item, parent, false);
- holder.title = convertView.findViewById(R.id.txtvTitle);
- holder.status = convertView.findViewById(R.id.txtvStatus);
- holder.secondaryActionButton = convertView.findViewById(R.id.secondaryActionButton);
- holder.secondaryActionIcon = convertView.findViewById(R.id.secondaryActionIcon);
- holder.secondaryActionProgress = convertView.findViewById(R.id.secondaryActionProgress);
- convertView.setTag(holder);
- } else {
- holder = (Holder) convertView.getTag();
- }
-
- holder.title.setText(request.getTitle());
- holder.secondaryActionIcon.setImageResource(ThemeUtils.getDrawableFromAttr(context, R.attr.navigation_cancel));
- holder.secondaryActionButton.setContentDescription(context.getString(R.string.cancel_download_label));
- holder.secondaryActionButton.setTag(downloader);
- holder.secondaryActionButton.setOnClickListener(butSecondaryListener);
-
- boolean percentageWasSet = false;
- String status = "";
- if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
- status += context.getString(R.string.download_type_feed);
- } else if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- status += context.getString(R.string.download_type_media);
- }
- status += " · ";
- if (request.getSoFar() <= 0) {
- status += context.getString(R.string.download_pending);
- } else {
- status += Formatter.formatShortFileSize(context, request.getSoFar());
- if (request.getSize() != DownloadStatus.SIZE_UNKNOWN) {
- status += " / " + Formatter.formatShortFileSize(context, request.getSize());
- holder.secondaryActionProgress.setPercentage(
- 0.01f * Math.max(1, request.getProgressPercent()), request);
- percentageWasSet = true;
- }
- }
- if (!percentageWasSet) {
- holder.secondaryActionProgress.setPercentage(0, request);
- }
- holder.status.setText(status);
-
- return convertView;
- }
-
- private final View.OnClickListener butSecondaryListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Downloader downloader = (Downloader) v.getTag();
- itemAccess.onSecondaryActionClick(downloader);
- }
- };
-
- static class Holder {
- TextView title;
- TextView status;
- View secondaryActionButton;
- ImageView secondaryActionIcon;
- CircularProgressBar secondaryActionProgress;
- }
-
- public interface ItemAccess {
- int getCount();
-
- Downloader getItem(int position);
-
- void onSecondaryActionClick(Downloader downloader);
- }
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java
index 4762622d1..774b97454 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java
@@ -10,7 +10,7 @@ import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.fragment.ItemPagerFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
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 8cb0fd30a..62a97e849 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
@@ -12,14 +12,15 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.util.NetworkUtils;
-import de.danoeh.antennapod.core.util.playback.RemoteMedia;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.playback.RemoteMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.DateUtils;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
+import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.dialog.StreamingConfirmationDialog;
import java.util.List;
@@ -59,7 +60,7 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
holder.title.setText(item.getTitle());
holder.pubDate.setText(DateUtils.formatAbbrev(getContext(), item.getPubDate()));
if (item.getDescription() != null) {
- String description = item.getDescription()
+ String description = HtmlToPlainText.getPlainText(item.getDescription())
.replaceAll("\n", " ")
.replaceAll("\\s+", " ")
.trim();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedSearchResultAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedSearchResultAdapter.java
index dbb9ce0d0..92865e211 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedSearchResultAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedSearchResultAdapter.java
@@ -8,7 +8,7 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
import de.danoeh.antennapod.ui.common.SquareImageView;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
index 7c8943f36..ff0311ab6 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -1,28 +1,31 @@
package de.danoeh.antennapod.adapter;
import android.app.Activity;
-import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import android.util.TypedValue;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
+import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconTextView;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.NavDrawerData;
import de.danoeh.antennapod.fragment.AddFeedFragment;
import de.danoeh.antennapod.fragment.DownloadsFragment;
import de.danoeh.antennapod.fragment.EpisodesFragment;
@@ -42,10 +45,9 @@ import java.util.List;
/**
* BaseAdapter for the navigation drawer
*/
-public class NavListAdapter extends BaseAdapter
+public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
implements SharedPreferences.OnSharedPreferenceChangeListener {
- private static final int VIEW_TYPE_COUNT = 3;
public static final int VIEW_TYPE_NAV = 0;
public static final int VIEW_TYPE_SECTION_DIVIDER = 1;
private static final int VIEW_TYPE_SUBSCRIPTION = 2;
@@ -56,9 +58,8 @@ public class NavListAdapter extends BaseAdapter
*/
public static final String SUBSCRIPTION_LIST_TAG = "SubscriptionList";
- private static List<String> tags;
- private static String[] titles;
-
+ private final List<String> fragmentTags = new ArrayList<>();
+ private final String[] titles;
private final ItemAccess itemAccess;
private final WeakReference<Activity> activity;
public boolean showSubscriptionList = true;
@@ -96,7 +97,8 @@ public class NavListAdapter extends BaseAdapter
showSubscriptionList = false;
}
- tags = newTags;
+ fragmentTags.clear();
+ fragmentTags.addAll(newTags);
notifyDataSetChanged();
}
@@ -105,47 +107,31 @@ public class NavListAdapter extends BaseAdapter
return titles[index];
}
- private Drawable getDrawable(String tag) {
- Activity context = activity.get();
- if (context == null) {
- return null;
- }
- int icon;
+ private @DrawableRes int getDrawable(String tag) {
switch (tag) {
case QueueFragment.TAG:
- icon = R.attr.stat_playlist;
- break;
+ return R.drawable.ic_playlist;
case EpisodesFragment.TAG:
- icon = R.attr.feed;
- break;
+ return R.drawable.ic_feed;
case DownloadsFragment.TAG:
- icon = R.attr.av_download;
- break;
+ return R.drawable.ic_download;
case PlaybackHistoryFragment.TAG:
- icon = R.attr.ic_history;
- break;
+ return R.drawable.ic_history;
case SubscriptionFragment.TAG:
- icon = R.attr.ic_folder;
- break;
+ return R.drawable.ic_folder;
case AddFeedFragment.TAG:
- icon = R.attr.content_new;
- break;
+ return R.drawable.ic_add;
default:
- return null;
+ return 0;
}
- TypedArray ta = context.obtainStyledAttributes(new int[] { icon } );
- Drawable result = ta.getDrawable(0);
- ta.recycle();
- return result;
}
- public List<String> getTags() {
- return Collections.unmodifiableList(tags);
+ public List<String> getFragmentTags() {
+ return Collections.unmodifiableList(fragmentTags);
}
-
@Override
- public int getCount() {
+ public int getItemCount() {
int baseCount = getSubscriptionOffset();
if (showSubscriptionList) {
baseCount += itemAccess.getCount();
@@ -154,25 +140,20 @@ public class NavListAdapter extends BaseAdapter
}
@Override
- public Object getItem(int position) {
+ public long getItemId(int position) {
int viewType = getItemViewType(position);
- if (viewType == VIEW_TYPE_NAV) {
- return getLabel(tags.get(position));
- } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) {
- return "";
+ if (viewType == VIEW_TYPE_SUBSCRIPTION) {
+ return itemAccess.getItem(position - getSubscriptionOffset()).id;
+ } else if (viewType == VIEW_TYPE_NAV) {
+ return -Math.abs((long) fragmentTags.get(position).hashCode()) - 1; // Folder IDs are >0
} else {
- return itemAccess.getItem(position);
+ return 0;
}
}
@Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
public int getItemViewType(int position) {
- if (0 <= position && position < tags.size()) {
+ if (0 <= position && position < fragmentTags.size()) {
return VIEW_TYPE_NAV;
} else if (position < getSubscriptionOffset()) {
return VIEW_TYPE_SECTION_DIVIDER;
@@ -181,69 +162,67 @@ public class NavListAdapter extends BaseAdapter
}
}
- @Override
- public int getViewTypeCount() {
- return VIEW_TYPE_COUNT;
- }
-
public int getSubscriptionOffset() {
- return tags.size() > 0 ? tags.size() + 1 : 0;
+ return fragmentTags.size() > 0 ? fragmentTags.size() + 1 : 0;
}
+ @NonNull
+ @Override
+ public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(activity.get());
+ if (viewType == VIEW_TYPE_NAV) {
+ return new NavHolder(inflater.inflate(R.layout.nav_listitem, parent, false));
+ } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) {
+ return new DividerHolder(inflater.inflate(R.layout.nav_section_item, parent, false));
+ } else {
+ return new FeedHolder(inflater.inflate(R.layout.nav_listitem, parent, false));
+ }
+ }
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
+ public void onBindViewHolder(@NonNull Holder holder, int position) {
int viewType = getItemViewType(position);
- View v;
+
+ holder.itemView.setOnCreateContextMenuListener(null);
if (viewType == VIEW_TYPE_NAV) {
- v = getNavView((String) getItem(position), position, convertView, parent);
+ bindNavView(getLabel(fragmentTags.get(position)), position, (NavHolder) holder);
} else if (viewType == VIEW_TYPE_SECTION_DIVIDER) {
- v = getSectionDividerView(convertView, parent);
+ bindSectionDivider((DividerHolder) holder);
} else {
- v = getFeedView(position, convertView, parent);
+ int itemPos = position - getSubscriptionOffset();
+ NavDrawerData.DrawerItem item = itemAccess.getItem(itemPos);
+ bindListItem(item, (FeedHolder) holder);
+ if (item.type == NavDrawerData.DrawerItem.Type.FEED) {
+ bindFeedView((NavDrawerData.FeedDrawerItem) item, (FeedHolder) holder);
+ holder.itemView.setOnCreateContextMenuListener(itemAccess);
+ } else {
+ bindFolderView((NavDrawerData.FolderDrawerItem) item, (FeedHolder) holder);
+ }
}
- if (v != null && viewType != VIEW_TYPE_SECTION_DIVIDER) {
+ if (viewType != VIEW_TYPE_SECTION_DIVIDER) {
TypedValue typedValue = new TypedValue();
- if (position == itemAccess.getSelectedItemIndex()) {
- v.getContext().getTheme().resolveAttribute(R.attr.drawer_activated_color, typedValue, true);
- v.setBackgroundResource(typedValue.resourceId);
- } else {
- v.getContext().getTheme().resolveAttribute(android.R.attr.windowBackground, typedValue, true);
- v.setBackgroundResource(typedValue.resourceId);
- }
+ activity.get().getTheme().resolveAttribute(itemAccess.isSelected(position)
+ ? R.attr.drawer_activated_color : android.R.attr.windowBackground, typedValue, true);
+ holder.itemView.setBackgroundResource(typedValue.resourceId);
+
+ holder.itemView.setOnClickListener(v -> itemAccess.onItemClick(position));
+ holder.itemView.setOnLongClickListener(v -> itemAccess.onItemLongClick(position));
}
- return v;
}
- private View getNavView(String title, int position, View convertView, ViewGroup parent) {
+ private void bindNavView(String title, int position, NavHolder holder) {
Activity context = activity.get();
- if(context == null) {
- return null;
- }
- NavHolder holder;
- if (convertView == null) {
- holder = new NavHolder();
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- convertView = inflater.inflate(R.layout.nav_listitem, parent, false);
-
- holder.image = convertView.findViewById(R.id.imgvCover);
- holder.title = convertView.findViewById(R.id.txtvTitle);
- holder.count = convertView.findViewById(R.id.txtvCount);
- convertView.setTag(holder);
- } else {
- holder = (NavHolder) convertView.getTag();
+ if (context == null) {
+ return;
}
-
holder.title.setText(title);
// reset for re-use
holder.count.setVisibility(View.GONE);
holder.count.setOnClickListener(null);
- String tag = tags.get(position);
+ String tag = fragmentTags.get(position);
if (tag.equals(QueueFragment.TAG)) {
int queueSize = itemAccess.getQueueSize();
if (queueSize > 0) {
@@ -262,78 +241,69 @@ public class NavListAdapter extends BaseAdapter
holder.count.setText(NumberFormat.getInstance().format(sum));
holder.count.setVisibility(View.VISIBLE);
}
- } else if(tag.equals(DownloadsFragment.TAG) && UserPreferences.isEnableAutodownload()) {
+ } else if (tag.equals(DownloadsFragment.TAG) && UserPreferences.isEnableAutodownload()) {
int epCacheSize = UserPreferences.getEpisodeCacheSize();
// don't count episodes that can be reclaimed
- int spaceUsed = itemAccess.getNumberOfDownloadedItems() -
- itemAccess.getReclaimableItems();
+ int spaceUsed = itemAccess.getNumberOfDownloadedItems()
+ - itemAccess.getReclaimableItems();
if (epCacheSize > 0 && spaceUsed >= epCacheSize) {
holder.count.setText("{md-disc-full 150%}");
Iconify.addIcons(holder.count);
holder.count.setVisibility(View.VISIBLE);
holder.count.setOnClickListener(v ->
- new AlertDialog.Builder(context)
+ new AlertDialog.Builder(context)
.setTitle(R.string.episode_cache_full_title)
.setMessage(R.string.episode_cache_full_message)
- .setPositiveButton(android.R.string.ok, (dialog, which) -> {})
+ .setPositiveButton(android.R.string.ok, null)
+ .setNeutralButton(R.string.open_autodownload_settings, (dialog, which) -> {
+ Intent intent = new Intent(context, PreferenceActivity.class);
+ intent.putExtra(PreferenceActivity.OPEN_AUTO_DOWNLOAD_SETTINGS, true);
+ context.startActivity(intent);
+ })
.show()
);
}
}
- holder.image.setImageDrawable(getDrawable(tags.get(position)));
-
- return convertView;
+ holder.image.setImageResource(getDrawable(fragmentTags.get(position)));
}
- private View getSectionDividerView(View convertView, ViewGroup parent) {
+ private void bindSectionDivider(DividerHolder holder) {
Activity context = activity.get();
- if(context == null) {
- return null;
+ if (context == null) {
+ return;
}
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- convertView = inflater.inflate(R.layout.nav_section_item, parent, false);
- TextView feedsFilteredMsg = convertView.findViewById(R.id.nav_feeds_filtered_message);
if (UserPreferences.getSubscriptionsFilter().isEnabled() && showSubscriptionList) {
- convertView.setEnabled(true);
- feedsFilteredMsg.setText("{md-info-outline} " + context.getString(R.string.subscriptions_are_filtered));
- Iconify.addIcons(feedsFilteredMsg);
- feedsFilteredMsg.setVisibility(View.VISIBLE);
+ holder.itemView.setEnabled(true);
+ holder.feedsFilteredMsg.setText("{md-info-outline} "
+ + context.getString(R.string.subscriptions_are_filtered));
+ Iconify.addIcons(holder.feedsFilteredMsg);
+ holder.feedsFilteredMsg.setVisibility(View.VISIBLE);
} else {
- convertView.setEnabled(false);
- feedsFilteredMsg.setVisibility(View.GONE);
+ holder.itemView.setEnabled(false);
+ holder.feedsFilteredMsg.setVisibility(View.GONE);
}
+ }
- return convertView;
+ private void bindListItem(NavDrawerData.DrawerItem item, FeedHolder holder) {
+ if (item.getCounter() > 0) {
+ holder.count.setVisibility(View.VISIBLE);
+ holder.count.setText(NumberFormat.getInstance().format(item.getCounter()));
+ } else {
+ holder.count.setVisibility(View.GONE);
+ }
+ holder.title.setText(item.getTitle());
+ int padding = (int) (activity.get().getResources().getDimension(R.dimen.thumbnail_length_navlist) / 2);
+ holder.itemView.setPadding(item.getLayer() * padding, 0, 0, 0);
}
- private View getFeedView(int position, View convertView, ViewGroup parent) {
+ private void bindFeedView(NavDrawerData.FeedDrawerItem drawerItem, FeedHolder holder) {
+ Feed feed = drawerItem.feed;
Activity context = activity.get();
- if(context == null) {
- return null;
- }
- int feedPos = position - getSubscriptionOffset();
- Feed feed = itemAccess.getItem(feedPos);
-
- FeedHolder holder;
- if (convertView == null) {
- holder = new FeedHolder();
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- convertView = inflater.inflate(R.layout.nav_listitem, parent, false);
-
- holder.image = convertView.findViewById(R.id.imgvCover);
- holder.title = convertView.findViewById(R.id.txtvTitle);
- holder.failure = convertView.findViewById(R.id.itxtvFailure);
- holder.count = convertView.findViewById(R.id.txtvCount);
- convertView.setTag(holder);
- } else {
- holder = (FeedHolder) convertView.getTag();
+ if (context == null) {
+ return;
}
Glide.with(context)
@@ -346,9 +316,7 @@ public class NavListAdapter extends BaseAdapter
.dontAnimate())
.into(holder.image);
- holder.title.setText(feed.getTitle());
-
- if(feed.hasLastUpdateFailed()) {
+ if (feed.hasLastUpdateFailed()) {
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) holder.title.getLayoutParams();
p.addRule(RelativeLayout.LEFT_OF, R.id.itxtvFailure);
holder.failure.setVisibility(View.VISIBLE);
@@ -357,39 +325,87 @@ public class NavListAdapter extends BaseAdapter
p.addRule(RelativeLayout.LEFT_OF, R.id.txtvCount);
holder.failure.setVisibility(View.GONE);
}
- int counter = itemAccess.getFeedCounter(feed.getId());
- if(counter > 0) {
- holder.count.setVisibility(View.VISIBLE);
- holder.count.setText(NumberFormat.getInstance().format(counter));
- } else {
+ }
+
+ private void bindFolderView(NavDrawerData.FolderDrawerItem folder, FeedHolder holder) {
+ Activity context = activity.get();
+ if (context == null) {
+ return;
+ }
+ if (folder.isOpen) {
holder.count.setVisibility(View.GONE);
}
- return convertView;
+ Glide.with(context).clear(holder.image);
+ holder.image.setImageResource(R.drawable.ic_folder);
+ holder.failure.setVisibility(View.GONE);
+ }
+
+ static class Holder extends RecyclerView.ViewHolder {
+ public Holder(@NonNull View itemView) {
+ super(itemView);
+ }
}
- static class NavHolder {
- ImageView image;
- TextView title;
- TextView count;
+ static class DividerHolder extends Holder {
+ final TextView feedsFilteredMsg;
+
+ public DividerHolder(@NonNull View itemView) {
+ super(itemView);
+ feedsFilteredMsg = itemView.findViewById(R.id.nav_feeds_filtered_message);
+ }
}
- static class FeedHolder {
- ImageView image;
- TextView title;
- IconTextView failure;
- TextView count;
+ static class NavHolder extends Holder {
+ final ImageView image;
+ final TextView title;
+ final TextView count;
+
+ public NavHolder(@NonNull View itemView) {
+ super(itemView);
+ image = itemView.findViewById(R.id.imgvCover);
+ title = itemView.findViewById(R.id.txtvTitle);
+ count = itemView.findViewById(R.id.txtvCount);
+ }
}
- public interface ItemAccess {
+ static class FeedHolder extends Holder {
+ final ImageView image;
+ final TextView title;
+ final IconTextView failure;
+ final TextView count;
+
+ public FeedHolder(@NonNull View itemView) {
+ super(itemView);
+ image = itemView.findViewById(R.id.imgvCover);
+ title = itemView.findViewById(R.id.txtvTitle);
+ failure = itemView.findViewById(R.id.itxtvFailure);
+ count = itemView.findViewById(R.id.txtvCount);
+ }
+ }
+
+ public interface ItemAccess extends View.OnCreateContextMenuListener {
int getCount();
- Feed getItem(int position);
- int getSelectedItemIndex();
+
+ NavDrawerData.DrawerItem getItem(int position);
+
+ boolean isSelected(int position);
+
int getQueueSize();
+
int getNumberOfNewItems();
+
int getNumberOfDownloadedItems();
+
int getReclaimableItems();
- int getFeedCounter(long feedId);
+
int getFeedCounterSum();
+
+ void onItemClick(int position);
+
+ boolean onItemLongClick(int position);
+
+ @Override
+ void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
index f7d6358de..0af6960d7 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
@@ -19,19 +19,17 @@ import java.util.Locale;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
+import de.danoeh.antennapod.core.storage.NavDrawerData;
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
+import de.danoeh.antennapod.fragment.SubscriptionFragment;
import jp.shts.android.library.TriangleLabelView;
/**
* Adapter for subscriptions
*/
public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
-
- /** placeholder object that indicates item should be added */
- public static final Object ADD_ITEM_OBJ = new Object();
-
/** the position in the view that holds the add item; 0 is the first, -1 is the last position */
private static final String TAG = "SubscriptionsAdapter";
@@ -60,7 +58,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
@Override
public long getItemId(int position) {
- return itemAccess.getItem(position).getId();
+ return ((NavDrawerData.DrawerItem) getItem(position)).id;
}
@Override
@@ -83,11 +81,13 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
holder = (Holder) convertView.getTag();
}
- final Feed feed = (Feed) getItem(position);
- if (feed == null) return null;
+ final NavDrawerData.DrawerItem drawerItem = (NavDrawerData.DrawerItem) getItem(position);
+ if (drawerItem == null) {
+ return null;
+ }
- holder.feedTitle.setText(feed.getTitle());
- holder.imageView.setContentDescription(feed.getTitle());
+ holder.feedTitle.setText(drawerItem.getTitle());
+ holder.imageView.setContentDescription(drawerItem.getTitle());
holder.feedTitle.setVisibility(View.VISIBLE);
// Fix TriangleLabelView corner for RTL
@@ -96,30 +96,46 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
holder.count.setCorner(TriangleLabelView.Corner.TOP_LEFT);
}
- int count = itemAccess.getFeedCounter(feed.getId());
- if(count > 0) {
- holder.count.setPrimaryText(
- NumberFormat.getInstance().format(itemAccess.getFeedCounter(feed.getId())));
+ if (drawerItem.getCounter() > 0) {
+ holder.count.setPrimaryText(NumberFormat.getInstance().format(drawerItem.getCounter()));
holder.count.setVisibility(View.VISIBLE);
} else {
holder.count.setVisibility(View.GONE);
}
- boolean textAndImageCombined = feed.isLocalFeed()
- && LocalFeedUpdater.getDefaultIconUrl(convertView.getContext()).equals(feed.getImageUrl());
- new CoverLoader(mainActivityRef.get())
- .withUri(feed.getImageUrl())
- .withPlaceholderView(holder.feedTitle, textAndImageCombined)
- .withCoverView(holder.imageView)
- .load();
-
+ if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) {
+ Feed feed = ((NavDrawerData.FeedDrawerItem) drawerItem).feed;
+ boolean textAndImageCombined = feed.isLocalFeed()
+ && LocalFeedUpdater.getDefaultIconUrl(convertView.getContext()).equals(feed.getImageUrl());
+ new CoverLoader(mainActivityRef.get())
+ .withUri(feed.getImageUrl())
+ .withPlaceholderView(holder.feedTitle, textAndImageCombined)
+ .withCoverView(holder.imageView)
+ .load();
+ } else {
+ new CoverLoader(mainActivityRef.get())
+ .withResource(R.drawable.ic_folder)
+ .withPlaceholderView(holder.feedTitle, true)
+ .withCoverView(holder.imageView)
+ .load();
+ }
return convertView;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Fragment fragment = FeedItemlistFragment.newInstance(getItemId(position));
- mainActivityRef.get().loadChildFragment(fragment);
+ final NavDrawerData.DrawerItem drawerItem = (NavDrawerData.DrawerItem) getItem(position);
+ if (drawerItem == null) {
+ return;
+ }
+ if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) {
+ Feed feed = ((NavDrawerData.FeedDrawerItem) drawerItem).feed;
+ Fragment fragment = FeedItemlistFragment.newInstance(feed.getId());
+ mainActivityRef.get().loadChildFragment(fragment);
+ } else if (drawerItem.type == NavDrawerData.DrawerItem.Type.FOLDER) {
+ Fragment fragment = SubscriptionFragment.newInstance(drawerItem.getTitle());
+ mainActivityRef.get().loadChildFragment(fragment);
+ }
}
static class Holder {
@@ -130,7 +146,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
public interface ItemAccess {
int getCount();
- Feed getItem(int position);
- int getFeedCounter(long feedId);
+
+ NavDrawerData.DrawerItem getItem(int position);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java
index a8001eeb1..b362a5a1d 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java
@@ -1,11 +1,11 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
class AddToQueueActionButton extends ItemActionButton {
@@ -20,9 +20,9 @@ class AddToQueueActionButton extends ItemActionButton {
}
@Override
- @AttrRes
+ @DrawableRes
public int getDrawable() {
- return R.attr.content_new;
+ return R.drawable.ic_add;
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java
index a31d2fdc0..afa86c9d7 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java
@@ -1,13 +1,13 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import android.widget.Toast;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -25,9 +25,9 @@ public class CancelDownloadActionButton extends ItemActionButton {
}
@Override
- @AttrRes
+ @DrawableRes
public int getDrawable() {
- return R.attr.navigation_cancel;
+ return R.drawable.ic_cancel;
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DeleteActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DeleteActionButton.java
index 45cce23b8..096d060c1 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DeleteActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DeleteActionButton.java
@@ -2,11 +2,11 @@ package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
import android.view.View;
-import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DBWriter;
public class DeleteActionButton extends ItemActionButton {
@@ -22,9 +22,9 @@ public class DeleteActionButton extends ItemActionButton {
}
@Override
- @AttrRes
+ @DrawableRes
public int getDrawable() {
- return R.attr.ic_delete;
+ return R.drawable.ic_delete;
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
index 0f7c2bdd0..c3e979dd8 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
@@ -4,14 +4,14 @@ import android.content.Context;
import android.view.View;
import android.widget.Toast;
-import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UsageStatistics;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
@@ -33,9 +33,9 @@ public class DownloadActionButton extends ItemActionButton {
}
@Override
- @AttrRes
+ @DrawableRes
public int getDrawable() {
- return R.attr.av_download;
+ return R.drawable.ic_download;
}
@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 5d95d3775..12150293f 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
@@ -1,17 +1,17 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import android.content.res.TypedArray;
import android.widget.ImageView;
-import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import android.view.View;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
public abstract class ItemActionButton {
FeedItem item;
@@ -23,7 +23,7 @@ public abstract class ItemActionButton {
@StringRes
public abstract int getLabel();
- @AttrRes
+ @DrawableRes
public abstract int getDrawable();
public abstract void onClick(Context context);
@@ -40,7 +40,7 @@ public abstract class ItemActionButton {
}
final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
- if (media.isCurrentlyPlaying()) {
+ if (FeedItemUtil.isCurrentlyPlaying(media)) {
return new PauseActionButton(item);
} else if (item.getFeed().isLocalFeed()) {
return new PlayLocalActionButton(item);
@@ -62,9 +62,6 @@ public abstract class ItemActionButton {
button.setVisibility(getVisibility());
button.setContentDescription(context.getString(getLabel()));
button.setOnClickListener((view) -> onClick(context));
-
- TypedArray drawables = context.obtainStyledAttributes(new int[]{getDrawable()});
- icon.setImageDrawable(drawables.getDrawable(0));
- drawables.recycle();
+ icon.setImageResource(getDrawable());
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java
index 14fa94f7a..8dc4ffe33 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java
@@ -1,12 +1,12 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import android.view.View;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBWriter;
public class MarkAsPlayedActionButton extends ItemActionButton {
@@ -22,9 +22,9 @@ public class MarkAsPlayedActionButton extends ItemActionButton {
}
@Override
- @AttrRes
+ @DrawableRes
public int getDrawable() {
- return R.attr.navigation_accept;
+ return R.drawable.ic_check;
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
index 49b785056..66ce973e2 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
@@ -5,7 +5,7 @@ import android.content.Context;
import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PauseActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PauseActionButton.java
index 4ac03c50e..de4dae6a0 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PauseActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PauseActionButton.java
@@ -1,11 +1,12 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE;
@@ -23,9 +24,9 @@ public class PauseActionButton extends ItemActionButton {
}
@Override
- @AttrRes
+ @DrawableRes
public int getDrawable() {
- return R.attr.av_pause;
+ return R.drawable.ic_pause;
}
@Override
@@ -35,7 +36,7 @@ public class PauseActionButton extends ItemActionButton {
return;
}
- if (media.isCurrentlyPlaying()) {
+ if (FeedItemUtil.isCurrentlyPlaying(media)) {
IntentUtils.sendLocalBroadcast(context, ACTION_PAUSE_PLAY_CURRENT_EPISODE);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
index 512f1a512..974b12bab 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
@@ -1,12 +1,12 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
@@ -24,9 +24,9 @@ public class PlayActionButton extends ItemActionButton {
}
@Override
- @AttrRes
+ @DrawableRes
public int getDrawable() {
- return R.attr.av_play;
+ return R.drawable.ic_play_24dp;
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayLocalActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayLocalActionButton.java
index 78ea3b93f..ab2122b12 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayLocalActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayLocalActionButton.java
@@ -1,12 +1,12 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
@@ -23,9 +23,9 @@ public class PlayLocalActionButton extends ItemActionButton {
}
@Override
- @AttrRes
+ @DrawableRes
public int getDrawable() {
- return R.attr.av_play;
+ return R.drawable.ic_play_24dp;
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java
index 8a892a621..94c446f50 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java
@@ -2,13 +2,13 @@ package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.preferences.UsageStatistics;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.NetworkUtils;
@@ -28,9 +28,9 @@ public class StreamActionButton extends ItemActionButton {
}
@Override
- @AttrRes
+ @DrawableRes
public int getDrawable() {
- return R.attr.action_stream;
+ return R.drawable.ic_stream;
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java
index e45280eed..03ccce2fe 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java
@@ -2,10 +2,10 @@ package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
import android.view.View;
-import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.util.IntentUtils;
public class VisitWebsiteActionButton extends ItemActionButton {
@@ -21,9 +21,9 @@ public class VisitWebsiteActionButton extends ItemActionButton {
}
@Override
- @AttrRes
+ @DrawableRes
public int getDrawable() {
- return R.attr.location_web_site;
+ return R.drawable.ic_web;
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
index 25fc0a05c..a125515cc 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
@@ -13,7 +13,7 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetPodcast;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetPodcast;
import org.apache.commons.lang3.StringUtils;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java
index 698e43145..b0441688d 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java
@@ -8,7 +8,7 @@ import android.widget.ArrayAdapter;
import android.widget.TextView;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetTag;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetTag;
import java.util.List;
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
index 906d50c61..73e19c746 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
@@ -5,7 +5,6 @@ import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.documentfile.provider.DocumentFile;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
@@ -37,9 +36,6 @@ public class DocumentFileExportWorker {
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();
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
index 0930b59eb..af9f041af 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
@@ -41,8 +41,8 @@ public class ExportWorker {
public Observable<File> exportObservable() {
if (output.exists()) {
- Log.w(TAG, "Overwriting previously exported file.");
- output.delete();
+ boolean success = output.delete();
+ Log.w(TAG, "Overwriting previously exported file: " + success);
}
return Observable.create(subscriber -> {
OutputStreamWriter writer = null;
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java
index ea5128102..a80e3d59b 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java
@@ -9,7 +9,7 @@ import java.util.Arrays;
import de.danoeh.antennapod.activity.OpmlImportHolder;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.export.opml.OpmlElement;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
diff --git a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
index f782308d1..938bb5931 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
@@ -21,7 +21,7 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks {
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, DownloadsFragment.TAG);
Bundle args = new Bundle();
- args.putInt(DownloadsFragment.ARG_SELECTED_TAB, DownloadsFragment.POS_RUNNING);
+ args.putInt(DownloadsFragment.ARG_SELECTED_TAB, DownloadsFragment.POS_LOG);
intent.putExtra(MainActivity.EXTRA_FRAGMENT_ARGS, args);
return PendingIntent.getActivity(context,
R.id.pending_intent_download_service_notification, intent, PendingIntent.FLAG_UPDATE_CURRENT);
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java
index eb33da037..ee19a0339 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java
@@ -8,7 +8,7 @@ import android.widget.RadioButton;
import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedFilter;
+import de.danoeh.antennapod.model.feed.FeedFilter;
/**
* Displays a dialog with a text box for filtering episodes and two radio buttons for exclusion/inclusion
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
index e1e8f1c2e..508ce74f4 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -21,14 +21,13 @@ import com.leinardi.android.speeddial.SpeedDialView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.LongList;
-import de.danoeh.antennapod.core.util.SortOrder;
-import de.danoeh.antennapod.ui.common.ThemeUtils;
+import de.danoeh.antennapod.model.feed.SortOrder;
import java.util.ArrayList;
import java.util.Arrays;
@@ -215,10 +214,10 @@ public class EpisodesApplyActionFragment extends Fragment implements Toolbar.OnM
public void refreshToolbarState() {
MenuItem selectAllItem = toolbar.getMenu().findItem(R.id.select_toggle);
if (checkedIds.size() == episodes.size()) {
- selectAllItem.setIcon(ThemeUtils.getDrawableFromAttr(getContext(), R.attr.ic_select_none));
+ selectAllItem.setIcon(R.drawable.ic_select_none);
selectAllItem.setTitle(R.string.deselect_all_label);
} else {
- selectAllItem.setIcon(ThemeUtils.getDrawableFromAttr(getContext(), R.attr.ic_select_all));
+ selectAllItem.setIcon(R.drawable.ic_select_all);
selectAllItem.setTitle(R.string.select_all_label);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/FeedRefreshIntervalDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/FeedRefreshIntervalDialog.java
new file mode 100644
index 000000000..7e42302d1
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/FeedRefreshIntervalDialog.java
@@ -0,0 +1,106 @@
+package de.danoeh.antennapod.dialog;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Build;
+import android.text.format.DateFormat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import androidx.appcompat.app.AlertDialog;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.databinding.FeedRefreshDialogBinding;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.concurrent.TimeUnit;
+
+public class FeedRefreshIntervalDialog {
+ private static final int[] INTERVAL_VALUES_HOURS = {1, 2, 4, 8, 12, 24, 72};
+ private final Context context;
+ private FeedRefreshDialogBinding viewBinding;
+
+ public FeedRefreshIntervalDialog(Context context) {
+ this.context = context;
+ }
+
+ public void show() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.feed_refresh_title);
+ builder.setMessage(R.string.feed_refresh_sum);
+ viewBinding = FeedRefreshDialogBinding.inflate(LayoutInflater.from(context));
+ builder.setView(viewBinding.getRoot());
+
+ ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(context,
+ android.R.layout.simple_spinner_item, buildSpinnerEntries());
+ spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ viewBinding.spinner.setAdapter(spinnerArrayAdapter);
+ viewBinding.timePicker.setIs24HourView(DateFormat.is24HourFormat(context));
+ viewBinding.spinner.setSelection(ArrayUtils.indexOf(INTERVAL_VALUES_HOURS, 24));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ viewBinding.timePicker.setHour(8);
+ viewBinding.timePicker.setMinute(0);
+ } else {
+ viewBinding.timePicker.setCurrentHour(8);
+ viewBinding.timePicker.setCurrentMinute(0);
+ }
+
+ long currInterval = UserPreferences.getUpdateInterval();
+ int[] updateTime = UserPreferences.getUpdateTimeOfDay();
+ if (currInterval > 0) {
+ viewBinding.spinner.setSelection(ArrayUtils.indexOf(INTERVAL_VALUES_HOURS,
+ (int) TimeUnit.MILLISECONDS.toHours(currInterval)));
+ viewBinding.intervalRadioButton.setChecked(true);
+ } else if (updateTime.length == 2 && updateTime[0] >= 0) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ viewBinding.timePicker.setHour(updateTime[0]);
+ viewBinding.timePicker.setMinute(updateTime[1]);
+ } else {
+ viewBinding.timePicker.setCurrentHour(updateTime[0]);
+ viewBinding.timePicker.setCurrentMinute(updateTime[1]);
+ }
+ viewBinding.timeRadioButton.setChecked(true);
+ } else {
+ viewBinding.disableRadioButton.setChecked(true);
+ }
+ updateVisibility();
+
+ viewBinding.radioGroup.setOnCheckedChangeListener((radioGroup, i) -> updateVisibility());
+
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ if (viewBinding.intervalRadioButton.isChecked()) {
+ UserPreferences.setUpdateInterval(INTERVAL_VALUES_HOURS[viewBinding.spinner.getSelectedItemPosition()]);
+ } else if (viewBinding.timeRadioButton.isChecked()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ UserPreferences.setUpdateTimeOfDay(viewBinding.timePicker.getHour(),
+ viewBinding.timePicker.getMinute());
+ } else {
+ UserPreferences.setUpdateTimeOfDay(viewBinding.timePicker.getCurrentHour(),
+ viewBinding.timePicker.getCurrentMinute());
+ }
+ } else if (viewBinding.disableRadioButton.isChecked()) {
+ UserPreferences.disableAutoUpdate(context);
+ } else {
+ throw new IllegalStateException("Unexpected error.");
+ }
+ });
+
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.show();
+ }
+
+ private String[] buildSpinnerEntries() {
+ final Resources res = context.getResources();
+ String[] entries = new String[INTERVAL_VALUES_HOURS.length];
+ for (int i = 0; i < INTERVAL_VALUES_HOURS.length; i++) {
+ int hours = INTERVAL_VALUES_HOURS[i];
+ entries[i] = res.getQuantityString(R.plurals.feed_refresh_every_x_hours, hours, hours);
+ }
+ return entries;
+ }
+
+ private void updateVisibility() {
+ viewBinding.spinner.setVisibility(viewBinding.intervalRadioButton.isChecked() ? View.VISIBLE : View.GONE);
+ viewBinding.timePicker.setVisibility(viewBinding.timeRadioButton.isChecked() ? View.VISIBLE : View.GONE);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java
index 779248e2f..a38ca6b71 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java
@@ -14,7 +14,7 @@ import java.util.HashSet;
import java.util.Set;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItemFilter;
+import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.core.feed.FeedItemFilterGroup;
import de.danoeh.antennapod.ui.common.RecursiveRadioGroup;
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java
index 7e0e59776..58f070046 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java
@@ -7,7 +7,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.model.feed.SortOrder;
public abstract class IntraFeedSortDialog {
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
index d0fb91692..13258b4ec 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
@@ -30,8 +30,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.service.download.ProxyConfig;
-import io.reactivex.Single;
-import io.reactivex.SingleOnSubscribe;
+import io.reactivex.Completable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
@@ -41,9 +40,6 @@ import okhttp3.Request;
import okhttp3.Response;
public class ProxyDialog {
-
- private static final String TAG = "ProxyDialog";
-
private final Context context;
private AlertDialog dialog;
@@ -116,32 +112,32 @@ public class ProxyDialog {
types.add(Proxy.Type.SOCKS.name());
}
ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
- android.R.layout.simple_spinner_item, types);
+ android.R.layout.simple_spinner_item, types);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spType.setAdapter(adapter);
ProxyConfig proxyConfig = UserPreferences.getProxyConfig();
spType.setSelection(adapter.getPosition(proxyConfig.type.name()));
etHost = content.findViewById(R.id.etHost);
- if(!TextUtils.isEmpty(proxyConfig.host)) {
+ if (!TextUtils.isEmpty(proxyConfig.host)) {
etHost.setText(proxyConfig.host);
}
etHost.addTextChangedListener(requireTestOnChange);
etPort = content.findViewById(R.id.etPort);
- if(proxyConfig.port > 0) {
+ if (proxyConfig.port > 0) {
etPort.setText(String.valueOf(proxyConfig.port));
}
etPort.addTextChangedListener(requireTestOnChange);
etUsername = content.findViewById(R.id.etUsername);
- if(!TextUtils.isEmpty(proxyConfig.username)) {
+ if (!TextUtils.isEmpty(proxyConfig.username)) {
etUsername.setText(proxyConfig.username);
}
etUsername.addTextChangedListener(requireTestOnChange);
etPassword = content.findViewById(R.id.etPassword);
- if(!TextUtils.isEmpty(proxyConfig.password)) {
- etPassword.setText(proxyConfig.username);
+ if (!TextUtils.isEmpty(proxyConfig.password)) {
+ etPassword.setText(proxyConfig.password);
}
etPassword.addTextChangedListener(requireTestOnChange);
- if(proxyConfig.type == Proxy.Type.DIRECT) {
+ if (proxyConfig.type == Proxy.Type.DIRECT) {
enableSettings(false);
setTestRequired(false);
}
@@ -184,8 +180,8 @@ public class ProxyDialog {
private boolean checkValidity() {
boolean valid = true;
- if(spType.getSelectedItemPosition() > 0) {
- valid &= checkHost();
+ if (spType.getSelectedItemPosition() > 0) {
+ valid = checkHost();
}
valid &= checkPort();
return valid;
@@ -193,11 +189,11 @@ public class ProxyDialog {
private boolean checkHost() {
String host = etHost.getText().toString();
- if(host.length() == 0) {
+ if (host.length() == 0) {
etHost.setError(context.getString(R.string.proxy_host_empty_error));
return false;
}
- if(!"localhost".equals(host) && !Patterns.DOMAIN_NAME.matcher(host).matches()) {
+ if (!"localhost".equals(host) && !Patterns.DOMAIN_NAME.matcher(host).matches()) {
etHost.setError(context.getString(R.string.proxy_host_invalid_error));
return false;
}
@@ -206,7 +202,7 @@ public class ProxyDialog {
private boolean checkPort() {
int port = getPort();
- if(port < 0 && port > 65535) {
+ if (port < 0 || port > 65535) {
etPort.setError(context.getString(R.string.proxy_port_invalid_error));
return false;
}
@@ -215,10 +211,10 @@ public class ProxyDialog {
private int getPort() {
String port = etPort.getText().toString();
- if(port.length() > 0) {
+ if (port.length() > 0) {
try {
return Integer.parseInt(port);
- } catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
// ignore
}
}
@@ -226,7 +222,7 @@ public class ProxyDialog {
}
private void setTestRequired(boolean required) {
- if(required) {
+ if (required) {
testSuccessful = false;
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.proxy_test_label);
} else {
@@ -240,7 +236,7 @@ public class ProxyDialog {
if (disposable != null) {
disposable.dispose();
}
- if(!checkValidity()) {
+ if (!checkValidity()) {
setTestRequired(true);
return;
}
@@ -251,7 +247,7 @@ public class ProxyDialog {
txtvMessage.setTextColor(textColorPrimary);
txtvMessage.setText("{fa-circle-o-notch spin} " + checking);
txtvMessage.setVisibility(View.VISIBLE);
- disposable = Single.create((SingleOnSubscribe<Response>) emitter -> {
+ disposable = Completable.create(emitter -> {
String type = (String) spType.getSelectedItem();
String host = etHost.getText().toString();
String port = etPort.getText().toString();
@@ -263,59 +259,44 @@ public class ProxyDialog {
}
SocketAddress address = InetSocketAddress.createUnresolved(host, portValue);
Proxy.Type proxyType = Proxy.Type.valueOf(type.toUpperCase(Locale.US));
- Proxy proxy = new Proxy(proxyType, address);
OkHttpClient.Builder builder = AntennapodHttpClient.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
- .proxy(proxy);
- builder.interceptors().clear();
- OkHttpClient client = builder.build();
+ .proxy(new Proxy(proxyType, address));
if (!TextUtils.isEmpty(username)) {
- String credentials = Credentials.basic(username, password);
- client.interceptors().add(chain -> {
- Request request = chain.request().newBuilder()
- .header("Proxy-Authorization", credentials).build();
- return chain.proceed(request);
+ builder.proxyAuthenticator((route, response) -> {
+ String credentials = Credentials.basic(username, password);
+ return response.request().newBuilder()
+ .header("Proxy-Authorization", credentials)
+ .build();
});
}
- Request request = new Request.Builder()
- .url("http://www.google.com")
- .head()
- .build();
- try {
- Response response = client.newCall(request).execute();
- emitter.onSuccess(response);
- } catch(IOException e) {
+ OkHttpClient client = builder.build();
+ Request request = new Request.Builder().url("https://www.example.com").head().build();
+ try (Response response = client.newCall(request).execute()) {
+ if (response.isSuccessful()) {
+ emitter.onComplete();
+ } else {
+ emitter.onError(new IOException(response.message()));
+ }
+ } catch (IOException e) {
emitter.onError(e);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
- response -> {
- int colorId;
- String icon;
- String result;
- if(response.isSuccessful()) {
- colorId = R.color.download_success_green;
- icon = "{fa-check}";
- result = context.getString(R.string.proxy_test_successful);
- } else {
- colorId = R.color.download_failed_red;
- icon = "{fa-close}";
- result = context.getString(R.string.proxy_test_failed);
- }
- int color = ContextCompat.getColor(context, colorId);
- txtvMessage.setTextColor(color);
- String message = String.format("%s %s: %s", icon, result, response.message());
+ () -> {
+ txtvMessage.setTextColor(ContextCompat.getColor(context, R.color.download_success_green));
+ String message = String.format("%s %s", "{fa-check}",
+ context.getString(R.string.proxy_test_successful));
txtvMessage.setText(message);
- setTestRequired(!response.isSuccessful());
+ setTestRequired(false);
},
error -> {
- String icon = "{fa-close}";
- String result = context.getString(R.string.proxy_test_failed);
- int color = ContextCompat.getColor(context, R.color.download_failed_red);
- txtvMessage.setTextColor(color);
- String message = String.format("%s %s: %s", icon, result, error.getMessage());
+ error.printStackTrace();
+ txtvMessage.setTextColor(ContextCompat.getColor(context, R.color.download_failed_red));
+ String message = String.format("%s %s: %s", "{fa-close}",
+ context.getString(R.string.proxy_test_failed), error.getMessage());
txtvMessage.setText(message);
setTestRequired(true);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java
index 156c1dba8..f42f0870d 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java
@@ -6,7 +6,7 @@ import android.content.DialogInterface;
import android.util.Log;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.storage.DBWriter;
import io.reactivex.Completable;
import io.reactivex.android.schedulers.AndroidSchedulers;
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
index a4e49ff96..42a854cd8 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
@@ -7,7 +7,7 @@ import java.lang.ref.WeakReference;
import android.view.View;
import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.databinding.EditTextDialogBinding;
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ShareDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ShareDialog.java
index 614cc1e71..37faea46f 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ShareDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ShareDialog.java
@@ -14,7 +14,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.util.ShareUtils;
public class ShareDialog extends DialogFragment {
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/StreamingConfirmationDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/StreamingConfirmationDialog.java
index 8e5ceece2..22f62d410 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/StreamingConfirmationDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/StreamingConfirmationDialog.java
@@ -4,7 +4,7 @@ import android.content.Context;
import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
public class StreamingConfirmationDialog {
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/TagSettingsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/TagSettingsDialog.java
new file mode 100644
index 000000000..de9f4d504
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/TagSettingsDialog.java
@@ -0,0 +1,120 @@
+package de.danoeh.antennapod.dialog;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import com.google.android.material.chip.Chip;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.databinding.EditTagsDialogBinding;
+import de.danoeh.antennapod.view.ItemOffsetDecoration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TagSettingsDialog extends DialogFragment {
+ public static final String TAG = "TagSettingsDialog";
+ private static final String ARG_FEED_PREFERENCES = "feed_preferences";
+ private List<String> displayedTags;
+ private EditTagsDialogBinding viewBinding;
+ private TagSelectionAdapter adapter;
+
+ public static TagSettingsDialog newInstance(FeedPreferences preferences) {
+ TagSettingsDialog fragment = new TagSettingsDialog();
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_FEED_PREFERENCES, preferences);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ FeedPreferences preferences = (FeedPreferences) getArguments().getSerializable(ARG_FEED_PREFERENCES);
+ displayedTags = new ArrayList<>(preferences.getTags());
+ displayedTags.remove(FeedPreferences.TAG_ROOT);
+
+ viewBinding = EditTagsDialogBinding.inflate(getLayoutInflater());
+ viewBinding.tagsRecycler.setLayoutManager(new GridLayoutManager(getContext(), 2));
+ viewBinding.tagsRecycler.addItemDecoration(new ItemOffsetDecoration(getContext(), 4));
+ adapter = new TagSelectionAdapter();
+ adapter.setHasStableIds(true);
+ viewBinding.tagsRecycler.setAdapter(adapter);
+ viewBinding.rootFolderCheckbox.setChecked(preferences.getTags().contains(FeedPreferences.TAG_ROOT));
+
+ viewBinding.newTagButton.setOnClickListener(v ->
+ addTag(viewBinding.newTagEditText.getText().toString().trim()));
+
+ AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
+ dialog.setView(viewBinding.getRoot());
+ dialog.setTitle(R.string.feed_folders_label);
+ dialog.setPositiveButton(android.R.string.ok, (d, input) -> {
+ addTag(viewBinding.newTagEditText.getText().toString().trim());
+ preferences.getTags().clear();
+ preferences.getTags().addAll(displayedTags);
+ if (viewBinding.rootFolderCheckbox.isChecked()) {
+ preferences.getTags().add(FeedPreferences.TAG_ROOT);
+ }
+ DBWriter.setFeedPreferences(preferences);
+ });
+ dialog.setNegativeButton(R.string.cancel_label, null);
+ return dialog.create();
+ }
+
+ private void addTag(String name) {
+ if (TextUtils.isEmpty(name) || displayedTags.contains(name)) {
+ return;
+ }
+ displayedTags.add(name);
+ viewBinding.newTagEditText.setText("");
+ adapter.notifyDataSetChanged();
+ }
+
+ public class TagSelectionAdapter extends RecyclerView.Adapter<TagSelectionAdapter.ViewHolder> {
+
+ @Override
+ @NonNull
+ public TagSelectionAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ Chip chip = new Chip(getContext());
+ chip.setCloseIconVisible(true);
+ chip.setCloseIconResource(R.drawable.ic_delete);
+ return new TagSelectionAdapter.ViewHolder(chip);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull TagSelectionAdapter.ViewHolder holder, int position) {
+ holder.chip.setText(displayedTags.get(position));
+ holder.chip.setOnCloseIconClickListener(v -> {
+ displayedTags.remove(position);
+ notifyDataSetChanged();
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return displayedTags.size();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return displayedTags.get(position).hashCode();
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder {
+ Chip chip;
+
+ ViewHolder(Chip itemView) {
+ super(itemView);
+ chip = itemView;
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
index 65e7c4424..c6927c69f 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.dialog;
import android.app.Dialog;
-import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
@@ -41,16 +40,6 @@ public class VariableSpeedDialog extends DialogFragment {
selectedSpeeds = new ArrayList<>(UserPreferences.getPlaybackSpeedArray());
}
- public static void showGetPluginDialog(final Context context) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.no_playback_plugin_title);
- builder.setMessage(R.string.no_playback_plugin_or_sonic_msg);
- builder.setPositiveButton(R.string.enable_sonic, (dialog, which) ->
- UserPreferences.enableSonic());
- builder.setNeutralButton(R.string.close_label, null);
- builder.show();
- }
-
@Override
public void onStart() {
super.onStart();
@@ -98,7 +87,7 @@ public class VariableSpeedDialog extends DialogFragment {
addCurrentSpeedChip = root.findViewById(R.id.add_current_speed_chip);
addCurrentSpeedChip.setCloseIconVisible(true);
- addCurrentSpeedChip.setCloseIconResource(R.drawable.ic_add_black);
+ addCurrentSpeedChip.setCloseIconResource(R.drawable.ic_add);
addCurrentSpeedChip.setOnCloseIconClickListener(v -> addCurrentSpeed());
addCurrentSpeedChip.setOnClickListener(v -> addCurrentSpeed());
@@ -126,7 +115,7 @@ public class VariableSpeedDialog extends DialogFragment {
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Chip chip = new Chip(getContext());
chip.setCloseIconVisible(true);
- chip.setCloseIconResource(R.drawable.ic_delete_black);
+ chip.setCloseIconResource(R.drawable.ic_delete);
return new ViewHolder(chip);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java
index 6de2186e0..f97c1c7ab 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java
@@ -2,9 +2,9 @@ package de.danoeh.antennapod.discovery;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetPodcast;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetServiceException;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetPodcast;
import io.reactivex.Single;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -18,7 +18,8 @@ public class GpodnetPodcastSearcher implements PodcastSearcher {
return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> {
try {
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
- GpodnetPreferences.getHosturl());
+ GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(),
+ GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
List<GpodnetPodcast> gpodnetPodcasts = service.searchPodcasts(query, 0);
List<PodcastSearchResult> results = new ArrayList<>();
for (GpodnetPodcast podcast : gpodnetPodcasts) {
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java
index e1034f89b..e4135fcaa 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.discovery;
import android.content.Context;
-import android.content.SharedPreferences;
import android.util.Log;
import de.danoeh.antennapod.R;
@@ -24,8 +23,6 @@ import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
-import static android.content.Context.MODE_PRIVATE;
-
public class ItunesTopListLoader {
private static final String TAG = "ITunesTopListLoader";
private final Context context;
@@ -38,13 +35,6 @@ public class ItunesTopListLoader {
this.context = context;
}
- public Single<List<PodcastSearchResult>> loadToplist() {
- String defaultCountry = Locale.getDefault().getCountry();
- SharedPreferences prefs = context.getSharedPreferences(PREFS, MODE_PRIVATE);
- String countryCode = prefs.getString(PREF_KEY_COUNTRY_CODE, COUNTRY_CODE_UNSET);
- return this.loadToplist(countryCode, 25);
- }
-
public Single<List<PodcastSearchResult>> loadToplist(String country, int limit) {
return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) emitter -> {
OkHttpClient client = AntennapodHttpClient.getHttpClient();
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java
index bba438d1d..767845cb4 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.discovery;
import androidx.annotation.Nullable;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetPodcast;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetPodcast;
import de.mfietz.fyydlin.SearchHit;
import org.json.JSONArray;
import org.json.JSONException;
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java
index 16c5548be..dfea627df 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java
@@ -12,7 +12,7 @@ public class PodcastSearcherRegistry {
private PodcastSearcherRegistry() {
}
- public static List<SearcherInfo> getSearchProviders() {
+ public static synchronized List<SearcherInfo> getSearchProviders() {
if (searchProviders == null) {
searchProviders = new ArrayList<>();
searchProviders.add(new SearcherInfo(new CombinedSearcher(), 1.0f));
diff --git a/app/src/main/java/de/danoeh/antennapod/error/CrashReportWriter.java b/app/src/main/java/de/danoeh/antennapod/error/CrashReportWriter.java
index dc62863f9..23c8ffdd5 100644
--- a/app/src/main/java/de/danoeh/antennapod/error/CrashReportWriter.java
+++ b/app/src/main/java/de/danoeh/antennapod/error/CrashReportWriter.java
@@ -7,7 +7,6 @@ import de.danoeh.antennapod.BuildConfig;
import org.apache.commons.io.IOUtils;
import java.io.File;
-import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
@@ -40,7 +39,7 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler {
File path = getFile();
PrintWriter out = null;
try {
- out = new PrintWriter(new FileWriter(path));
+ out = new PrintWriter(path, "UTF-8");
out.println("## Crash info");
out.println("Time: " + new SimpleDateFormat("dd-MM-yyyy HH:mm:ss", Locale.getDefault()).format(new Date()));
out.println("AntennaPod version: " + BuildConfig.VERSION_NAME);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
index 08e23fc7f..64e7f161e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -25,10 +25,10 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.activity.OpmlImportActivity;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
-import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.databinding.AddfeedBinding;
import de.danoeh.antennapod.databinding.EditTextDialogBinding;
import de.danoeh.antennapod.discovery.CombinedSearcher;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
index 612959c04..f65c6fdc6 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -10,8 +10,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedItemFilter;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.dialog.FilterDialog;
import org.apache.commons.lang3.StringUtils;
@@ -27,7 +27,7 @@ public class AllEpisodesFragment extends EpisodesListFragment {
private static final String PREF_NAME = "PrefAllEpisodesFragment";
private static final String PREF_FILTER = "filter";
- private static FeedItemFilter feedItemFilter = new FeedItemFilter("");
+ private FeedItemFilter feedItemFilter = new FeedItemFilter("");
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
index 1580ac930..64cbaa023 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
@@ -11,6 +11,7 @@ import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
@@ -20,26 +21,37 @@ import androidx.fragment.app.Fragment;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
+
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
-import com.google.android.material.tabs.TabLayout;
-import com.google.android.material.tabs.TabLayoutMediator;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.List;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.event.FavoritesEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.event.ServiceEvent;
+import de.danoeh.antennapod.model.feed.Chapter;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
@@ -47,28 +59,22 @@ import de.danoeh.antennapod.dialog.SleepTimerDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.ui.common.PlaybackSpeedIndicatorView;
+import de.danoeh.antennapod.view.ChapterSeekBar;
+import de.danoeh.antennapod.view.PlayButton;
import io.reactivex.Maybe;
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.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.util.List;
/**
* Shows the audio player.
*/
public class AudioPlayerFragment extends Fragment implements
- SeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
+ ChapterSeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
public static final String TAG = "AudioPlayerFragment";
- private static final int POS_COVER = 0;
- private static final int POS_SHOWNOTES = 1;
- private static final int POS_CHAPTERS = 2;
- private static final int NUM_CONTENT_FRAGMENTS = 3;
+ public static final int POS_COVER = 0;
+ public static final int POS_DESCRIPTION = 1;
+ private static final int NUM_CONTENT_FRAGMENTS = 2;
private static final float EPSILON = 0.001f;
PlaybackSpeedIndicatorView butPlaybackSpeed;
@@ -76,10 +82,10 @@ public class AudioPlayerFragment extends Fragment implements
private ViewPager2 pager;
private TextView txtvPosition;
private TextView txtvLength;
- private SeekBar sbPosition;
+ private ChapterSeekBar sbPosition;
private ImageButton butRev;
private TextView txtvRev;
- private ImageButton butPlay;
+ private PlayButton butPlay;
private ImageButton butFF;
private TextView txtvFF;
private ImageButton butSkip;
@@ -91,8 +97,9 @@ public class AudioPlayerFragment extends Fragment implements
private PlaybackController controller;
private Disposable disposable;
private boolean showTimeLeft;
- private boolean hasChapters = false;
- private TabLayoutMediator tabLayoutMediator;
+ private boolean seekedToChapterStart = false;
+ private int currentChapterIndex = -1;
+ private int duration;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@@ -148,34 +155,27 @@ public class AudioPlayerFragment extends Fragment implements
}
});
- TabLayout tabLayout = root.findViewById(R.id.sliding_tabs);
- tabLayoutMediator = new TabLayoutMediator(tabLayout, pager, (tab, position) -> {
- tab.view.setAlpha(1.0f);
- switch (position) {
- case POS_COVER:
- tab.setText(R.string.cover_label);
- break;
- case POS_SHOWNOTES:
- tab.setText(R.string.shownotes_label);
- break;
- case POS_CHAPTERS:
- tab.setText(R.string.chapters_label);
- if (!hasChapters) {
- tab.view.setAlpha(0.5f);
- }
- break;
- default:
- break;
- }
- });
- tabLayoutMediator.attach();
return root;
}
- public void setHasChapters(boolean hasChapters) {
- this.hasChapters = hasChapters;
- tabLayoutMediator.detach();
- tabLayoutMediator.attach();
+ private void setChapterDividers(Playable media) {
+
+ if (media == null) {
+ return;
+ }
+
+ float[] dividerPos = null;
+
+ if (media.getChapters() != null) {
+ List<Chapter> chapters = media.getChapters();
+ dividerPos = new float[chapters.size()];
+
+ for (int i = 0; i < chapters.size(); i++) {
+ dividerPos[i] = chapters.get(i).getStart() / (float) duration;
+ }
+ }
+
+ sbPosition.setDividerPos(dividerPos);
}
public View getExternalPlayerHolder() {
@@ -224,6 +224,13 @@ public class AudioPlayerFragment extends Fragment implements
controller.getDuration()));
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlaybackServiceChanged(ServiceEvent event) {
+ if (event.action == ServiceEvent.Action.SERVICE_SHUT_DOWN) {
+ ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
+ }
+ }
+
private void setupLengthTextView() {
showTimeLeft = UserPreferences.shouldShowRemainingTime();
txtvLength.setOnClickListener(v -> {
@@ -242,10 +249,6 @@ public class AudioPlayerFragment extends Fragment implements
if (controller == null) {
return;
}
- if (!controller.canSetPlaybackSpeed()) {
- VariableSpeedDialog.showGetPluginDialog(getContext());
- return;
- }
List<Float> availableSpeeds = UserPreferences.getPlaybackSpeedArray();
float currentSpeed = controller.getCurrentPlaybackSpeedMultiplier();
@@ -279,14 +282,10 @@ public class AudioPlayerFragment extends Fragment implements
if (butPlaybackSpeed == null || controller == null) {
return;
}
- float speed = 1.0f;
- if (controller.canSetPlaybackSpeed()) {
- speed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(media);
- }
+ float speed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(media);
String speedStr = new DecimalFormat("0.00").format(speed);
txtvPlaybackSpeed.setText(speedStr);
butPlaybackSpeed.setSpeed(speed);
- butPlaybackSpeed.setAlpha(controller.canSetPlaybackSpeed() ? 1.0f : 0.5f);
butPlaybackSpeed.setVisibility(View.VISIBLE);
txtvPlaybackSpeed.setVisibility(View.VISIBLE);
}
@@ -298,16 +297,17 @@ public class AudioPlayerFragment extends Fragment implements
disposable = Maybe.create(emitter -> {
Playable media = controller.getMedia();
if (media != null) {
+ ChapterUtils.loadChapters(media, getContext());
emitter.onSuccess(media);
} else {
emitter.onComplete();
}
})
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(media -> updateUi((Playable) media),
- error -> Log.e(TAG, Log.getStackTraceString(error)),
- () -> updateUi(null));
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(media -> updateUi((Playable) media),
+ error -> Log.e(TAG, Log.getStackTraceString(error)),
+ () -> updateUi(null));
}
private PlaybackController newPlaybackController() {
@@ -354,8 +354,8 @@ public class AudioPlayerFragment extends Fragment implements
}
@Override
- public ImageButton getPlayButton() {
- return butPlay;
+ protected void updatePlayButtonShowsPlay(boolean showPlay) {
+ butPlay.setIsShowPlay(showPlay);
}
@Override
@@ -364,11 +364,6 @@ public class AudioPlayerFragment extends Fragment implements
}
@Override
- public void onShutdownNotification() {
- ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
- }
-
- @Override
public void onPlaybackEnd() {
((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
}
@@ -377,11 +372,6 @@ public class AudioPlayerFragment extends Fragment implements
public void onPlaybackSpeedChange() {
updatePlaybackSpeedButton(getMedia());
}
-
- @Override
- public void onSetSpeedAbilityChanged() {
- updatePlaybackSpeedButton(getMedia());
- }
};
}
@@ -389,8 +379,10 @@ public class AudioPlayerFragment extends Fragment implements
if (controller == null) {
return;
}
- updatePosition(new PlaybackPositionEvent(controller.getPosition(), controller.getDuration()));
+ duration = controller.getDuration();
+ updatePosition(new PlaybackPositionEvent(controller.getPosition(), duration));
updatePlaybackSpeedButton(media);
+ setChapterDividers(media);
setupOptionsMenu(media);
}
@@ -433,6 +425,7 @@ public class AudioPlayerFragment extends Fragment implements
int currentPosition = converter.convert(event.getPosition());
int duration = converter.convert(event.getDuration());
int remainingTime = converter.convert(Math.max(event.getDuration() - event.getPosition(), 0));
+ currentChapterIndex = ChapterUtils.getCurrentChapterIndex(controller.getMedia(), currentPosition);
Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition));
if (currentPosition == PlaybackService.INVALID_TIME || duration == PlaybackService.INVALID_TIME) {
Log.w(TAG, "Could not react to position observer update because of invalid time");
@@ -445,8 +438,11 @@ public class AudioPlayerFragment extends Fragment implements
} else {
txtvLength.setText(Converter.getDurationStringLong(duration));
}
- float progress = ((float) event.getPosition()) / event.getDuration();
- sbPosition.setProgress((int) (progress * sbPosition.getMax()));
+
+ if (!sbPosition.isPressed()) {
+ float progress = ((float) event.getPosition()) / event.getDuration();
+ sbPosition.setProgress((int) (progress * sbPosition.getMax()));
+ }
}
@Subscribe(threadMode = ThreadMode.MAIN)
@@ -459,11 +455,28 @@ public class AudioPlayerFragment extends Fragment implements
if (controller == null || txtvLength == null) {
return;
}
+
if (fromUser) {
float prog = progress / ((float) seekBar.getMax());
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
int position = converter.convert((int) (prog * controller.getDuration()));
- txtvSeek.setText(Converter.getDurationStringLong(position));
+ int newChapterIndex = ChapterUtils.getCurrentChapterIndex(controller.getMedia(), position);
+ if (newChapterIndex > -1) {
+ if (!sbPosition.isPressed() && currentChapterIndex != newChapterIndex) {
+ currentChapterIndex = newChapterIndex;
+ position = (int) controller.getMedia().getChapters().get(currentChapterIndex).getStart();
+ seekedToChapterStart = true;
+ controller.seekTo(position);
+ updateUi(controller.getMedia());
+ sbPosition.highlightCurrentChapter();
+ }
+ txtvSeek.setText(controller.getMedia().getChapters().get(newChapterIndex).getTitle()
+ + "\n" + Converter.getDurationStringLong(position));
+ } else {
+ txtvSeek.setText(Converter.getDurationStringLong(position));
+ }
+ } else if (duration != controller.getDuration()) {
+ updateUi(controller.getMedia());
}
}
@@ -482,8 +495,12 @@ public class AudioPlayerFragment extends Fragment implements
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (controller != null) {
- float prog = seekBar.getProgress() / ((float) seekBar.getMax());
- controller.seekTo((int) (prog * controller.getDuration()));
+ if (seekedToChapterStart) {
+ seekedToChapterStart = false;
+ } else {
+ float prog = seekBar.getProgress() / ((float) seekBar.getMax());
+ controller.seekTo((int) (prog * controller.getDuration()));
+ }
}
cardViewSeek.setScaleX(1f);
cardViewSeek.setScaleY(1f);
@@ -557,14 +574,13 @@ public class AudioPlayerFragment extends Fragment implements
@Override
public Fragment createFragment(int position) {
Log.d(TAG, "getItem(" + position + ")");
+
switch (position) {
case POS_COVER:
return new CoverFragment();
- case POS_SHOWNOTES:
- return new ItemDescriptionFragment();
default:
- case POS_CHAPTERS:
- return new ChaptersFragment();
+ case POS_DESCRIPTION:
+ return new ItemDescriptionFragment();
}
}
@@ -573,4 +589,21 @@ public class AudioPlayerFragment extends Fragment implements
return NUM_CONTENT_FRAGMENTS;
}
}
+
+ public void scrollToPage(int page, boolean smoothScroll) {
+ if (pager == null) {
+ return;
+ }
+
+ pager.setCurrentItem(page, smoothScroll);
+
+ Fragment visibleChild = getChildFragmentManager().findFragmentByTag("f" + POS_DESCRIPTION);
+ if (visibleChild instanceof ItemDescriptionFragment) {
+ ((ItemDescriptionFragment) visibleChild).scrollToTop();
+ }
+ }
+
+ public void scrollToPage(int page) {
+ scrollToPage(page, false);
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
index b578a603f..de14f220e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -1,48 +1,65 @@
package de.danoeh.antennapod.fragment;
+import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.util.ChapterUtils;
-import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
-import de.danoeh.antennapod.view.EmptyViewHandler;
+import de.danoeh.antennapod.model.feed.Chapter;
+import de.danoeh.antennapod.model.playback.Playable;
import io.reactivex.Maybe;
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;
-public class ChaptersFragment extends Fragment {
- private static final String TAG = "ChaptersFragment";
+public class ChaptersFragment extends AppCompatDialogFragment {
+ public static final String TAG = "ChaptersFragment";
private ChaptersListAdapter adapter;
private PlaybackController controller;
private Disposable disposable;
private int focusedChapter = -1;
private Playable media;
private LinearLayoutManager layoutManager;
+ private ProgressBar progressBar;
- @Nullable
+ @NonNull
@Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
- View root = inflater.inflate(R.layout.simple_list_fragment, container, false);
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ return new AlertDialog.Builder(requireContext())
+ .setTitle(getString(R.string.chapters_label))
+ .setView(onCreateView(getLayoutInflater()))
+ .setNegativeButton(getString(R.string.cancel_label), null) //dismisses
+ .create();
+ }
+
+
+ public View onCreateView(@NonNull LayoutInflater inflater) {
+ View root = inflater.inflate(R.layout.simple_list_fragment, null, false);
root.findViewById(R.id.toolbar).setVisibility(View.GONE);
RecyclerView recyclerView = root.findViewById(R.id.recyclerView);
+ progressBar = root.findViewById(R.id.progLoading);
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(),
@@ -53,16 +70,16 @@ public class ChaptersFragment extends Fragment {
controller.playPause();
}
Chapter chapter = adapter.getItem(pos);
- controller.seekToChapter(chapter);
+ controller.seekTo((int) chapter.getStart());
updateChapterSelection(pos);
});
recyclerView.setAdapter(adapter);
- EmptyViewHandler emptyView = new EmptyViewHandler(getContext());
- emptyView.attachToRecyclerView(recyclerView);
- emptyView.setIcon(R.attr.ic_bookmark);
- emptyView.setTitle(R.string.no_chapters_head_label);
- emptyView.setMessage(R.string.no_chapters_label);
+ progressBar.setVisibility(View.VISIBLE);
+
+ RelativeLayout.LayoutParams wrapHeight = new RelativeLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ recyclerView.setLayoutParams(wrapHeight);
return root;
}
@@ -118,7 +135,7 @@ public class ChaptersFragment extends Fragment {
disposable = Maybe.create(emitter -> {
Playable media = controller.getMedia();
if (media != null) {
- media.loadChapterMarks(getContext());
+ ChapterUtils.loadChapters(media, getContext());
emitter.onSuccess(media);
} else {
emitter.onComplete();
@@ -136,8 +153,12 @@ public class ChaptersFragment extends Fragment {
if (adapter == null) {
return;
}
+ if (media.getChapters() != null && media.getChapters().size() <= 0) {
+ dismiss();
+ } else {
+ progressBar.setVisibility(View.GONE);
+ }
adapter.setMedia(media);
- ((AudioPlayerFragment) getParentFragment()).setHasChapters(adapter.getItemCount() > 0);
int positionOfCurrentChapter = getCurrentChapter(media);
updateChapterSelection(positionOfCurrentChapter);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
index 3519a34b4..613216610 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -22,7 +22,7 @@ import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -146,7 +146,7 @@ public class CompletedDownloadsFragment extends Fragment {
private void addEmptyView() {
emptyView = new EmptyViewHandler(getActivity());
- emptyView.setIcon(R.attr.av_download);
+ emptyView.setIcon(R.drawable.ic_download);
emptyView.setTitle(R.string.no_comp_downloads_head_label);
emptyView.setMessage(R.string.no_comp_downloads_label);
emptyView.attachToRecyclerView(recyclerView);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index 5cb44655e..0dd97098e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -1,7 +1,15 @@
package de.danoeh.antennapod.fragment;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.ClipData;
+import android.content.ClipboardManager;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
+import android.graphics.ColorFilter;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
@@ -10,34 +18,47 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.Space;
import android.widget.TextView;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+import androidx.core.graphics.BlendModeColorFilterCompat;
+import androidx.core.graphics.BlendModeCompat;
import androidx.fragment.app.Fragment;
+
import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
-import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.request.RequestOptions;
+import com.google.android.material.snackbar.Snackbar;
+
+import org.apache.commons.lang3.StringUtils;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
-import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.model.feed.Chapter;
+import de.danoeh.antennapod.model.playback.Playable;
import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.apache.commons.lang3.StringUtils;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
/**
* Displays the cover and the title of a FeedItem.
@@ -51,9 +72,16 @@ public class CoverFragment extends Fragment {
private TextView txtvPodcastTitle;
private TextView txtvEpisodeTitle;
private ImageView imgvCover;
+ private LinearLayout openDescription;
+ private Space counterweight;
+ private Space spacer;
+ private ImageButton butPrevChapter;
+ private ImageButton butNextChapter;
+ private LinearLayout episodeDetails;
+ private LinearLayout chapterControl;
private PlaybackController controller;
private Disposable disposable;
- private int displayedChapterIndex = -2;
+ private int displayedChapterIndex = -1;
private Playable media;
@Override
@@ -64,7 +92,30 @@ public class CoverFragment extends Fragment {
txtvPodcastTitle = root.findViewById(R.id.txtvPodcastTitle);
txtvEpisodeTitle = root.findViewById(R.id.txtvEpisodeTitle);
imgvCover = root.findViewById(R.id.imgvCover);
+ episodeDetails = root.findViewById(R.id.episode_details);
+ final ImageView descriptionIcon = root.findViewById(R.id.description_icon);
+ chapterControl = root.findViewById(R.id.chapterButton);
+ butPrevChapter = root.findViewById(R.id.butPrevChapter);
+ butNextChapter = root.findViewById(R.id.butNextChapter);
+
imgvCover.setOnClickListener(v -> onPlayPause());
+ openDescription = root.findViewById(R.id.openDescription);
+ counterweight = root.findViewById(R.id.counterweight);
+ spacer = root.findViewById(R.id.details_spacer);
+ View.OnClickListener scrollToDesc = view ->
+ ((AudioPlayerFragment) requireParentFragment()).scrollToPage(AudioPlayerFragment.POS_DESCRIPTION, true);
+ openDescription.setOnClickListener(scrollToDesc);
+ ColorFilter colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
+ txtvPodcastTitle.getCurrentTextColor(), BlendModeCompat.SRC_IN);
+ butNextChapter.setColorFilter(colorFilter);
+ butPrevChapter.setColorFilter(colorFilter);
+ descriptionIcon.setColorFilter(colorFilter);
+ ChaptersFragment chaptersFragment = new ChaptersFragment();
+ chapterControl.setOnClickListener(v ->
+ chaptersFragment.show(getChildFragmentManager(), ChaptersFragment.TAG));
+ butPrevChapter.setOnClickListener(v -> seekToPrevChapter());
+ butNextChapter.setOnClickListener(v -> seekToNextChapter());
+
return root;
}
@@ -80,6 +131,7 @@ public class CoverFragment extends Fragment {
disposable = Maybe.<Playable>create(emitter -> {
Playable media = controller.getMedia();
if (media != null) {
+ ChapterUtils.loadChapters(media, getContext());
emitter.onSuccess(media);
} else {
emitter.onComplete();
@@ -99,9 +151,102 @@ public class CoverFragment extends Fragment {
+ "・"
+ "\u00A0"
+ StringUtils.replace(StringUtils.stripToEmpty(pubDateStr), " ", "\u00A0"));
+ Intent openFeed = MainActivity.getIntentToOpenFeed(requireContext(), ((FeedMedia) media).getItem().getFeedId());
+ txtvPodcastTitle.setOnClickListener(v -> startActivity(openFeed));
+ txtvPodcastTitle.setOnLongClickListener(v -> copyText(media.getFeedTitle()));
txtvEpisodeTitle.setText(media.getEpisodeTitle());
- displayedChapterIndex = -2; // Force refresh
- displayCoverImage(media.getPosition());
+ txtvEpisodeTitle.setOnLongClickListener(v -> copyText(media.getEpisodeTitle()));
+ txtvEpisodeTitle.setOnClickListener(v -> {
+ int lines = txtvEpisodeTitle.getLineCount();
+ int animUnit = 1500;
+ if (lines > txtvEpisodeTitle.getMaxLines()) {
+ ObjectAnimator verticalMarquee = ObjectAnimator.ofInt(
+ txtvEpisodeTitle, "scrollY", 0, txtvEpisodeTitle.getHeight())
+ .setDuration(lines * animUnit);
+ ObjectAnimator fadeOut = ObjectAnimator.ofFloat(
+ txtvEpisodeTitle, "alpha", 0);
+ fadeOut.setStartDelay(animUnit);
+ fadeOut.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ txtvEpisodeTitle.scrollTo(0, 0);
+ }
+ });
+ ObjectAnimator fadeBackIn = ObjectAnimator.ofFloat(
+ txtvEpisodeTitle, "alpha", 1);
+ AnimatorSet set = new AnimatorSet();
+ set.playSequentially(verticalMarquee, fadeOut, fadeBackIn);
+ set.start();
+ }
+ });
+
+ displayedChapterIndex = -1;
+ refreshChapterData(ChapterUtils.getCurrentChapterIndex(media, media.getPosition())); //calls displayCoverImage
+ updateChapterControlVisibility();
+ }
+
+ private void updateChapterControlVisibility() {
+ if (media.getChapters() != null) {
+ boolean chapterControlVisible = media.getChapters().size() > 0;
+ int newVisibility = chapterControlVisible ? View.VISIBLE : View.GONE;
+ if (chapterControl.getVisibility() != newVisibility) {
+ chapterControl.setVisibility(newVisibility);
+ ObjectAnimator.ofFloat(chapterControl,
+ "alpha",
+ chapterControlVisible ? 0 : 1,
+ chapterControlVisible ? 1 : 0)
+ .start();
+ }
+ }
+ }
+
+ private void refreshChapterData(int chapterIndex) {
+ if (chapterIndex > -1) {
+ if (media.getPosition() > media.getDuration() || chapterIndex >= media.getChapters().size() - 1) {
+ displayedChapterIndex = media.getChapters().size() - 1;
+ butNextChapter.setVisibility(View.INVISIBLE);
+ } else {
+ displayedChapterIndex = chapterIndex;
+ butNextChapter.setVisibility(View.VISIBLE);
+ }
+ }
+
+ displayCoverImage();
+ }
+
+ private Chapter getCurrentChapter() {
+ if (media == null || media.getChapters() == null || displayedChapterIndex == -1) {
+ return null;
+ }
+ return media.getChapters().get(displayedChapterIndex);
+ }
+
+ private void seekToPrevChapter() {
+ Chapter curr = getCurrentChapter();
+
+ if (controller == null || curr == null || displayedChapterIndex == -1) {
+ return;
+ }
+
+ if (displayedChapterIndex < 1) {
+ controller.seekTo(0);
+ } else if ((controller.getPosition() - 10000 * controller.getCurrentPlaybackSpeedMultiplier())
+ < curr.getStart()) {
+ refreshChapterData(displayedChapterIndex - 1);
+ controller.seekTo((int) media.getChapters().get(displayedChapterIndex).getStart());
+ } else {
+ controller.seekTo((int) curr.getStart());
+ }
+ }
+
+ private void seekToNextChapter() {
+ if (controller == null || media == null || media.getChapters() == null
+ || displayedChapterIndex == -1 || displayedChapterIndex + 1 >= media.getChapters().size()) {
+ return;
+ }
+
+ refreshChapterData(displayedChapterIndex + 1);
+ controller.seekTo((int) media.getChapters().get(displayedChapterIndex).getStart());
}
@Override
@@ -139,22 +284,18 @@ public class CoverFragment extends Fragment {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(PlaybackPositionEvent event) {
- if (media == null) {
- return;
+ int newChapterIndex = ChapterUtils.getCurrentChapterIndex(media, event.getPosition());
+ if (newChapterIndex > -1 && newChapterIndex != displayedChapterIndex) {
+ refreshChapterData(newChapterIndex);
}
- displayCoverImage(event.getPosition());
}
- private void displayCoverImage(int position) {
- int chapter = ChapterUtils.getCurrentChapterIndex(media, position);
- if (chapter != displayedChapterIndex) {
- displayedChapterIndex = chapter;
-
- RequestOptions options = new RequestOptions()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .dontAnimate()
- .transforms(new FitCenter(),
- new RoundedCorners((int) (16 * getResources().getDisplayMetrics().density)));
+ private void displayCoverImage() {
+ RequestOptions options = new RequestOptions()
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .dontAnimate()
+ .transforms(new FitCenter(),
+ new RoundedCorners((int) (16 * getResources().getDisplayMetrics().density)));
RequestBuilder<Drawable> cover = Glide.with(this)
.load(media.getImageLocation())
@@ -163,16 +304,16 @@ public class CoverFragment extends Fragment {
.apply(options))
.apply(options);
- if (chapter == -1 || TextUtils.isEmpty(media.getChapters().get(chapter).getImageUrl())) {
- cover.into(imgvCover);
- } else {
- Glide.with(this)
- .load(EmbeddedChapterImage.getModelFor(media, chapter))
- .apply(options)
- .thumbnail(cover)
- .error(cover)
- .into(imgvCover);
- }
+ if (displayedChapterIndex == -1 || media == null || media.getChapters() == null
+ || TextUtils.isEmpty(media.getChapters().get(displayedChapterIndex).getImageUrl())) {
+ cover.into(imgvCover);
+ } else {
+ Glide.with(this)
+ .load(EmbeddedChapterImage.getModelFor(media, displayedChapterIndex))
+ .apply(options)
+ .thumbnail(cover)
+ .error(cover)
+ .into(imgvCover);
}
}
@@ -196,6 +337,10 @@ public class CoverFragment extends Fragment {
LinearLayout.LayoutParams textParams = (LinearLayout.LayoutParams) textContainer.getLayoutParams();
double ratio = (float) newConfig.screenHeightDp / (float) newConfig.screenWidthDp;
+ boolean spacerVisible = true;
+ ViewGroup detailsParent = (ViewGroup) getView();
+ int detailsWidth = ViewGroup.LayoutParams.MATCH_PARENT;
+
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
double percentageWidth = 0.8;
if (ratio <= SIXTEEN_BY_NINE) {
@@ -217,6 +362,26 @@ public class CoverFragment extends Fragment {
textParams.weight = 1;
imgvCover.setLayoutParams(params);
}
+
+ spacerVisible = false;
+ detailsParent = textContainer;
+ detailsWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+
+ if (displayedChapterIndex == -1) {
+ detailsWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+
+ spacer.setVisibility(spacerVisible ? View.VISIBLE : View.GONE);
+ counterweight.setVisibility(spacerVisible ? View.VISIBLE : View.GONE);
+ LinearLayout.LayoutParams wrapHeight =
+ new LinearLayout.LayoutParams(detailsWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
+ episodeDetails.setLayoutParams(wrapHeight);
+ getView().findViewById(R.id.vertical_divider).setVisibility(spacerVisible ? View.GONE : View.VISIBLE);
+
+ if (episodeDetails.getParent() != detailsParent) {
+ ((ViewGroup) episodeDetails.getParent()).removeView(episodeDetails);
+ detailsParent.addView(episodeDetails);
}
}
@@ -226,4 +391,15 @@ public class CoverFragment extends Fragment {
}
controller.playPause();
}
+
+ private boolean copyText(String text) {
+ ClipboardManager clipboardManager = ContextCompat.getSystemService(requireContext(), ClipboardManager.class);
+ if (clipboardManager != null) {
+ clipboardManager.setPrimaryClip(ClipData.newPlainText("AntennaPod", text));
+ }
+ ((MainActivity) requireActivity()).showSnackbarAbovePlayer(
+ getResources().getString(R.string.copied_to_clipboard),
+ Snackbar.LENGTH_SHORT);
+ return true;
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
index 2e11ea4ec..1f6067125 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -5,36 +5,36 @@ import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.ListFragment;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.ListFragment;
import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DownloadLogAdapter;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloadLogEvent;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
+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.DownloadStatus;
+import de.danoeh.antennapod.core.service.download.Downloader;
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.download.AutoUpdateManager;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -44,6 +44,9 @@ import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Shows the download log
*/
@@ -52,6 +55,7 @@ public class DownloadLogFragment extends ListFragment {
private static final String TAG = "DownloadLogFragment";
private List<DownloadStatus> downloadLog = new ArrayList<>();
+ private List<Downloader> runningDownloads = new ArrayList<>();
private DownloadLogAdapter adapter;
private Disposable disposable;
@@ -60,7 +64,7 @@ public class DownloadLogFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- loadItems();
+ loadDownloadLog();
}
@Override
@@ -79,14 +83,15 @@ public class DownloadLogFragment extends ListFragment {
lv.setClipToPadding(false);
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
lv.setPadding(0, vertPadding, 0, vertPadding);
+ setListShown(true);
EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
- emptyView.setIcon(R.attr.av_download);
+ emptyView.setIcon(R.drawable.ic_download);
emptyView.setTitle(R.string.no_log_downloads_head_label);
emptyView.setMessage(R.string.no_log_downloads_label);
emptyView.attachToListView(getListView());
- adapter = new DownloadLogAdapter(getActivity(), itemAccess);
+ adapter = new DownloadLogAdapter(getActivity(), this);
setListAdapter(adapter);
EventBus.getDefault().register(this);
}
@@ -97,70 +102,64 @@ public class DownloadLogFragment extends ListFragment {
super.onDestroyView();
}
- private void onFragmentLoaded() {
- setListShown(true);
- adapter.notifyDataSetChanged();
- ((PagedToolbarFragment) getParentFragment()).invalidateOptionsMenuIfActive(this);
- }
-
@Override
public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) {
super.onListItemClick(l, v, position, id);
- DownloadStatus status = adapter.getItem(position);
- String url = "unknown";
- String message = getString(R.string.download_successful);
- if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
- if (media != null) {
- url = media.getDownload_url();
- }
- } else if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
- Feed feed = DBReader.getFeed(status.getFeedfileId());
- if (feed != null) {
- url = feed.getDownload_url();
- }
- }
-
- if (!status.isSuccessful()) {
- message = status.getReasonDetailed();
- }
-
- String messageFull = getString(R.string.download_error_details_message, message, url);
- AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
- builder.setTitle(R.string.download_error_details);
- builder.setMessage(messageFull);
- builder.setPositiveButton(android.R.string.ok, null);
- builder.setNeutralButton(R.string.copy_to_clipboard, (dialog, which) -> {
- ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText(getString(R.string.download_error_details), messageFull);
- clipboard.setPrimaryClip(clip);
- ((MainActivity) getActivity()).showSnackbarAbovePlayer(R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT);
- });
- Dialog dialog = builder.show();
- ((TextView) dialog.findViewById(android.R.id.message)).setTextIsSelectable(true);
- }
+ Object item = adapter.getItem(position);
+ if (item instanceof Downloader) {
+ DownloadRequest downloadRequest = ((Downloader) item).getDownloadRequest();
+ DownloadRequester.getInstance().cancelDownload(getActivity(), downloadRequest.getSource());
- private final DownloadLogAdapter.ItemAccess itemAccess = new DownloadLogAdapter.ItemAccess() {
+ if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
+ && UserPreferences.isEnableAutodownload()) {
+ FeedMedia media = DBReader.getFeedMedia(downloadRequest.getFeedfileId());
+ DBWriter.setFeedItemAutoDownload(media.getItem(), false);
- @Override
- public int getCount() {
- return downloadLog.size();
- }
+ ((MainActivity) getActivity()).showSnackbarAbovePlayer(
+ R.string.download_canceled_autodownload_enabled_msg, Toast.LENGTH_SHORT);
+ }
+ } else if (item instanceof DownloadStatus) {
+ DownloadStatus status = (DownloadStatus) item;
+ String url = "unknown";
+ String message = getString(R.string.download_successful);
+ if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
+ if (media != null) {
+ url = media.getDownload_url();
+ }
+ } else if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
+ Feed feed = DBReader.getFeed(status.getFeedfileId());
+ if (feed != null) {
+ url = feed.getDownload_url();
+ }
+ }
- @Override
- public DownloadStatus getItem(int position) {
- if (0 <= position && position < downloadLog.size()) {
- return downloadLog.get(position);
- } else {
- return null;
+ if (!status.isSuccessful()) {
+ message = status.getReasonDetailed();
}
+
+ String messageFull = getString(R.string.download_error_details_message, message, url);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setTitle(R.string.download_error_details);
+ builder.setMessage(messageFull);
+ builder.setPositiveButton(android.R.string.ok, null);
+ builder.setNeutralButton(R.string.copy_to_clipboard, (dialog, which) -> {
+ ClipboardManager clipboard = (ClipboardManager) getContext()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText(getString(R.string.download_error_details), messageFull);
+ clipboard.setPrimaryClip(clip);
+ ((MainActivity) getActivity()).showSnackbarAbovePlayer(
+ R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT);
+ });
+ Dialog dialog = builder.show();
+ ((TextView) dialog.findViewById(android.R.id.message)).setTextIsSelectable(true);
}
- };
+ }
@Subscribe
public void onDownloadLogChanged(DownloadLogEvent event) {
- loadItems();
+ loadDownloadLog();
}
@Override
@@ -172,20 +171,16 @@ public class DownloadLogFragment extends ListFragment {
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
- if (!super.onOptionsItemSelected(item)) {
- switch (item.getItemId()) {
- case R.id.clear_logs_item:
- DBWriter.clearDownloadLog();
- return true;
- case R.id.refresh_item:
- AutoUpdateManager.runImmediate(requireContext());
- return true;
- default:
- return false;
- }
- } else {
+ if (super.onOptionsItemSelected(item)) {
+ return true;
+ } else if (item.getItemId() == R.id.clear_logs_item) {
+ DBWriter.clearDownloadLog();
+ return true;
+ } else if (item.getItemId() == R.id.refresh_item) {
+ AutoUpdateManager.runImmediate(requireContext());
return true;
}
+ return false;
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
@@ -196,10 +191,18 @@ public class DownloadLogFragment extends ListFragment {
}
}
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
+ public void onEvent(DownloadEvent event) {
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ runningDownloads = update.downloaders;
+ adapter.setRunningDownloads(runningDownloads);
+ }
+
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
- private void loadItems() {
+ private void loadDownloadLog() {
if (disposable != null) {
disposable.dispose();
}
@@ -209,7 +212,8 @@ public class DownloadLogFragment extends ListFragment {
.subscribe(result -> {
if (result != null) {
downloadLog = result;
- onFragmentLoaded();
+ adapter.setDownloadLog(downloadLog);
+ ((PagedToolbarFragment) getParentFragment()).invalidateOptionsMenuIfActive(this);
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
index 5c83cee57..bc3884b37 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
@@ -31,10 +31,9 @@ public class DownloadsFragment extends PagedToolbarFragment {
private static final String PREF_LAST_TAB_POSITION = "tab_position";
private static final String KEY_UP_ARROW = "up_arrow";
- public static final int POS_RUNNING = 0;
- private static final int POS_COMPLETED = 1;
- public static final int POS_LOG = 2;
- private static final int TOTAL_COUNT = 3;
+ private static final int POS_COMPLETED = 0;
+ public static final int POS_LOG = 1;
+ private static final int TOTAL_COUNT = 2;
private ViewPager2 viewPager;
private TabLayout tabLayout;
@@ -64,9 +63,6 @@ public class DownloadsFragment extends PagedToolbarFragment {
tabLayout = root.findViewById(R.id.sliding_tabs);
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
switch (position) {
- case POS_RUNNING:
- tab.setText(R.string.downloads_running_label);
- break;
case POS_COMPLETED:
tab.setText(R.string.downloads_completed_label);
break;
@@ -121,8 +117,6 @@ public class DownloadsFragment extends PagedToolbarFragment {
@Override
public Fragment createFragment(int position) {
switch (position) {
- case POS_RUNNING:
- return new RunningDownloadsFragment();
case POS_COMPLETED:
return new CompletedDownloadsFragment();
default:
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 39f935bbe..b43ae97a4 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -40,7 +40,7 @@ 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.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -216,7 +216,7 @@ public abstract class EpisodesListFragment extends Fragment {
emptyView = new EmptyViewHandler(getContext());
emptyView.attachToRecyclerView(recyclerView);
- emptyView.setIcon(R.attr.feed);
+ emptyView.setIcon(R.drawable.ic_feed);
emptyView.setTitle(R.string.no_all_episodes_head_label);
emptyView.setMessage(R.string.no_all_episodes_label);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index 4efba9277..8e070738c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -6,7 +6,6 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -17,13 +16,15 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.event.ServiceEvent;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.view.PlayButton;
import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -40,7 +41,7 @@ public class ExternalPlayerFragment extends Fragment {
private ImageView imgvCover;
private TextView txtvTitle;
- private ImageButton butPlay;
+ private PlayButton butPlay;
private TextView feedName;
private ProgressBar progressBar;
private PlaybackController controller;
@@ -103,8 +104,8 @@ public class ExternalPlayerFragment extends Fragment {
}
@Override
- public ImageButton getPlayButton() {
- return butPlay;
+ protected void updatePlayButtonShowsPlay(boolean showPlay) {
+ butPlay.setIsShowPlay(showPlay);
}
@Override
@@ -113,11 +114,6 @@ public class ExternalPlayerFragment extends Fragment {
}
@Override
- public void onShutdownNotification() {
- ((MainActivity) getActivity()).setPlayerVisible(false);
- }
-
- @Override
public void onPlaybackEnd() {
((MainActivity) getActivity()).setPlayerVisible(false);
}
@@ -148,6 +144,13 @@ public class ExternalPlayerFragment extends Fragment {
onPositionObserverUpdate();
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlaybackServiceChanged(ServiceEvent event) {
+ if (event.action == ServiceEvent.Action.SERVICE_SHUT_DOWN) {
+ ((MainActivity) getActivity()).setPlayerVisible(false);
+ }
+ }
+
@Override
public void onDestroy() {
super.onDestroy();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
index e1fd36731..986c417fd 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -19,7 +19,7 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.FavoritesEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -55,7 +55,7 @@ public class FavoriteEpisodesFragment extends EpisodesListFragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = super.onCreateView(inflater, container, savedInstanceState);
- emptyView.setIcon(R.attr.ic_unfav);
+ emptyView.setIcon(R.drawable.ic_star);
emptyView.setTitle(R.string.no_fav_episodes_head_label);
emptyView.setMessage(R.string.no_fav_episodes_label);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
index 25ab925eb..da7e7e633 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
@@ -13,6 +13,7 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.AppCompatDrawableManager;
import androidx.appcompat.widget.Toolbar;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.Fragment;
@@ -33,10 +34,14 @@ import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.snackbar.Snackbar;
import com.joanzapata.iconify.Iconify;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedFunding;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -45,7 +50,6 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.StatisticsItem;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.fragment.preferences.StatisticsFragment;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
@@ -58,6 +62,8 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -80,6 +86,8 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
private TextView txtvPodcastTime;
private TextView txtvPodcastSpace;
private TextView txtvPodcastEpisodeCount;
+ private TextView txtvFundingUrl;
+ private TextView lblSupport;
private Button btnvOpenStatistics;
private TextView txtvUrl;
private TextView txtvAuthorHeader;
@@ -129,9 +137,9 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
@Override
protected void doTint(Context themedContext) {
toolbar.getMenu().findItem(R.id.visit_website_item)
- .setIcon(ThemeUtils.getDrawableFromAttr(themedContext, R.attr.location_web_site));
+ .setIcon(AppCompatDrawableManager.get().getDrawable(themedContext, R.drawable.ic_web));
toolbar.getMenu().findItem(R.id.share_parent)
- .setIcon(ThemeUtils.getDrawableFromAttr(themedContext, R.attr.ic_share));
+ .setIcon(AppCompatDrawableManager.get().getDrawable(themedContext, R.drawable.ic_share));
}
};
iconTintManager.updateTint();
@@ -155,6 +163,8 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
txtvPodcastTime = root.findViewById(R.id.txtvPodcastTime);
btnvOpenStatistics = root.findViewById(R.id.btnvOpenStatistics);
txtvUrl = root.findViewById(R.id.txtvUrl);
+ lblSupport = root.findViewById(R.id.lblSupport);
+ txtvFundingUrl = root.findViewById(R.id.txtvFundingUrl);
txtvUrl.setOnClickListener(copyUrlToClipboard);
@@ -222,6 +232,7 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
.into(imgvBackground);
txtvTitle.setText(feed.getTitle());
+ txtvTitle.setMaxLines(6);
String description = HtmlToPlainText.getPlainText(feed.getDescription());
@@ -232,6 +243,29 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
}
txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}");
+
+ if (feed.getPaymentLinks() == null || feed.getPaymentLinks().size() == 0) {
+ lblSupport.setVisibility(View.GONE);
+ txtvFundingUrl.setVisibility(View.GONE);
+ } else {
+ lblSupport.setVisibility(View.VISIBLE);
+ ArrayList<FeedFunding> fundingList = feed.getPaymentLinks();
+ StringBuilder str = new StringBuilder();
+ HashSet<String> seen = new HashSet<String>();
+ for (FeedFunding funding : fundingList) {
+ if (seen.contains(funding.url)) {
+ continue;
+ }
+ seen.add(funding.url);
+ str.append(funding.content.isEmpty()
+ ? getContext().getResources().getString(R.string.support_podcast)
+ : funding.content).append(" ").append(funding.url);
+ str.append("\n");
+ }
+ str = new StringBuilder(StringUtils.trim(str.toString()));
+ txtvFundingUrl.setText(str.toString());
+ }
+
Iconify.addIcons(txtvUrl);
refreshToolbarState();
}
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 c86fdc070..7aa4d5094 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -20,6 +20,7 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatDrawableManager;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
@@ -42,10 +43,10 @@ import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedItemFilter;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.service.download.DownloadService;
@@ -56,8 +57,6 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.FeedItemUtil;
-import de.danoeh.antennapod.core.util.Optional;
-import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.dialog.FilterDialog;
@@ -101,6 +100,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
private ImageView imgvCover;
private TextView txtvInformation;
private TextView txtvAuthor;
+ private TextView txtvUpdatesDisabled;
private ImageButton butShowInfo;
private ImageButton butShowSettings;
private View header;
@@ -167,6 +167,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
butShowSettings = root.findViewById(R.id.butShowSettings);
txtvInformation = root.findViewById(R.id.txtvInformation);
txtvFailure = root.findViewById(R.id.txtvFailure);
+ txtvUpdatesDisabled = root.findViewById(R.id.txtvUpdatesDisabled);
header = root.findViewById(R.id.headerContainer);
AppBarLayout appBar = root.findViewById(R.id.appBar);
CollapsingToolbarLayout collapsingToolbar = root.findViewById(R.id.collapsing_toolbar);
@@ -175,13 +176,13 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
@Override
protected void doTint(Context themedContext) {
toolbar.getMenu().findItem(R.id.sort_items)
- .setIcon(ThemeUtils.getDrawableFromAttr(themedContext, R.attr.ic_sort));
+ .setIcon(AppCompatDrawableManager.get().getDrawable(themedContext, R.drawable.ic_sort));
toolbar.getMenu().findItem(R.id.filter_items)
- .setIcon(ThemeUtils.getDrawableFromAttr(themedContext, R.attr.ic_filter));
+ .setIcon(AppCompatDrawableManager.get().getDrawable(themedContext, R.drawable.ic_filter));
toolbar.getMenu().findItem(R.id.refresh_item)
- .setIcon(ThemeUtils.getDrawableFromAttr(themedContext, R.attr.navigation_refresh));
+ .setIcon(AppCompatDrawableManager.get().getDrawable(themedContext, R.drawable.ic_refresh));
toolbar.getMenu().findItem(R.id.action_search)
- .setIcon(ThemeUtils.getDrawableFromAttr(themedContext, R.attr.action_search));
+ .setIcon(AppCompatDrawableManager.get().getDrawable(themedContext, R.drawable.ic_search));
}
};
iconTintManager.updateTint();
@@ -457,6 +458,13 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
} else {
txtvFailure.setVisibility(View.GONE);
}
+ if (!feed.getPreferences().getKeepUpdated()) {
+ txtvUpdatesDisabled.setText("{md-pause-circle-outline} " + this.getString(R.string.updates_disabled_label));
+ Iconify.addIcons(txtvUpdatesDisabled);
+ txtvUpdatesDisabled.setVisibility(View.VISIBLE);
+ } else {
+ txtvUpdatesDisabled.setVisibility(View.GONE);
+ }
txtvTitle.setText(feed.getTitle());
txtvAuthor.setText(feed.getAuthor());
if (feed.getItemFilter() != null) {
@@ -549,27 +557,32 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
disposable = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- feed = result.orElse(null);
- refreshHeaderView();
- displayList();
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ .subscribe(
+ result -> {
+ feed = result;
+ refreshHeaderView();
+ displayList();
+ }, error -> {
+ feed = null;
+ refreshHeaderView();
+ displayList();
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
- @NonNull
- private Optional<Feed> loadData() {
- Feed feed = DBReader.getFeed(feedID);
- if (feed != null && feed.getItemFilter() != null) {
- DBReader.loadAdditionalFeedItemListData(feed.getItems());
- FeedItemFilter filter = feed.getItemFilter();
- feed.setItems(filter.filter(feed.getItems()));
+ @Nullable
+ private Feed loadData() {
+ Feed feed = DBReader.getFeed(feedID, true);
+ if (feed == null) {
+ return null;
}
- if (feed != null && feed.getSortOrder() != null) {
+ DBReader.loadAdditionalFeedItemListData(feed.getItems());
+ if (feed.getSortOrder() != null) {
List<FeedItem> feedItems = feed.getItems();
FeedItemPermutors.getPermutor(feed.getSortOrder()).reorder(feedItems);
feed.setItems(feedItems);
}
- return Optional.ofNullable(feed);
+ return feed;
}
private static class FeedItemListAdapter extends EpisodeItemListAdapter {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
index e24c89478..9e57497bf 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
@@ -20,16 +20,17 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.settings.SkipIntroEndingChangedEvent;
import de.danoeh.antennapod.core.event.settings.SpeedPresetChangedEvent;
import de.danoeh.antennapod.core.event.settings.VolumeAdaptionChangedEvent;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedFilter;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
-import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedFilter;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.danoeh.antennapod.dialog.EpisodeFilterDialog;
import de.danoeh.antennapod.dialog.FeedPreferenceSkipDialog;
+import de.danoeh.antennapod.dialog.TagSettingsDialog;
import io.reactivex.Maybe;
import io.reactivex.MaybeOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -41,7 +42,7 @@ import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
-import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
+import static de.danoeh.antennapod.model.feed.FeedPreferences.SPEED_USE_GLOBAL;
public class FeedSettingsFragment extends Fragment {
private static final String TAG = "FeedSettingsFragment";
@@ -105,6 +106,7 @@ public class FeedSettingsFragment extends Fragment {
private static final CharSequence PREF_CATEGORY_AUTO_DOWNLOAD = "autoDownloadCategory";
private static final String PREF_FEED_PLAYBACK_SPEED = "feedPlaybackSpeed";
private static final String PREF_AUTO_SKIP = "feedAutoSkip";
+ private static final String PREF_TAGS = "tags";
private static final DecimalFormat SPEED_FORMAT =
new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.US));
@@ -160,6 +162,7 @@ public class FeedSettingsFragment extends Fragment {
setupPlaybackSpeedPreference();
setupFeedAutoSkipPreference();
setupEpisodeNotificationPreference();
+ setupTags();
updateAutoDeleteSummary();
updateVolumeReductionValue();
@@ -395,6 +398,13 @@ public class FeedSettingsFragment extends Fragment {
}
}
+ private void setupTags() {
+ findPreference(PREF_TAGS).setOnPreferenceClickListener(preference -> {
+ TagSettingsDialog.newInstance(feedPreferences).show(getChildFragmentManager(), TagSettingsDialog.TAG);
+ return true;
+ });
+ }
+
private void setupEpisodeNotificationPreference() {
SwitchPreferenceCompat pref = findPreference("episodeNotification");
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 8ac7b941a..518450f92 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -8,11 +8,15 @@ import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+
import androidx.fragment.app.Fragment;
+
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.view.ShownotesWebView;
import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -84,12 +88,19 @@ public class ItemDescriptionFragment extends Fragment {
}
webViewLoader = Maybe.<String>create(emitter -> {
Playable media = controller.getMedia();
- if (media != null) {
- Timeline timeline = new Timeline(getActivity(), media);
- emitter.onSuccess(timeline.processShownotes());
- } else {
+ if (media == null) {
emitter.onComplete();
+ return;
+ }
+ if (media instanceof FeedMedia) {
+ FeedMedia feedMedia = ((FeedMedia) media);
+ if (feedMedia.getItem() == null) {
+ feedMedia.setItem(DBReader.getFeedItem(feedMedia.getItemId()));
+ }
+ DBReader.loadDescriptionOfFeedItem(feedMedia.getItem());
}
+ Timeline timeline = new Timeline(getActivity(), media.getDescription(), media.getDuration());
+ emitter.onSuccess(timeline.processShownotes());
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@@ -134,14 +145,18 @@ public class ItemDescriptionFragment extends Fragment {
&& id.equals(controller.getMedia().getIdentifier().toString())
&& webvDescription != null) {
Log.d(TAG, "Restored scroll Position: " + scrollY);
- webvDescription.scrollTo(webvDescription.getScrollX(),
- scrollY);
+ webvDescription.scrollTo(webvDescription.getScrollX(), scrollY);
return true;
}
}
return false;
}
+ public void scrollToTop() {
+ webvDescription.scrollTo(0, 0);
+ savePreference();
+ }
+
@Override
public void onStart() {
super.onStart();
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 48eae9583..c83ed4722 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -6,7 +6,6 @@ import android.os.Bundle;
import android.text.Layout;
import android.text.TextUtils;
import android.util.Log;
-import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
@@ -46,8 +45,8 @@ import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UsageStatistics;
@@ -57,6 +56,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
@@ -337,7 +337,7 @@ public class ItemFragment extends Fragment {
txtvDuration.setContentDescription(
Converter.getDurationStringLocalized(getContext(), media.getDuration()));
}
- if (media.isCurrentlyPlaying()) {
+ if (FeedItemUtil.isCurrentlyPlaying(media)) {
actionButton1 = new PauseActionButton(item);
} else if (item.getFeed().isLocalFeed()) {
actionButton1 = new PlayLocalActionButton(item);
@@ -357,15 +357,12 @@ public class ItemFragment extends Fragment {
butAction1Text.setText(actionButton1.getLabel());
butAction1Text.setTransformationMethod(null);
- TypedValue typedValue = new TypedValue();
- getContext().getTheme().resolveAttribute(actionButton1.getDrawable(), typedValue, true);
- butAction1Icon.setImageResource(typedValue.resourceId);
+ butAction1Icon.setImageResource(actionButton1.getDrawable());
butAction1.setVisibility(actionButton1.getVisibility());
butAction2Text.setText(actionButton2.getLabel());
butAction2Text.setTransformationMethod(null);
- getContext().getTheme().resolveAttribute(actionButton2.getDrawable(), typedValue, true);
- butAction2Icon.setImageResource(typedValue.resourceId);
+ butAction2Icon.setImageResource(actionButton2.getDrawable());
butAction2.setVisibility(actionButton2.getVisibility());
}
@@ -439,7 +436,9 @@ public class ItemFragment extends Fragment {
FeedItem feedItem = DBReader.getFeedItem(itemId);
Context context = getContext();
if (feedItem != null && context != null) {
- Timeline t = new Timeline(context, feedItem);
+ int duration = feedItem.getMedia() != null ? feedItem.getMedia().getDuration() : Integer.MAX_VALUE;
+ DBReader.loadDescriptionOfFeedItem(feedItem);
+ Timeline t = new Timeline(context, feedItem.getDescription(), duration);
webviewData = t.processShownotes();
}
return feedItem;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
index 7b7a09082..d42300ca7 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
@@ -21,7 +21,7 @@ import org.greenrobot.eventbus.ThreadMode;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import io.reactivex.Observable;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java
index be74678d3..98ba59980 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java
@@ -12,15 +12,16 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ListView;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
+import androidx.core.util.Pair;
import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
@@ -30,10 +31,11 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.NavDrawerData;
import de.danoeh.antennapod.dialog.RemoveFeedDialog;
import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
@@ -46,12 +48,15 @@ import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
-public class NavDrawerFragment extends Fragment implements AdapterView.OnItemClickListener,
- AdapterView.OnItemLongClickListener, SharedPreferences.OnSharedPreferenceChangeListener {
+public class NavDrawerFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener {
@VisibleForTesting
public static final String PREF_LAST_FRAGMENT_TAG = "prefLastFragmentTag";
+ private static final String PREF_OPEN_FOLDERS = "prefOpenFolders";
@VisibleForTesting
public static final String PREF_NAME = "NavDrawerPrefs";
public static final String TAG = "NavDrawerFragment";
@@ -66,12 +71,13 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli
NavListAdapter.SUBSCRIPTION_LIST_TAG
};
- private DBReader.NavDrawerData navDrawerData;
- private int selectedNavListIndex = -1;
- private int position = -1;
+ private NavDrawerData navDrawerData;
+ private List<NavDrawerData.DrawerItem> flatItemList;
+ private NavDrawerData.DrawerItem contextPressedItem = null;
private NavListAdapter navAdapter;
private Disposable disposable;
private ProgressBar progressBar;
+ private Set<String> openFolders = new HashSet<>();
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@@ -79,40 +85,21 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.nav_list, container, false);
+ SharedPreferences preferences = getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+ openFolders = new HashSet<>(preferences.getStringSet(PREF_OPEN_FOLDERS, new HashSet<>())); // Must not modify
+
progressBar = root.findViewById(R.id.progressBar);
- ListView navList = root.findViewById(R.id.nav_list);
+ RecyclerView navList = root.findViewById(R.id.nav_list);
navAdapter = new NavListAdapter(itemAccess, getActivity());
+ navAdapter.setHasStableIds(true);
navList.setAdapter(navAdapter);
- navList.setOnItemClickListener(this);
- navList.setOnItemLongClickListener(this);
- registerForContextMenu(navList);
- updateSelection();
+ navList.setLayoutManager(new LinearLayoutManager(getContext()));
root.findViewById(R.id.nav_settings).setOnClickListener(v ->
startActivity(new Intent(getActivity(), PreferenceActivity.class)));
- getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
- .registerOnSharedPreferenceChangeListener(this);
- return root;
- }
- private void updateSelection() {
- String lastNavFragment = getLastNavFragment(getContext());
- int tagIndex = navAdapter.getTags().indexOf(lastNavFragment);
- if (tagIndex >= 0) {
- selectedNavListIndex = tagIndex;
- } else if (StringUtils.isNumeric(lastNavFragment)) { // last fragment was not a list, but a feed
- long feedId = Long.parseLong(lastNavFragment);
- if (navDrawerData != null) {
- List<Feed> feeds = navDrawerData.feeds;
- for (int i = 0; i < feeds.size(); i++) {
- if (feeds.get(i).getId() == feedId) {
- selectedNavListIndex = navAdapter.getSubscriptionOffset() + i;
- break;
- }
- }
- }
- }
- navAdapter.notifyDataSetChanged();
+ preferences.registerOnSharedPreferenceChangeListener(this);
+ return root;
}
@Override
@@ -135,29 +122,27 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli
@Override
public void onCreateContextMenu(@NonNull ContextMenu menu, @NonNull View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- if (v.getId() != R.id.nav_list) {
- return;
- }
- AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
- int position = adapterInfo.position;
- if (position < navAdapter.getSubscriptionOffset()) {
- return;
+ if (contextPressedItem.type != NavDrawerData.DrawerItem.Type.FEED) {
+ return; // Should actually never happen because the context menu is not set up for other items
}
+
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.nav_feed_context, menu);
- Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
- menu.setHeaderTitle(feed.getTitle());
+ menu.setHeaderTitle(((NavDrawerData.FeedDrawerItem) contextPressedItem).feed.getTitle());
// episodes are not loaded, so we cannot check if the podcast has new or unplayed ones!
}
@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
- final int position = this.position;
- this.position = -1; // reset
- if (position < 0) {
- return false;
+ NavDrawerData.DrawerItem pressedItem = contextPressedItem;
+ contextPressedItem = null;
+ if (pressedItem != null && pressedItem.type == NavDrawerData.DrawerItem.Type.FEED) {
+ return onFeedContextMenuClicked(((NavDrawerData.FeedDrawerItem) pressedItem).feed, item);
}
- Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
+ return false;
+ }
+
+ private boolean onFeedContextMenuClicked(Feed feed, MenuItem item) {
switch (item.getItemId()) {
case R.id.remove_all_new_flags_item:
ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(getContext(),
@@ -189,13 +174,7 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli
return true;
case R.id.remove_item:
RemoveFeedDialog.show(getContext(), feed, () -> {
- if (selectedNavListIndex == position) {
- if (getActivity() instanceof MainActivity) {
- ((MainActivity) getActivity()).loadFragment(EpisodesFragment.TAG, null);
- } else {
- showMainActivity(EpisodesFragment.TAG);
- }
- }
+ ((MainActivity) getActivity()).loadFragment(EpisodesFragment.TAG, null);
});
return true;
default:
@@ -203,12 +182,6 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli
}
}
- private void showMainActivity(String tag) {
- Intent intent = new Intent(getActivity(), MainActivity.class);
- intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, tag);
- startActivity(intent);
- }
-
@Subscribe(threadMode = ThreadMode.MAIN)
public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
loadData();
@@ -261,7 +234,7 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli
});
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
UserPreferences.setHiddenDrawerItems(hiddenDrawerItems);
- updateSelection();
+ navAdapter.notifyDataSetChanged(); // Update selection
});
builder.setNegativeButton(R.string.cancel_label, null);
builder.create().show();
@@ -270,25 +243,39 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli
private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() {
@Override
public int getCount() {
- if (navDrawerData != null) {
- return navDrawerData.feeds.size();
+ if (flatItemList != null) {
+ return flatItemList.size();
} else {
return 0;
}
}
@Override
- public Feed getItem(int position) {
- if (navDrawerData != null && 0 <= position && position < navDrawerData.feeds.size()) {
- return navDrawerData.feeds.get(position);
+ public NavDrawerData.DrawerItem getItem(int position) {
+ if (flatItemList != null && 0 <= position && position < flatItemList.size()) {
+ return flatItemList.get(position);
} else {
return null;
}
}
@Override
- public int getSelectedItemIndex() {
- return selectedNavListIndex;
+ public boolean isSelected(int position) {
+ String lastNavFragment = getLastNavFragment(getContext());
+ if (position < navAdapter.getSubscriptionOffset()) {
+ return navAdapter.getFragmentTags().get(position).equals(lastNavFragment);
+ } else if (StringUtils.isNumeric(lastNavFragment)) { // last fragment was not a list, but a feed
+ long feedId = Long.parseLong(lastNavFragment);
+ if (navDrawerData != null) {
+ NavDrawerData.DrawerItem itemToCheck = flatItemList.get(
+ position - navAdapter.getSubscriptionOffset());
+ if (itemToCheck.type == NavDrawerData.DrawerItem.Type.FEED) {
+ // When the same feed is displayed multiple times, it should be highlighted multiple times.
+ return ((NavDrawerData.FeedDrawerItem) itemToCheck).feed.getId() == feedId;
+ }
+ }
+ }
+ return false;
}
@Override
@@ -312,11 +299,6 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli
}
@Override
- public int getFeedCounter(long feedId) {
- return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0;
- }
-
- @Override
public int getFeedCounterSum() {
if (navDrawerData == null) {
return 0;
@@ -328,16 +310,81 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli
return sum;
}
+ @Override
+ public void onItemClick(int position) {
+ int viewType = navAdapter.getItemViewType(position);
+ if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) {
+ if (position < navAdapter.getSubscriptionOffset()) {
+ String tag = navAdapter.getFragmentTags().get(position);
+ ((MainActivity) getActivity()).loadFragment(tag, null);
+ ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
+ } else {
+ int pos = position - navAdapter.getSubscriptionOffset();
+ NavDrawerData.DrawerItem clickedItem = flatItemList.get(pos);
+
+ if (clickedItem.type == NavDrawerData.DrawerItem.Type.FEED) {
+ long feedId = ((NavDrawerData.FeedDrawerItem) clickedItem).feed.getId();
+ ((MainActivity) getActivity()).loadFeedFragmentById(feedId, null);
+ ((MainActivity) getActivity()).getBottomSheet()
+ .setState(BottomSheetBehavior.STATE_COLLAPSED);
+ } else {
+ NavDrawerData.FolderDrawerItem folder = ((NavDrawerData.FolderDrawerItem) clickedItem);
+ if (openFolders.contains(folder.name)) {
+ openFolders.remove(folder.name);
+ } else {
+ openFolders.add(folder.name);
+ }
+
+ getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
+ .edit()
+ .putStringSet(PREF_OPEN_FOLDERS, openFolders)
+ .apply();
+
+ disposable = Observable.fromCallable(() -> makeFlatDrawerData(navDrawerData.items, 0))
+ .subscribeOn(Schedulers.computation())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ result -> {
+ flatItemList = result;
+ navAdapter.notifyDataSetChanged();
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+ }
+ } else if (UserPreferences.getSubscriptionsFilter().isEnabled()
+ && navAdapter.showSubscriptionList) {
+ SubscriptionsFilterDialog.showDialog(requireContext());
+ }
+ }
+
+ @Override
+ public boolean onItemLongClick(int position) {
+ if (position < navAdapter.getFragmentTags().size()) {
+ showDrawerPreferencesDialog();
+ return true;
+ } else {
+ contextPressedItem = flatItemList.get(position - navAdapter.getSubscriptionOffset());
+ return false;
+ }
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ NavDrawerFragment.this.onCreateContextMenu(menu, v, menuInfo);
+ }
};
private void loadData() {
- disposable = Observable.fromCallable(DBReader::getNavDrawerData)
+ disposable = Observable.fromCallable(
+ () -> {
+ NavDrawerData data = DBReader.getNavDrawerData();
+ return new Pair<>(data, makeFlatDrawerData(data.items, 0));
+ })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
result -> {
- navDrawerData = result;
- updateSelection(); // Selected item might be a feed
+ navDrawerData = result.first;
+ flatItemList = result.second;
navAdapter.notifyDataSetChanged();
progressBar.setVisibility(View.GONE); // Stays hidden once there is something in the list
}, error -> {
@@ -346,45 +393,20 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli
});
}
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- int viewType = parent.getAdapter().getItemViewType(position);
- if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) {
- if (position < navAdapter.getSubscriptionOffset()) {
- String tag = navAdapter.getTags().get(position);
- if (getActivity() instanceof MainActivity) {
- ((MainActivity) getActivity()).loadFragment(tag, null);
- ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
- } else {
- showMainActivity(tag);
- }
- } else {
- int pos = position - navAdapter.getSubscriptionOffset();
- long feedId = navDrawerData.feeds.get(pos).getId();
- if (getActivity() instanceof MainActivity) {
- ((MainActivity) getActivity()).loadFeedFragmentById(feedId, null);
- ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
- } else {
- Intent intent = new Intent(getActivity(), MainActivity.class);
- intent.putExtra(MainActivity.EXTRA_FEED_ID, feedId);
- startActivity(intent);
+ private List<NavDrawerData.DrawerItem> makeFlatDrawerData(List<NavDrawerData.DrawerItem> items, int layer) {
+ List<NavDrawerData.DrawerItem> flatItems = new ArrayList<>();
+ for (NavDrawerData.DrawerItem item : items) {
+ item.setLayer(layer);
+ flatItems.add(item);
+ if (item.type == NavDrawerData.DrawerItem.Type.FOLDER) {
+ NavDrawerData.FolderDrawerItem folder = ((NavDrawerData.FolderDrawerItem) item);
+ folder.isOpen = openFolders.contains(folder.name);
+ if (folder.isOpen) {
+ flatItems.addAll(makeFlatDrawerData(((NavDrawerData.FolderDrawerItem) item).children, layer + 1));
}
}
- } else if (UserPreferences.getSubscriptionsFilter().isEnabled()
- && navAdapter.showSubscriptionList) {
- SubscriptionsFilterDialog.showDialog(requireContext());
- }
- }
-
- @Override
- public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
- if (position < navAdapter.getTags().size()) {
- showDrawerPreferencesDialog();
- return true;
- } else {
- this.position = position;
- return false;
}
+ return flatItems;
}
public static void saveLastNavFragment(Context context, String tag) {
@@ -409,8 +431,7 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (PREF_LAST_FRAGMENT_TAG.equals(key)) {
- updateSelection();
- navAdapter.notifyDataSetChanged();
+ navAdapter.notifyDataSetChanged(); // Update selection
}
}
}
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 1aa66dcbb..44b82ee19 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -12,7 +12,7 @@ import android.view.ViewGroup;
import java.util.List;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PagedToolbarFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PagedToolbarFragment.java
index 2ed26b1c0..f79bffabc 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PagedToolbarFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PagedToolbarFragment.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.fragment;
+import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.widget.ViewPager2;
@@ -14,12 +15,12 @@ public abstract class PagedToolbarFragment extends Fragment {
/**
* Invalidate the toolbar menu if the current child fragment is visible.
- * @param child The fragment, or null to force-refresh whatever the active fragment is.
+ * @param child The fragment to invalidate
*/
- void invalidateOptionsMenuIfActive(Fragment child) {
+ void invalidateOptionsMenuIfActive(@NonNull Fragment child) {
Fragment visibleChild = getChildFragmentManager().findFragmentByTag("f" + viewPager.getCurrentItem());
- if (visibleChild == child || child == null) {
- child.onPrepareOptionsMenu(toolbar.getMenu());
+ if (visibleChild == child) {
+ visibleChild.onPrepareOptionsMenu(toolbar.getMenu());
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index e97b7cd7f..c067af4e4 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -21,7 +21,7 @@ import de.danoeh.antennapod.core.event.PlaybackHistoryEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
@@ -81,7 +81,7 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI
progressBar = root.findViewById(R.id.progLoading);
emptyView = new EmptyViewHandler(getActivity());
- emptyView.setIcon(R.attr.ic_history);
+ emptyView.setIcon(R.drawable.ic_history);
emptyView.setTitle(R.string.no_history_head_label);
emptyView.setMessage(R.string.no_history_label);
emptyView.attachToRecyclerView(recyclerView);
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 b92043c7d..1fda42d41 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -4,6 +4,8 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
@@ -19,6 +21,8 @@ import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
@@ -31,7 +35,7 @@ import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
@@ -40,7 +44,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
-import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
@@ -242,7 +246,6 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
private void refreshToolbarState() {
MenuItemUtils.refreshLockItem(getActivity(), toolbar.getMenu());
boolean keepSorted = UserPreferences.isQueueKeepSorted();
- toolbar.getMenu().findItem(R.id.queue_lock).setVisible(!keepSorted);
toolbar.getMenu().findItem(R.id.queue_sort_random).setVisible(!keepSorted);
toolbar.getMenu().findItem(R.id.queue_keep_sorted).setChecked(keepSorted);
@@ -441,6 +444,13 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool());
registerForContextMenu(recyclerView);
+ SwipeRefreshLayout swipeRefreshLayout = root.findViewById(R.id.swipeRefresh);
+ swipeRefreshLayout.setOnRefreshListener(() -> {
+ AutoUpdateManager.runImmediate(requireContext());
+ new Handler(Looper.getMainLooper()).postDelayed(() -> swipeRefreshLayout.setRefreshing(false),
+ getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms));
+ });
+
itemTouchHelper = new ItemTouchHelper(
new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@@ -480,19 +490,13 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
final int position = viewHolder.getAdapterPosition();
Log.d(TAG, "remove(" + position + ")");
final FeedItem item = queue.get(position);
- final boolean isRead = item.isPlayed();
- DBWriter.markItemPlayed(FeedItem.PLAYED, false, item.getId());
DBWriter.removeQueueItem(getActivity(), true, item);
((MainActivity) getActivity()).showSnackbarAbovePlayer(
- item.hasMedia() ? R.string.marked_as_read_label : R.string.marked_as_read_no_media_label,
+ getResources().getQuantityString(R.plurals.removed_from_queue_batch_label, 1, 1),
Snackbar.LENGTH_LONG)
- .setAction(getString(R.string.undo), v -> {
- DBWriter.addQueueItemAt(getActivity(), item.getId(), position, false);
- if (!isRead) {
- DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
- }
- });
+ .setAction(getString(R.string.undo), v ->
+ DBWriter.addQueueItemAt(getActivity(), item.getId(), position, false));
}
@Override
@@ -527,7 +531,7 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
emptyView = new EmptyViewHandler(getContext());
emptyView.attachToRecyclerView(recyclerView);
- emptyView.setIcon(R.attr.stat_playlist);
+ emptyView.setIcon(R.drawable.ic_playlist);
emptyView.setTitle(R.string.no_items_header_label);
emptyView.setMessage(R.string.no_items_label);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
deleted file mode 100644
index fc500a223..000000000
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package de.danoeh.antennapod.fragment;
-
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.fragment.app.ListFragment;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ListView;
-import android.widget.Toast;
-
-import de.danoeh.antennapod.activity.MainActivity;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.adapter.DownloadlistAdapter;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.event.DownloaderUpdate;
-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.DBWriter;
-import de.danoeh.antennapod.core.storage.DownloadRequester;
-import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
-import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-import de.danoeh.antennapod.view.EmptyViewHandler;
-import org.greenrobot.eventbus.ThreadMode;
-
-/**
- * Displays all running downloads and provides actions to cancel them
- */
-public class RunningDownloadsFragment extends ListFragment {
-
- private static final String TAG = "RunningDownloadsFrag";
-
- private DownloadlistAdapter adapter;
- private List<Downloader> downloaderList = new ArrayList<>();
-
- private boolean isUpdatingFeeds = false;
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- // add padding
- final ListView lv = getListView();
- lv.setClipToPadding(false);
- final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
- lv.setPadding(0, vertPadding, 0, vertPadding);
-
- adapter = new DownloadlistAdapter(getActivity(), itemAccess);
- setListAdapter(adapter);
-
- EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
- emptyView.setIcon(R.attr.av_download);
- emptyView.setTitle(R.string.no_run_downloads_head_label);
- emptyView.setMessage(R.string.no_run_downloads_label);
- emptyView.attachToListView(getListView());
-
- }
-
- @Override
- public void onStart() {
- super.onStart();
- EventBus.getDefault().register(this);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- EventBus.getDefault().unregister(this);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- setListAdapter(null);
- }
-
- @Override
- public void onPrepareOptionsMenu(@NonNull Menu menu) {
- menu.findItem(R.id.clear_logs_item).setVisible(false);
- menu.findItem(R.id.episode_actions).setVisible(false);
- isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == R.id.refresh_item) {
- AutoUpdateManager.runImmediate(requireContext());
- return true;
- }
- return false;
- }
-
- @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
- ((PagedToolbarFragment) getParentFragment()).invalidateOptionsMenuIfActive(this);
- }
- }
-
- private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
- () -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
-
- @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEvent(DownloadEvent event) {
- Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
- DownloaderUpdate update = event.update;
- downloaderList = update.downloaders;
- adapter.notifyDataSetChanged();
- }
-
- private final DownloadlistAdapter.ItemAccess itemAccess = new DownloadlistAdapter.ItemAccess() {
- @Override
- public int getCount() {
- return downloaderList.size();
- }
-
- @Override
- public Downloader getItem(int position) {
- if (0 <= position && position < downloaderList.size()) {
- return downloaderList.get(position);
- } else {
- return null;
- }
- }
-
- @Override
- public void onSecondaryActionClick(Downloader downloader) {
- DownloadRequest downloadRequest = downloader.getDownloadRequest();
- DownloadRequester.getInstance().cancelDownload(getActivity(), downloadRequest.getSource());
-
- if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
- && UserPreferences.isEnableAutodownload()) {
- FeedMedia media = DBReader.getFeedMedia(downloadRequest.getFeedfileId());
- DBWriter.setFeedItemAutoDownload(media.getItem(), false);
-
- ((MainActivity) getActivity()).showSnackbarAbovePlayer(
- R.string.download_canceled_autodownload_enabled_msg, Toast.LENGTH_SHORT);
- }
- }
- };
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index e791da1c6..c4fa0a3b1 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -28,8 +28,8 @@ import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.FeedSearcher;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
@@ -133,7 +133,7 @@ public class SearchFragment extends Fragment {
emptyViewHandler = new EmptyViewHandler(getContext());
emptyViewHandler.attachToRecyclerView(recyclerView);
- emptyViewHandler.setIcon(R.attr.action_search);
+ emptyViewHandler.setIcon(R.drawable.ic_search);
emptyViewHandler.setTitle(R.string.search_status_no_results);
EventBus.getDefault().register(this);
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 9bbc03fba..367eb4aaf 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -28,6 +28,7 @@ import android.widget.TextView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.joanzapata.iconify.Iconify;
+import java.util.List;
import java.util.Locale;
import java.util.concurrent.Callable;
@@ -38,12 +39,13 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.storage.NavDrawerData;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.RemoveFeedDialog;
import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog;
@@ -68,6 +70,7 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
private static final String PREFS = "SubscriptionFragment";
private static final String PREF_NUM_COLUMNS = "columns";
private static final String KEY_UP_ARROW = "up_arrow";
+ private static final String ARGUMENT_FOLDER = "folder";
private static final int MIN_NUM_COLUMNS = 2;
private static final int[] COLUMN_CHECKBOX_IDS = {
@@ -77,21 +80,30 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
R.id.subscription_num_columns_5};
private GridView subscriptionGridLayout;
- private DBReader.NavDrawerData navDrawerData;
+ private List<NavDrawerData.DrawerItem> listItems;
private SubscriptionsAdapter subscriptionAdapter;
private FloatingActionButton subscriptionAddButton;
private ProgressBar progressBar;
private EmptyViewHandler emptyView;
private TextView feedsFilteredMsg;
private Toolbar toolbar;
+ private String displayedFolder = null;
- private int mPosition = -1;
+ private Feed selectedFeed = null;
private boolean isUpdatingFeeds = false;
private boolean displayUpArrow;
private Disposable disposable;
private SharedPreferences prefs;
+ public static SubscriptionFragment newInstance(String folderTitle) {
+ SubscriptionFragment fragment = new SubscriptionFragment();
+ Bundle args = new Bundle();
+ args.putString(ARGUMENT_FOLDER, folderTitle);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -119,6 +131,13 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
}
refreshToolbarState();
+ if (getArguments() != null) {
+ displayedFolder = getArguments().getString(ARGUMENT_FOLDER, null);
+ if (displayedFolder != null) {
+ toolbar.setTitle(displayedFolder);
+ }
+ }
+
subscriptionGridLayout = root.findViewById(R.id.subscriptions_grid);
subscriptionGridLayout.setNumColumns(prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns()));
registerForContextMenu(subscriptionGridLayout);
@@ -188,7 +207,7 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
private void setupEmptyView() {
emptyView = new EmptyViewHandler(getContext());
- emptyView.setIcon(R.attr.ic_folder);
+ emptyView.setIcon(R.drawable.ic_folder);
emptyView.setTitle(R.string.no_subscriptions_head_label);
emptyView.setMessage(R.string.no_subscriptions_label);
emptyView.attachToListView(subscriptionGridLayout);
@@ -231,12 +250,23 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
disposable.dispose();
}
emptyView.hide();
- disposable = Observable.fromCallable(DBReader::getNavDrawerData)
+ disposable = Observable.fromCallable(
+ () -> {
+ NavDrawerData data = DBReader.getNavDrawerData();
+ List<NavDrawerData.DrawerItem> items = data.items;
+ for (NavDrawerData.DrawerItem item : items) {
+ if (item.type == NavDrawerData.DrawerItem.Type.FOLDER
+ && item.getTitle().equals(displayedFolder)) {
+ return ((NavDrawerData.FolderDrawerItem) item).children;
+ }
+ }
+ return items;
+ })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
result -> {
- navDrawerData = result;
+ listItems = result;
subscriptionAdapter.notifyDataSetChanged();
emptyView.updateVisibility();
progressBar.setVisibility(View.GONE); // Keep hidden to avoid flickering while refreshing
@@ -261,40 +291,30 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
- int position = adapterInfo.position;
-
- Object selectedObject = subscriptionAdapter.getItem(position);
- if (selectedObject.equals(SubscriptionsAdapter.ADD_ITEM_OBJ)) {
- mPosition = position;
+ if (menuInfo == null) {
return;
}
+ AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ int position = adapterInfo.position;
- Feed feed = (Feed) selectedObject;
-
- MenuInflater inflater = requireActivity().getMenuInflater();
- inflater.inflate(R.menu.nav_feed_context, menu);
-
- menu.setHeaderTitle(feed.getTitle());
+ NavDrawerData.DrawerItem selectedObject = (NavDrawerData.DrawerItem) subscriptionAdapter.getItem(position);
- mPosition = position;
+ if (selectedObject.type == NavDrawerData.DrawerItem.Type.FEED) {
+ MenuInflater inflater = requireActivity().getMenuInflater();
+ inflater.inflate(R.menu.nav_feed_context, menu);
+ selectedFeed = ((NavDrawerData.FeedDrawerItem) selectedObject).feed;
+ }
+ menu.setHeaderTitle(selectedObject.getTitle());
}
@Override
public boolean onContextItemSelected(MenuItem item) {
- final int position = mPosition;
- mPosition = -1; // reset
- if (position < 0) {
+ if (selectedFeed == null) {
return false;
}
- Object selectedObject = subscriptionAdapter.getItem(position);
- if (selectedObject.equals(SubscriptionsAdapter.ADD_ITEM_OBJ)) {
- // this is the add object, do nothing
- return false;
- }
-
- Feed feed = (Feed) selectedObject;
+ Feed feed = selectedFeed;
+ selectedFeed = null;
switch (item.getItemId()) {
case R.id.remove_all_new_flags_item:
displayConfirmationDialog(
@@ -359,25 +379,20 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
private final SubscriptionsAdapter.ItemAccess itemAccess = new SubscriptionsAdapter.ItemAccess() {
@Override
public int getCount() {
- if (navDrawerData != null) {
- return navDrawerData.feeds.size();
+ if (listItems != null) {
+ return listItems.size();
} else {
return 0;
}
}
@Override
- public Feed getItem(int position) {
- if (navDrawerData != null && 0 <= position && position < navDrawerData.feeds.size()) {
- return navDrawerData.feeds.get(position);
+ public NavDrawerData.DrawerItem getItem(int position) {
+ if (listItems != null && 0 <= position && position < listItems.size()) {
+ return listItems.get(position);
} else {
return null;
}
}
-
- @Override
- public int getFeedCounter(long feedId) {
- return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0;
- }
};
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
index 7ee0936d0..c813cbf7a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
@@ -17,9 +17,9 @@ import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.adapter.gpodnet.PodcastListAdapter;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetPodcast;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetServiceException;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetPodcast;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -76,7 +76,8 @@ public abstract class PodcastListFragment extends Fragment {
disposable = Observable.fromCallable(
() -> {
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
- GpodnetPreferences.getHosturl());
+ GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(),
+ GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
return loadPodcastData(service);
})
.subscribeOn(Schedulers.io())
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java
index 64261493d..f51ab032f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.fragment.gpodnet;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetPodcast;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetServiceException;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetPodcast;
import java.util.List;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SuggestionListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SuggestionListFragment.java
index 41b99cdfc..e3cdb8959 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SuggestionListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SuggestionListFragment.java
@@ -4,9 +4,9 @@ import java.util.Collections;
import java.util.List;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetPodcast;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetServiceException;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetPodcast;
/**
* Displays suggestions from gpodder.net
@@ -17,7 +17,7 @@ public class SuggestionListFragment extends PodcastListFragment {
@Override
protected List<GpodnetPodcast> loadPodcastData(GpodnetService service) throws GpodnetServiceException {
if (GpodnetPreferences.loggedIn()) {
- service.authenticate(GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
+ service.login();
return service.getSuggestions(SUGGESTIONS_COUNT);
} else {
return Collections.emptyList();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
index b60f1bed2..62e2e30d1 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
@@ -1,15 +1,14 @@
package de.danoeh.antennapod.fragment.gpodnet;
import android.os.Bundle;
-
-import org.apache.commons.lang3.Validate;
+import androidx.annotation.NonNull;
import java.util.List;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetPodcast;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetTag;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetServiceException;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetPodcast;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetTag;
/**
* Shows all podcasts from gpodder.net that belong to a specific tag.
@@ -22,8 +21,7 @@ public class TagFragment extends PodcastListFragment {
private GpodnetTag tag;
- public static TagFragment newInstance(GpodnetTag tag) {
- Validate.notNull(tag);
+ public static TagFragment newInstance(@NonNull GpodnetTag tag) {
TagFragment fragment = new TagFragment();
Bundle args = new Bundle();
args.putParcelable("tag", tag);
@@ -36,7 +34,9 @@ public class TagFragment extends PodcastListFragment {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
- Validate.isTrue(args != null && args.getParcelable("tag") != null, "args invalid");
+ if (args == null || args.getParcelable("tag") == null) {
+ throw new IllegalArgumentException("Arguments not given");
+ }
tag = args.getParcelable("tag");
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
index 9d0f99aa9..f961e30bb 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
@@ -10,8 +10,8 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.gpodnet.TagListAdapter;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetTag;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetTag;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -51,7 +51,8 @@ public class TagListFragment extends ListFragment {
disposable = Observable.fromCallable(
() -> {
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
- GpodnetPreferences.getHosturl());
+ GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(),
+ GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
return service.getTopTags(COUNT);
})
.subscribeOn(Schedulers.io())
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java
index 6eb19aff2..c0bf3e0ea 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java
@@ -25,8 +25,8 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.sync.SyncService;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetDevice;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetDevice;
import de.danoeh.antennapod.core.util.FileNameGenerator;
import de.danoeh.antennapod.core.util.IntentUtils;
import io.reactivex.Completable;
@@ -96,7 +96,9 @@ public class GpodderAuthenticationFragment extends DialogFragment {
} else {
GpodnetPreferences.setHosturl(GpodnetService.DEFAULT_BASE_HOST);
}
- service = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetPreferences.getHosturl());
+ service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
+ GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(),
+ GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
getDialog().setTitle(GpodnetPreferences.getHosturl());
advance();
});
@@ -138,7 +140,8 @@ public class GpodderAuthenticationFragment extends DialogFragment {
inputManager.hideSoftInputFromWindow(login.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
Completable.fromAction(() -> {
- service.authenticate(usernameStr, passwordStr);
+ service.setCredentials(usernameStr, passwordStr);
+ service.login();
devices = service.getDevices();
GpodderAuthenticationFragment.this.username = usernameStr;
GpodderAuthenticationFragment.this.password = passwordStr;
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 7bf602e35..baf4c7c57 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,14 +1,15 @@
package de.danoeh.antennapod.fragment.preferences;
import android.content.Intent;
-import android.os.Build;
import android.os.Bundle;
-import android.provider.Settings;
+
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
+
import com.bytehamster.lib.preferencesearch.SearchConfiguration;
import com.bytehamster.lib.preferencesearch.SearchPreference;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.BugReportActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
@@ -30,6 +31,7 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
private static final String STATISTICS = "statistics";
private static final String PREF_ABOUT = "prefAbout";
private static final String PREF_NOTIFICATION = "notifications";
+ private static final String PREF_CONTRIBUTE = "prefContribute";
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -81,26 +83,21 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
return true;
});
findPreference(PREF_NOTIFICATION).setOnPreferenceClickListener(preference -> {
- if (Build.VERSION.SDK_INT >= 26) {
- Intent intent = new Intent();
- intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
- intent.putExtra(Settings.EXTRA_APP_PACKAGE, getActivity().getPackageName());
- startActivity(intent);
- } else {
- ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_notifications);
- }
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_notifications);
return true;
});
findPreference(PREF_ABOUT).setOnPreferenceClickListener(
preference -> {
- getParentFragmentManager().beginTransaction().replace(R.id.content, new AboutFragment())
+ getParentFragmentManager().beginTransaction()
+ .replace(R.id.settingsContainer, new AboutFragment())
.addToBackStack(getString(R.string.about_pref)).commit();
return true;
}
);
findPreference(STATISTICS).setOnPreferenceClickListener(
preference -> {
- getParentFragmentManager().beginTransaction().replace(R.id.content, new StatisticsFragment())
+ getParentFragmentManager().beginTransaction()
+ .replace(R.id.settingsContainer, new StatisticsFragment())
.addToBackStack(getString(R.string.statistics_label)).commit();
return true;
}
@@ -113,6 +110,10 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
IntentUtils.openInBrowser(getContext(), "https://forum.antennapod.org/");
return true;
});
+ findPreference(PREF_CONTRIBUTE).setOnPreferenceClickListener(preference -> {
+ IntentUtils.openInBrowser(getContext(), "https://antennapod.org/contribute/");
+ return true;
+ });
findPreference(PREF_SEND_BUG_REPORT).setOnPreferenceClickListener(preference -> {
startActivity(new Intent(getActivity(), BugReportActivity.class));
return true;
@@ -123,7 +124,7 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
SearchPreference searchPreference = findPreference("searchPreference");
SearchConfiguration config = searchPreference.getSearchConfiguration();
config.setActivity((AppCompatActivity) getActivity());
- config.setFragmentContainerViewId(R.id.content);
+ config.setFragmentContainerViewId(R.id.settingsContainer);
config.setBreadcrumbsEnabled(true);
config.index(R.xml.preferences_user_interface)
@@ -145,5 +146,7 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
.addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_gpodder));
config.index(R.xml.preferences_notifications)
.addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_notifications));
+ config.index(R.xml.feed_settings)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.feed_settings));
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
index 3889034fa..305e495e8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
@@ -1,23 +1,25 @@
package de.danoeh.antennapod.fragment.preferences;
-import android.app.TimePickerDialog;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
-import androidx.appcompat.app.AlertDialog;
-import androidx.preference.PreferenceFragmentCompat;
import android.text.format.DateFormat;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceManager;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.dialog.FeedRefreshIntervalDialog;
import de.danoeh.antennapod.dialog.ProxyDialog;
-import org.apache.commons.lang3.ArrayUtils;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.concurrent.TimeUnit;
-public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
+
+public class NetworkPreferencesFragment extends PreferenceFragmentCompat
+ implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String PREF_SCREEN_AUTODL = "prefAutoDownloadSettings";
private static final String PREF_PROXY = "prefProxy";
@@ -31,6 +33,13 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
public void onStart() {
super.onStart();
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.network_pref);
+ PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ PreferenceManager.getDefaultSharedPreferences(getContext()).unregisterOnSharedPreferenceChangeListener(this);
}
@Override
@@ -47,9 +56,10 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
});
findPreference(UserPreferences.PREF_UPDATE_INTERVAL)
.setOnPreferenceClickListener(preference -> {
- showUpdateIntervalTimePreferencesDialog();
+ new FeedRefreshIntervalDialog(getContext()).show();
return true;
});
+
findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)
.setOnPreferenceChangeListener(
(preference, o) -> {
@@ -67,6 +77,9 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
});
}
+ /**
+ * Used to init and handle changes to view
+ */
private void setUpdateIntervalText() {
Context context = getActivity().getApplicationContext();
String val;
@@ -74,7 +87,7 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
if (interval > 0) {
int hours = (int) TimeUnit.MILLISECONDS.toHours(interval);
val = context.getResources().getQuantityString(
- R.plurals.pref_autoUpdateIntervallOrTime_every_hours, hours, hours);
+ R.plurals.feed_refresh_every_x_hours, hours, hours);
} else {
int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
if (timeOfDay.length == 2) {
@@ -82,94 +95,29 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
cal.set(Calendar.HOUR_OF_DAY, timeOfDay[0]);
cal.set(Calendar.MINUTE, timeOfDay[1]);
String timeOfDayStr = DateFormat.getTimeFormat(context).format(cal.getTime());
- val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_at),
+ val = String.format(context.getString(R.string.feed_refresh_interval_at),
timeOfDayStr);
} else {
- val = context.getString(R.string.pref_smart_mark_as_played_disabled); // TODO: Is this a bug? Otherwise document why is this related to smart mark???
+ val = context.getString(R.string.feed_refresh_never);
}
}
- String summary = context.getString(R.string.pref_autoUpdateIntervallOrTime_sum) + "\n"
+ String summary = context.getString(R.string.feed_refresh_sum) + "\n"
+ String.format(context.getString(R.string.pref_current_value), val);
findPreference(UserPreferences.PREF_UPDATE_INTERVAL).setSummary(summary);
}
private void setParallelDownloadsText(int downloads) {
final Resources res = getActivity().getResources();
-
String s = res.getString(R.string.parallel_downloads, downloads);
findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS).setSummary(s);
}
- private void showUpdateIntervalTimePreferencesDialog() {
- final Context context = getActivity();
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.pref_autoUpdateIntervallOrTime_title);
- builder.setMessage(R.string.pref_autoUpdateIntervallOrTime_message);
- builder.setPositiveButton(R.string.pref_autoUpdateIntervallOrTime_Interval, (dialog, which) -> {
- AlertDialog.Builder builder1 = new AlertDialog.Builder(context);
- builder1.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
- final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
- final String[] entries = getUpdateIntervalEntries(values);
- long currInterval = UserPreferences.getUpdateInterval();
- int checkedItem = -1;
- if(currInterval > 0) {
- String currIntervalStr = String.valueOf(TimeUnit.MILLISECONDS.toHours(currInterval));
- checkedItem = ArrayUtils.indexOf(values, currIntervalStr);
- }
- builder1.setSingleChoiceItems(entries, checkedItem, (dialog1, which1) -> {
- int hours = Integer.parseInt(values[which1]);
- UserPreferences.setUpdateInterval(hours);
- dialog1.dismiss();
- setUpdateIntervalText();
- });
- builder1.setNegativeButton(context.getString(R.string.cancel_label), null);
- builder1.show();
- });
- builder.setNegativeButton(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay, (dialog, which) -> {
- int hourOfDay = 7;
- int minute = 0;
- int[] updateTime = UserPreferences.getUpdateTimeOfDay();
- if (updateTime.length == 2) {
- hourOfDay = updateTime[0];
- minute = updateTime[1];
- }
- TimePickerDialog timePickerDialog = new TimePickerDialog(context,
- (view, selectedHourOfDay, selectedMinute) -> {
- if (view.getTag() == null) { // onTimeSet() may get called twice!
- view.setTag("TAGGED");
- UserPreferences.setUpdateTimeOfDay(selectedHourOfDay, selectedMinute);
- setUpdateIntervalText();
- }
- }, hourOfDay, minute, DateFormat.is24HourFormat(context));
- timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
- timePickerDialog.show();
- });
- builder.setNeutralButton(R.string.pref_autoUpdateIntervallOrTime_Disable, (dialog, which) -> {
- UserPreferences.disableAutoUpdate(context);
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (UserPreferences.PREF_UPDATE_INTERVAL.equals(key)) {
setUpdateIntervalText();
- });
- builder.show();
- }
-
- private String[] getUpdateIntervalEntries(final String[] values) {
- final Resources res = getActivity().getResources();
- String[] entries = new String[values.length];
- for (int x = 0; x < values.length; x++) {
- Integer v = Integer.parseInt(values[x]);
- switch (v) {
- case 0:
- entries[x] = res.getString(R.string.pref_update_interval_hours_manual);
- break;
- case 1:
- entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_singular);
- break;
- default:
- entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_plural);
- break;
-
- }
}
- return entries;
}
}
+
+
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/AboutFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/AboutFragment.java
index 0a64bbe71..e85f60027 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/AboutFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/AboutFragment.java
@@ -28,7 +28,8 @@ public class AboutFragment extends PreferenceFragmentCompat {
return true;
});
findPreference("about_contributors").setOnPreferenceClickListener((preference) -> {
- getParentFragmentManager().beginTransaction().replace(R.id.content, new ContributorsPagerFragment())
+ getParentFragmentManager().beginTransaction()
+ .replace(R.id.settingsContainer, new ContributorsPagerFragment())
.addToBackStack(getString(R.string.contributors)).commit();
return true;
});
@@ -37,7 +38,8 @@ public class AboutFragment extends PreferenceFragmentCompat {
return true;
});
findPreference("about_licenses").setOnPreferenceClickListener((preference) -> {
- getParentFragmentManager().beginTransaction().replace(R.id.content, new LicensesFragment())
+ getParentFragmentManager().beginTransaction()
+ .replace(R.id.settingsContainer, new LicensesFragment())
.addToBackStack(getString(R.string.translators)).commit();
return true;
});
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/DevelopersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/DevelopersFragment.java
index b844234b7..dcd720dc2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/DevelopersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/DevelopersFragment.java
@@ -29,7 +29,7 @@ public class DevelopersFragment extends ListFragment {
developersLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> {
ArrayList<SimpleIconListAdapter.ListItem> developers = new ArrayList<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(
- getContext().getAssets().open("developers.csv")));
+ getContext().getAssets().open("developers.csv"), "UTF-8"));
String line;
while ((line = reader.readLine()) != null) {
String[] info = line.split(";");
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/LicensesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/LicensesFragment.java
index 97565a613..38e532aed 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/LicensesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/LicensesFragment.java
@@ -95,7 +95,7 @@ public class LicensesFragment extends ListFragment {
private void showLicenseText(String licenseTextFile) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
- getContext().getAssets().open(licenseTextFile)));
+ getContext().getAssets().open(licenseTextFile), "UTF-8"));
StringBuilder licenseText = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/SpecialThanksFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/SpecialThanksFragment.java
index d759a5ff2..1b4beeea0 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/SpecialThanksFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/SpecialThanksFragment.java
@@ -29,7 +29,7 @@ public class SpecialThanksFragment extends ListFragment {
translatorsLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> {
ArrayList<SimpleIconListAdapter.ListItem> translators = new ArrayList<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(
- getContext().getAssets().open("special_thanks.csv")));
+ getContext().getAssets().open("special_thanks.csv"), "UTF-8"));
String line;
while ((line = reader.readLine()) != null) {
String[] info = line.split(";");
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/TranslatorsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/TranslatorsFragment.java
index b77c74de6..ed0d53145 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/TranslatorsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/TranslatorsFragment.java
@@ -29,7 +29,7 @@ public class TranslatorsFragment extends ListFragment {
translatorsLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> {
ArrayList<SimpleIconListAdapter.ListItem> translators = new ArrayList<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(
- getContext().getAssets().open("translators.csv")));
+ getContext().getAssets().open("translators.csv"), "UTF-8"));
String line;
while ((line = reader.readLine()) != null) {
String[] info = line.split(";");
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 1eecccb4c..d478c581d 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -12,15 +12,15 @@ import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.sync.SyncService;
-import de.danoeh.antennapod.core.sync.model.EpisodeAction;
+import de.danoeh.antennapod.net.sync.model.EpisodeAction;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
@@ -49,7 +49,7 @@ public class FeedItemMenuHandler {
return false;
}
final boolean hasMedia = selectedItem.getMedia() != null;
- final boolean isPlaying = hasMedia && selectedItem.getState() == FeedItem.State.PLAYING;
+ final boolean isPlaying = hasMedia && FeedItemUtil.isPlaying(selectedItem.getMedia());
final boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE);
final boolean fileDownloaded = hasMedia && selectedItem.getMedia().fileExists();
final boolean isFavorite = selectedItem.isTagged(FeedItem.TAG_FAVORITE);
@@ -249,7 +249,7 @@ public class FeedItemMenuHandler {
final Handler h = new Handler(fragment.requireContext().getMainLooper());
final Runnable r = () -> {
FeedMedia media = item.getMedia();
- if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
+ if (media != null && FeedItemUtil.hasAlmostEnded(media) && UserPreferences.isAutoDelete()) {
DBWriter.deleteFeedMediaOfItem(fragment.requireContext(), media.getId());
}
};
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 0086a75ab..ed0cac05d 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -14,13 +14,13 @@ import java.util.Set;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
-import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.dialog.FilterDialog;
import de.danoeh.antennapod.dialog.IntraFeedSortDialog;
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
index fbfdf537f..b42244160 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
@@ -24,10 +24,10 @@ public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuIte
final MenuItem queueLock = menu.findItem(R.id.queue_lock);
if (UserPreferences.isQueueLocked()) {
queueLock.setTitle(de.danoeh.antennapod.R.string.unlock_queue);
- queueLock.setIcon(ThemeUtils.getDrawableFromAttr(context, R.attr.ic_lock_open));
+ queueLock.setIcon(R.drawable.ic_lock_open);
} else {
queueLock.setTitle(de.danoeh.antennapod.R.string.lock_queue);
- queueLock.setIcon(ThemeUtils.getDrawableFromAttr(context, R.attr.ic_lock_closed));
+ queueLock.setIcon(R.drawable.ic_lock_closed);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java b/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java
index c9bd973cb..5981d3d67 100644
--- a/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java
+++ b/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java
@@ -11,7 +11,7 @@ import java.util.Arrays;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
diff --git a/app/src/main/java/de/danoeh/antennapod/view/ChapterSeekBar.java b/app/src/main/java/de/danoeh/antennapod/view/ChapterSeekBar.java
new file mode 100644
index 000000000..ba7acb847
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/ChapterSeekBar.java
@@ -0,0 +1,148 @@
+package de.danoeh.antennapod.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
+
+public class ChapterSeekBar extends androidx.appcompat.widget.AppCompatSeekBar {
+
+ private float top;
+ private float width;
+ private float center;
+ private float bottom;
+ private float density;
+ private float progressPrimary;
+ private float progressSecondary;
+ private float[] dividerPos;
+ private boolean isHighlighted = false;
+ private final Paint paintBackground = new Paint();
+ private final Paint paintProgressPrimary = new Paint();
+
+ public ChapterSeekBar(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public ChapterSeekBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public ChapterSeekBar(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context);
+ }
+
+ private void init(Context context) {
+ setBackground(null); // Removes the thumb shadow
+ dividerPos = null;
+ density = context.getResources().getDisplayMetrics().density;
+
+ paintBackground.setColor(ThemeUtils.getColorFromAttr(getContext(),
+ de.danoeh.antennapod.core.R.attr.currently_playing_background));
+ paintBackground.setAlpha(128);
+ paintProgressPrimary.setColor(ThemeUtils.getColorFromAttr(getContext(),
+ de.danoeh.antennapod.core.R.attr.colorPrimary));
+ }
+
+ /**
+ * Sets the relative positions of the chapter dividers.
+ * @param dividerPos of the chapter dividers relative to the duration of the media.
+ */
+ public void setDividerPos(final float[] dividerPos) {
+ if (dividerPos != null) {
+ this.dividerPos = new float[dividerPos.length + 2];
+ this.dividerPos[0] = 0;
+ System.arraycopy(dividerPos, 0, this.dividerPos, 1, dividerPos.length);
+ this.dividerPos[this.dividerPos.length - 1] = 1;
+ } else {
+ this.dividerPos = null;
+ }
+ }
+
+ public void highlightCurrentChapter() {
+ isHighlighted = true;
+ new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ isHighlighted = false;
+ invalidate();
+ }
+ }, 1000);
+ }
+
+ @Override
+ protected synchronized void onDraw(Canvas canvas) {
+ center = (getBottom() - getPaddingBottom() - getTop() - getPaddingTop()) / 2.0f;
+ top = center - density * 1.5f;
+ bottom = center + density * 1.5f;
+ width = (float) (getRight() - getPaddingRight() - getLeft() - getPaddingLeft());
+ progressSecondary = getSecondaryProgress() / (float) getMax() * width;
+ progressPrimary = getProgress() / (float) getMax() * width;
+
+ if (dividerPos == null) {
+ drawProgress(canvas);
+ } else {
+ drawProgressChapters(canvas);
+ }
+ drawThumb(canvas);
+ }
+
+ private void drawProgress(Canvas canvas) {
+ final int saveCount = canvas.save();
+ canvas.translate(getPaddingLeft(), getPaddingTop());
+ canvas.drawRect(0, top, width, bottom, paintBackground);
+ canvas.drawRect(0, top, progressSecondary, bottom, paintBackground);
+ canvas.drawRect(0, top, progressPrimary, bottom, paintProgressPrimary);
+ canvas.restoreToCount(saveCount);
+ }
+
+ private void drawProgressChapters(Canvas canvas) {
+ final int saveCount = canvas.save();
+ int currChapter = 1;
+ float chapterMargin = density * 1.2f;
+ float topExpanded = center - density * 2.0f;
+ float bottomExpanded = center + density * 2.0f;
+
+ canvas.translate(getPaddingLeft(), getPaddingTop());
+
+ for (int i = 1; i < dividerPos.length; i++) {
+ float right = dividerPos[i] * width - chapterMargin;
+ float left = dividerPos[i - 1] * width;
+ float rightCurr = dividerPos[currChapter] * width - chapterMargin;
+ float leftCurr = dividerPos[currChapter - 1] * width;
+
+ canvas.drawRect(left, top, right, bottom, paintBackground);
+
+ if (progressSecondary > 0 && progressSecondary < width) {
+ if (right < progressSecondary) {
+ canvas.drawRect(left, top, right, bottom, paintBackground);
+ } else if (progressSecondary > left) {
+ canvas.drawRect(left, top, progressSecondary, bottom, paintBackground);
+ }
+ }
+
+ if (right < progressPrimary) {
+ currChapter = i + 1;
+ canvas.drawRect(left, top, right, bottom, paintProgressPrimary);
+ } else if (isHighlighted || isPressed()) {
+ canvas.drawRect(leftCurr, topExpanded, rightCurr, bottomExpanded, paintBackground);
+ canvas.drawRect(leftCurr, topExpanded, progressPrimary, bottomExpanded, paintProgressPrimary);
+ } else {
+ canvas.drawRect(leftCurr, top, progressPrimary, bottom, paintProgressPrimary);
+ }
+ }
+ canvas.restoreToCount(saveCount);
+ }
+
+ private void drawThumb(Canvas canvas) {
+ final int saveCount = canvas.save();
+ canvas.translate(getPaddingLeft() - getThumbOffset(), getPaddingTop());
+ getThumb().draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
index eaad11d1a..ce8f08511 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
@@ -2,15 +2,12 @@ package de.danoeh.antennapod.view;
import android.content.Context;
import android.database.DataSetObserver;
-import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.widget.AbsListView;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
-import androidx.annotation.AttrRes;
-import androidx.core.content.ContextCompat;
+import androidx.annotation.DrawableRes;
import androidx.recyclerview.widget.RecyclerView;
-import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
@@ -25,7 +22,6 @@ public class EmptyViewHandler {
private ListAdapter listAdapter;
private RecyclerView.Adapter<?> recyclerAdapter;
- private final Context context;
private final View emptyView;
private final TextView tvTitle;
private final TextView tvMessage;
@@ -33,7 +29,6 @@ public class EmptyViewHandler {
public EmptyViewHandler(Context context) {
emptyView = View.inflate(context, R.layout.empty_view_layout, null);
- this.context = context;
tvTitle = emptyView.findViewById(R.id.emptyViewTitle);
tvMessage = emptyView.findViewById(R.id.emptyViewMessage);
ivIcon = emptyView.findViewById(R.id.emptyViewIcon);
@@ -51,11 +46,8 @@ public class EmptyViewHandler {
tvMessage.setText(message);
}
- public void setIcon(@AttrRes int iconAttr) {
- TypedValue typedValue = new TypedValue();
- context.getTheme().resolveAttribute(iconAttr, typedValue, true);
- Drawable d = ContextCompat.getDrawable(context, typedValue.resourceId);
- ivIcon.setImageDrawable(d);
+ public void setIcon(@DrawableRes int icon) {
+ ivIcon.setImageResource(icon);
ivIcon.setVisibility(View.VISIBLE);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java
index 0e1846f1c..ff52df71f 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java
@@ -20,92 +20,181 @@
package de.danoeh.antennapod.view;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewParent;
+import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.viewpager2.widget.ViewPager2;
+import de.danoeh.antennapod.R;
+
import static androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL;
import static androidx.viewpager2.widget.ViewPager2.ORIENTATION_VERTICAL;
+
/**
* Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem
* where pages of ViewPager2 have nested scrollable elements that scroll in the same direction as
* ViewPager2. The scrollable element needs to be the immediate and only child of this host layout.
*
- * <p>This solution has limitations when using multiple levels of nested scrollable elements
+ * This solution has limitations when using multiple levels of nested scrollable elements
* (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2).
- */
-class NestedScrollableHost extends FrameLayout {
+ */ // KhaledAlharthi/NestedScrollableHost.java
+public class NestedScrollableHost extends FrameLayout {
+
+ private ViewPager2 parentViewPager;
+ private int touchSlop = 0;
+ private float initialX = 0f;
+ private float initialY = 0f;
+ private int preferVertical = 1;
+ private int preferHorizontal = 1;
+ private int scrollDirection = 0;
public NestedScrollableHost(@NonNull Context context) {
super(context);
- touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ init(context);
}
public NestedScrollableHost(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
+ init(context);
+ setAttributes(context, attrs);
+ }
+
+ public NestedScrollableHost(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ setAttributes(context, attrs);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ public NestedScrollableHost(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(context);
+ setAttributes(context, attrs);
+ }
+
+ private void setAttributes(@NonNull Context context, @Nullable AttributeSet attrs) {
+ TypedArray a = context.getTheme().obtainStyledAttributes(
+ attrs,
+ R.styleable.NestedScrollableHost,
+ 0, 0);
+
+ try {
+ preferHorizontal = a.getInteger(R.styleable.NestedScrollableHost_preferHorizontal, 1);
+ preferVertical = a.getInteger(R.styleable.NestedScrollableHost_preferVertical, 1);
+ scrollDirection = a.getInteger(R.styleable.NestedScrollableHost_scrollDirection, 0);
+ } finally {
+ a.recycle();
+ }
+
+ }
+
+ private void init(Context context) {
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+
+
+ getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ View v = (View) getParent();
+ while (v != null && !(v instanceof ViewPager2) || isntSameDirection(v)) {
+ v = (View) v.getParent();
+ }
+ parentViewPager = (ViewPager2) v;
+
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ return false;
+ }
+ });
}
- private int touchSlop;
- private float initialX = 0f;
- private float initialY = 0f;
+ private Boolean isntSameDirection(View v) {
+ int orientation = 0;
+ switch (scrollDirection) {
+ default:
+ case 0:
+ return false;
+ case 1:
+ orientation = ORIENTATION_VERTICAL;
+ break;
+ case 2:
+ orientation = ORIENTATION_HORIZONTAL;
+ break;
+ }
+ return ((v instanceof ViewPager2) && ((ViewPager2) v).getOrientation() != orientation);
+ }
+
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ handleInterceptTouchEvent(ev);
+ return super.onInterceptTouchEvent(ev);
+ }
- private ViewPager2 getParentViewPager() {
- View v = (View) getParent();
- while (v != null && !(v instanceof ViewPager2)) {
- v = (View) v.getParent();
+
+ private boolean canChildScroll(int orientation, float delta) {
+ int direction = (int) -delta;
+ View child = getChildAt(0);
+ if (orientation == 0) {
+ return child.canScrollHorizontally(direction);
+ } else if (orientation == 1) {
+ return child.canScrollVertically(direction);
+ } else {
+ throw new IllegalArgumentException();
}
- return v == null ? null : (ViewPager2) v;
}
- public boolean onInterceptTouchEvent(MotionEvent e) {
- ViewPager2 parentViewPager = getParentViewPager();
+ private void handleInterceptTouchEvent(MotionEvent e) {
if (parentViewPager == null) {
- return super.onInterceptTouchEvent(e);
+ return;
}
-
- ViewParent parent = getParent();
int orientation = parentViewPager.getOrientation();
+ boolean preferedDirection = preferHorizontal + preferVertical > 2;
+
+ // Early return if child can't scroll in same direction as parent and theres no prefered scroll direction
+ if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f) && !preferedDirection) {
+ return;
+ }
+
if (e.getAction() == MotionEvent.ACTION_DOWN) {
initialX = e.getX();
initialY = e.getY();
- parent.requestDisallowInterceptTouchEvent(true);
+ getParent().requestDisallowInterceptTouchEvent(true);
} else if (e.getAction() == MotionEvent.ACTION_MOVE) {
- int dx = (int) (e.getX() - initialX);
- int dy = (int) (e.getY() - initialY);
- boolean isVpHorizontal = orientation == ORIENTATION_HORIZONTAL;
+ float dx = e.getX() - initialX;
+ float dy = e.getY() - initialY;
+ boolean isVpHorizontal = orientation == ViewPager2.ORIENTATION_HORIZONTAL;
// assuming ViewPager2 touch-slop is 2x touch-slop of child
- float scaledDx = Math.abs(dx) * (isVpHorizontal ? .5f : 1f);
- float scaledDy = Math.abs(dy) * (isVpHorizontal ? 1f : .5f);
+ float scaledDx = Math.abs(dx) * (isVpHorizontal ? 1f : 0.5f) * preferHorizontal;
+ float scaledDy = Math.abs(dy) * (isVpHorizontal ? 0.5f : 1f) * preferVertical;
if (scaledDx > touchSlop || scaledDy > touchSlop) {
- int value = isVpHorizontal ? dy : dx;
if (isVpHorizontal == (scaledDy > scaledDx)) {
- // Gesture is perpendicular
- orientation = orientation == ORIENTATION_VERTICAL
- ? ORIENTATION_HORIZONTAL : ORIENTATION_VERTICAL;
- value = isVpHorizontal ? dy : dx;
- }
-
- int direction = (int) -Math.copySign(1, value);
- View child = getChildAt(0);
- if (orientation == ORIENTATION_HORIZONTAL) {
- parent.requestDisallowInterceptTouchEvent(child.canScrollHorizontally(direction));
+ // Gesture is perpendicular, allow all parents to intercept
+ getParent().requestDisallowInterceptTouchEvent(preferedDirection);
} else {
- parent.requestDisallowInterceptTouchEvent(child.canScrollVertically(direction));
+ // Gesture is parallel, query child if movement in that direction is possible
+ if (canChildScroll(orientation, isVpHorizontal ? dx : dy)) {
+ // Child can scroll, disallow all parents to intercept
+ getParent().requestDisallowInterceptTouchEvent(true);
+ } else {
+ // Child cannot scroll, allow all parents to intercept
+ getParent().requestDisallowInterceptTouchEvent(false);
+ }
}
}
- }
- return super.onInterceptTouchEvent(e);
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/PlayButton.java b/app/src/main/java/de/danoeh/antennapod/view/PlayButton.java
new file mode 100644
index 000000000..04b277fb4
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/PlayButton.java
@@ -0,0 +1,52 @@
+package de.danoeh.antennapod.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatImageButton;
+import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
+import de.danoeh.antennapod.R;
+
+public class PlayButton extends AppCompatImageButton {
+ private boolean isShowPlay = true;
+ private boolean isVideoScreen = false;
+
+ public PlayButton(@NonNull Context context) {
+ super(context);
+ }
+
+ public PlayButton(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public PlayButton(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public void setIsVideoScreen(boolean isVideoScreen) {
+ this.isVideoScreen = isVideoScreen;
+ }
+
+ public void setIsShowPlay(boolean showPlay) {
+ if (this.isShowPlay != showPlay) {
+ this.isShowPlay = showPlay;
+ setContentDescription(getContext().getString(showPlay ? R.string.play_label : R.string.pause_label));
+ if (isVideoScreen) {
+ setImageResource(showPlay ? R.drawable.ic_play_video_white : R.drawable.ic_pause_video_white);
+ } else if (!isShown()) {
+ setImageResource(showPlay ? R.drawable.ic_play_48dp : R.drawable.ic_pause);
+ } else if (showPlay) {
+ AnimatedVectorDrawableCompat drawable = AnimatedVectorDrawableCompat.create(
+ getContext(), R.drawable.ic_animate_pause_play);
+ setImageDrawable(drawable);
+ drawable.start();
+ } else {
+ AnimatedVectorDrawableCompat drawable = AnimatedVectorDrawableCompat.create(
+ getContext(), R.drawable.ic_animate_play_pause);
+ setImageDrawable(drawable);
+ drawable.start();
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java b/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java
index 47797e4a4..c75164a74 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java
@@ -10,7 +10,6 @@ import androidx.annotation.Nullable;
import androidx.core.util.Consumer;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
-import de.danoeh.antennapod.dialog.VariableSpeedDialog;
public class PlaybackSpeedSeekBar extends FrameLayout {
private SeekBar seekBar;
@@ -41,7 +40,7 @@ public class PlaybackSpeedSeekBar extends FrameLayout {
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (controller != null && controller.canSetPlaybackSpeed()) {
+ if (controller != null) {
float playbackSpeed = (progress + 10) / 20.0f;
controller.setPlaybackSpeed(playbackSpeed);
@@ -55,9 +54,6 @@ public class PlaybackSpeedSeekBar extends FrameLayout {
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
- if (controller != null && !controller.canSetPlaybackSpeed()) {
- VariableSpeedDialog.showGetPluginDialog(getContext());
- }
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/view/viewholder/DownloadItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/view/viewholder/DownloadLogItemViewHolder.java
index 0e446fb84..578e1b149 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/viewholder/DownloadItemViewHolder.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/viewholder/DownloadLogItemViewHolder.java
@@ -11,25 +11,26 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.joanzapata.iconify.widget.IconTextView;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.ui.common.CircularProgressBar;
-public class DownloadItemViewHolder extends RecyclerView.ViewHolder {
+public class DownloadLogItemViewHolder extends RecyclerView.ViewHolder {
public final View secondaryActionButton;
public final ImageView secondaryActionIcon;
+ public final CircularProgressBar secondaryActionProgress;
public final IconTextView icon;
public final TextView title;
- public final TextView type;
- public final TextView date;
+ public final TextView status;
public final TextView reason;
public final TextView tapForDetails;
- public DownloadItemViewHolder(Context context, ViewGroup parent) {
+ public DownloadLogItemViewHolder(Context context, ViewGroup parent) {
super(LayoutInflater.from(context).inflate(R.layout.downloadlog_item, parent, false));
- date = itemView.findViewById(R.id.txtvDate);
- type = itemView.findViewById(R.id.txtvType);
+ status = itemView.findViewById(R.id.status);
icon = itemView.findViewById(R.id.txtvIcon);
reason = itemView.findViewById(R.id.txtvReason);
tapForDetails = itemView.findViewById(R.id.txtvTapForDetails);
secondaryActionButton = itemView.findViewById(R.id.secondaryActionButton);
+ secondaryActionProgress = itemView.findViewById(R.id.secondaryActionProgress);
secondaryActionIcon = itemView.findViewById(R.id.secondaryActionIcon);
title = itemView.findViewById(R.id.txtvTitle);
if (Build.VERSION.SDK_INT >= 23) {
diff --git a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
index 9fd742d5f..1ea9d71f9 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
@@ -19,9 +19,9 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.CoverLoader;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
@@ -29,6 +29,7 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.ui.common.CircularProgressBar;
@@ -133,7 +134,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
isVideo.setVisibility(media.getMediaType() == MediaType.VIDEO ? View.VISIBLE : View.GONE);
duration.setVisibility(media.getDuration() > 0 ? View.VISIBLE : View.GONE);
- if (media.isCurrentlyPlaying()) {
+ if (FeedItemUtil.isCurrentlyPlaying(media)) {
itemView.setBackgroundColor(ThemeUtils.getColorFromAttr(activity, R.attr.currently_playing_background));
} else {
itemView.setBackgroundResource(ThemeUtils.getDrawableFromAttr(activity, R.attr.selectableItemBackground));
@@ -152,7 +153,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
duration.setText(Converter.getDurationStringLong(media.getDuration()));
duration.setContentDescription(activity.getString(R.string.chapter_duration,
Converter.getDurationStringLocalized(activity, media.getDuration())));
- if (item.getState() == FeedItem.State.PLAYING || item.getState() == FeedItem.State.IN_PROGRESS) {
+ if (FeedItemUtil.isPlaying(item.getMedia()) || item.isInProgress()) {
int progress = (int) (100.0 * media.getPosition() / media.getDuration());
int remainingTime = Math.max(media.getDuration() - media.getPosition(), 0);
progressBar.setProgress(progress);
@@ -213,7 +214,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
}
public boolean isCurrentlyPlayingItem() {
- return item.getMedia() != null && item.getMedia().isCurrentlyPlaying();
+ return item.getMedia() != null && FeedItemUtil.isCurrentlyPlaying(item.getMedia());
}
public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
diff --git a/app/src/main/res/drawable-v21/grey_border.xml b/app/src/main/res/drawable-v21/grey_border.xml
new file mode 100644
index 000000000..beccf9e85
--- /dev/null
+++ b/app/src/main/res/drawable-v21/grey_border.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+android:color="?attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape
+ android:shape="rectangle">
+ <corners
+ android:radius="8dp"
+ android:topRightRadius="8dp"
+ android:bottomRightRadius="8dp"
+ android:bottomLeftRadius="8dp" />
+ <solid android:color="@android:color/white"/>
+ </shape>
+ </item>
+ <item>
+ <shape
+ android:shape="rectangle">
+ <corners
+ android:radius="8dp"
+ android:topRightRadius="8dp"
+ android:bottomRightRadius="8dp"
+ android:bottomLeftRadius="8dp" />
+ <stroke
+ android:width="1dp"
+ android:color="?android:attr/textColorSecondary" />
+ <solid android:color="@android:color/transparent"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/app/src/main/res/drawable/grey_border.xml b/app/src/main/res/drawable/grey_border.xml
new file mode 100644
index 000000000..4362f05b6
--- /dev/null
+++ b/app/src/main/res/drawable/grey_border.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector>
+
+ <item>
+
+ <shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:radius="8dp"
+ android:topRightRadius="8dp"
+ android:bottomRightRadius="8dp"
+ android:bottomLeftRadius="8dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@android:color/darker_gray" />
+ <solid android:color="@android:color/transparent"/>
+ </shape>
+
+ </item>
+
+</selector> \ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_animate_pause_play.xml b/app/src/main/res/drawable/ic_animate_pause_play.xml
new file mode 100644
index 000000000..cc68279cd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_animate_pause_play.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:drawable="@drawable/ic_animate_play"
+ tools:ignore="NewApi">
+ <target android:name="path">
+ <aapt:attr name="android:animation">
+ <objectAnimator
+ android:duration="@integer/fragment_transition_duration"
+ android:propertyName="pathData"
+ android:valueFrom="@string/svg_animatable_pause"
+ android:valueTo="@string/svg_animatable_play"
+ android:valueType="pathType"/>
+ </aapt:attr>
+ </target>
+ <target android:name="group">
+ <aapt:attr name="android:animation">
+ <objectAnimator
+ android:duration="@integer/fragment_transition_duration"
+ android:propertyName="rotation"
+ android:valueFrom="-90"
+ android:valueTo="0"/>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/app/src/main/res/drawable/ic_animate_play.xml b/app/src/main/res/drawable/ic_animate_play.xml
new file mode 100644
index 000000000..3c0a5ef1d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_animate_play.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <group android:name="group"
+ android:pivotX="12"
+ android:pivotY="12">
+ <path android:name="path"
+ android:fillColor="?attr/action_icon_color"
+ android:pathData="@string/svg_animatable_play"/>
+ </group>
+</vector>
diff --git a/app/src/main/res/drawable/ic_animate_play_pause.xml b/app/src/main/res/drawable/ic_animate_play_pause.xml
new file mode 100644
index 000000000..8e4f65b5e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_animate_play_pause.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:drawable="@drawable/ic_animate_play"
+ tools:ignore="NewApi">
+ <target android:name="path">
+ <aapt:attr name="android:animation">
+ <objectAnimator
+ android:duration="@integer/fragment_transition_duration"
+ android:propertyName="pathData"
+ android:valueFrom="@string/svg_animatable_play"
+ android:valueTo="@string/svg_animatable_pause"
+ android:valueType="pathType"/>
+ </aapt:attr>
+ </target>
+ <target android:name="group">
+ <aapt:attr name="android:animation">
+ <objectAnimator
+ android:duration="@integer/fragment_transition_duration"
+ android:propertyName="rotation"
+ android:valueFrom="0"
+ android:valueTo="90"/>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/app/src/main/res/layout/activity_widget_config.xml b/app/src/main/res/layout/activity_widget_config.xml
index 6e31aec0d..8c540fcc6 100644
--- a/app/src/main/res/layout/activity_widget_config.xml
+++ b/app/src/main/res/layout/activity_widget_config.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
@@ -15,7 +16,7 @@
android:id="@+id/widget_config_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:src="@drawable/teaser"
+ app:srcCompat="@drawable/teaser"
android:scaleType="centerCrop" />
<include
diff --git a/app/src/main/res/layout/addfeed.xml b/app/src/main/res/layout/addfeed.xml
index d6ebd58d7..a6e0fc9ea 100644
--- a/app/src/main/res/layout/addfeed.xml
+++ b/app/src/main/res/layout/addfeed.xml
@@ -34,7 +34,7 @@
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:contentDescription="@string/search_podcast_hint"
- app:srcCompat="?attr/action_search"
+ app:srcCompat="@drawable/ic_search"
android:id="@+id/searchButton"
android:scaleType="center"/>
@@ -90,8 +90,8 @@
android:id="@+id/addViaUrlButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:drawableStartCompat="?attr/feed"
- app:drawableLeftCompat="?attr/feed"
+ app:drawableStartCompat="@drawable/ic_feed"
+ app:drawableLeftCompat="@drawable/ic_feed"
style="@style/AddPodcastTextView"
android:text="@string/add_podcast_by_url"/>
@@ -99,8 +99,8 @@
android:id="@+id/addLocalFolderButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:drawableStartCompat="?attr/ic_folder"
- app:drawableLeftCompat="?attr/ic_folder"
+ app:drawableStartCompat="@drawable/ic_folder"
+ app:drawableLeftCompat="@drawable/ic_folder"
style="@style/AddPodcastTextView"
android:text="@string/add_local_folder"/>
@@ -108,8 +108,8 @@
android:id="@+id/searchItunesButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:drawableStartCompat="?attr/action_search"
- app:drawableLeftCompat="?attr/action_search"
+ app:drawableStartCompat="@drawable/ic_search"
+ app:drawableLeftCompat="@drawable/ic_search"
style="@style/AddPodcastTextView"
android:text="@string/search_itunes_label"/>
@@ -117,8 +117,8 @@
android:id="@+id/searchFyydButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:drawableStartCompat="?attr/action_search"
- app:drawableLeftCompat="?attr/action_search"
+ app:drawableStartCompat="@drawable/ic_search"
+ app:drawableLeftCompat="@drawable/ic_search"
style="@style/AddPodcastTextView"
android:text="@string/search_fyyd_label"/>
@@ -126,8 +126,8 @@
android:id="@+id/searchGPodderButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:drawableStartCompat="?attr/action_search"
- app:drawableLeftCompat="?attr/action_search"
+ app:drawableStartCompat="@drawable/ic_search"
+ app:drawableLeftCompat="@drawable/ic_search"
style="@style/AddPodcastTextView"
android:text="@string/browse_gpoddernet_label"/>
@@ -135,8 +135,8 @@
android:id="@+id/searchPodcastIndexButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:drawableStartCompat="?attr/action_search"
- app:drawableLeftCompat="?attr/action_search"
+ app:drawableStartCompat="@drawable/ic_search"
+ app:drawableLeftCompat="@drawable/ic_search"
style="@style/AddPodcastTextView"
android:text="@string/search_podcastindex_label"/>
@@ -144,8 +144,8 @@
android:id="@+id/opmlImportButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:drawableStartCompat="?attr/av_download"
- app:drawableLeftCompat="?attr/av_download"
+ app:drawableStartCompat="@drawable/ic_download"
+ app:drawableLeftCompat="@drawable/ic_download"
style="@style/AddPodcastTextView"
android:text="@string/opml_add_podcast_label"/>
</LinearLayout>
diff --git a/app/src/main/res/layout/alternate_urls_dropdown_item.xml b/app/src/main/res/layout/alternate_urls_dropdown_item.xml
new file mode 100644
index 000000000..82de8a02f
--- /dev/null
+++ b/app/src/main/res/layout/alternate_urls_dropdown_item.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<CheckedTextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ style="?android:attr/spinnerDropDownItemStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingVertical="8dp" />
diff --git a/app/src/main/res/layout/alternate_urls_item.xml b/app/src/main/res/layout/alternate_urls_item.xml
new file mode 100644
index 000000000..9fdb50e33
--- /dev/null
+++ b/app/src/main/res/layout/alternate_urls_item.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ style="?android:attr/spinnerItemStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="3"
+ android:textAlignment="inherit" />
diff --git a/app/src/main/res/layout/audioplayer_fragment.xml b/app/src/main/res/layout/audioplayer_fragment.xml
index c9d443e63..f801930f5 100644
--- a/app/src/main/res/layout/audioplayer_fragment.xml
+++ b/app/src/main/res/layout/audioplayer_fragment.xml
@@ -6,44 +6,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <!--
- While the same visuals could be achieved without this nested layout,
- RelativeLayout invalidates all its direct children when the playback
- position is updated. This causes flickering of the tabs every second.
- -->
- <LinearLayout
- android:id="@+id/pagerContainer"
+ <androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:minHeight="?attr/actionBarSize"
+ android:theme="?attr/actionBarTheme"
android:layout_alignParentTop="true"
- android:layout_above="@id/playtime_layout"
- android:orientation="vertical"
- android:layout_marginBottom="12dp">
-
- <androidx.appcompat.widget.Toolbar
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?attr/actionBarSize"
- android:theme="?attr/actionBarTheme"
- app:navigationIcon="?homeAsUpIndicator"
- android:id="@+id/toolbar"/>
-
- <com.google.android.material.tabs.TabLayout
- android:id="@+id/sliding_tabs"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/windowBackground"
- app:tabBackground="?attr/selectableItemBackground"
- app:tabMode="fixed"
- app:tabGravity="fill"/>
-
- <androidx.viewpager2.widget.ViewPager2
- android:id="@+id/pager"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:foreground="?android:windowContentOverlay"/>
- </LinearLayout>
+ app:navigationIcon="?homeAsUpIndicator"
+ android:id="@+id/toolbar"/>
<FrameLayout
android:id="@+id/playerFragment"
@@ -54,11 +24,21 @@
tools:layout_height="@dimen/external_player_height"
android:elevation="8dp"/>
+ <androidx.viewpager2.widget.ViewPager2
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_above="@id/playtime_layout"
+ android:layout_below="@id/toolbar"
+ android:layout_marginBottom="12dp"
+ android:foreground="?android:windowContentOverlay"
+ android:orientation="vertical" />
+
<ImageView
android:layout_width="match_parent"
android:layout_height="8dp"
- android:layout_alignBottom="@id/pagerContainer"
- android:src="@drawable/bg_gradient"
+ android:layout_alignBottom="@id/pager"
+ app:srcCompat="@drawable/bg_gradient"
app:tint="?android:attr/windowBackground"
android:importantForAccessibility="no"/>
@@ -67,7 +47,9 @@
android:alpha="0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignBottom="@+id/pagerContainer"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:layout_alignBottom="@+id/pager"
android:layout_centerHorizontal="true"
android:layout_marginBottom="12dp"
app:cardCornerRadius="8dp"
@@ -85,7 +67,7 @@
android:paddingRight="24dp"
android:paddingBottom="4dp"
android:textColor="@color/white"
- android:textSize="24sp"
+ android:textSize="16sp"
tools:text="1:06:29" />
</androidx.cardview.widget.CardView>
@@ -98,7 +80,7 @@
android:layoutDirection="ltr"
android:orientation="vertical">
- <SeekBar
+ <de.danoeh.antennapod.view.ChapterSeekBar
android:id="@+id/sbPosition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -149,7 +131,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="24dp">
- <ImageButton
+ <de.danoeh.antennapod.view.PlayButton
android:id="@+id/butPlay"
android:layout_width="@dimen/audioplayer_playercontrols_length_big"
android:layout_height="@dimen/audioplayer_playercontrols_length_big"
@@ -162,9 +144,9 @@
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/pause_label"
- app:srcCompat="?attr/av_play"
+ app:srcCompat="@drawable/ic_play_48dp"
android:scaleType="fitCenter"
- tools:srcCompat="@drawable/ic_av_play_white_24dp"/>
+ tools:srcCompat="@drawable/ic_play_48dp"/>
<de.danoeh.antennapod.ui.common.CircularProgressBar
android:layout_width="@dimen/audioplayer_playercontrols_length_big"
@@ -197,9 +179,9 @@
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/rewind_label"
- app:srcCompat="?attr/av_rewind"
+ app:srcCompat="@drawable/ic_fast_rewind"
android:scaleType="fitCenter"
- tools:srcCompat="@drawable/ic_av_fast_rewind_white_48dp"/>
+ tools:srcCompat="@drawable/ic_fast_rewind"/>
<TextView
android:id="@+id/txtvRev"
@@ -225,7 +207,7 @@
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/playback_speed"
- tools:srcCompat="@drawable/ic_playback_speed_white"
+ tools:srcCompat="@drawable/ic_playback_speed"
app:foregroundColor="?attr/action_icon_color"/>
<TextView
@@ -254,9 +236,9 @@
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/fast_forward_label"
- app:srcCompat="?attr/av_fast_forward"
+ app:srcCompat="@drawable/ic_fast_forward"
android:scaleType="fitCenter"
- tools:srcCompat="@drawable/ic_av_fast_forward_white_48dp"/>
+ tools:srcCompat="@drawable/ic_fast_forward"/>
<TextView
android:id="@+id/txtvFF"
@@ -282,9 +264,9 @@
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="fitCenter"
- app:srcCompat="?attr/av_skip"
+ app:srcCompat="@drawable/ic_skip_48dp"
android:contentDescription="@string/skip_episode_label"
- tools:srcCompat="@drawable/ic_av_skip_white_48dp"/>
+ tools:srcCompat="@drawable/ic_skip_48dp"/>
</RelativeLayout>
</LinearLayout>
diff --git a/app/src/main/res/layout/cover_fragment.xml b/app/src/main/res/layout/cover_fragment.xml
index 0ec46cbcd..8333ded1e 100644
--- a/app/src/main/res/layout/cover_fragment.xml
+++ b/app/src/main/res/layout/cover_fragment.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:squareImageView="http://schemas.android.com/apk/de.danoeh.antennapod"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -10,14 +11,18 @@
android:padding="8dp"
android:gravity="center">
+ <Space
+ android:id="@+id/counterweight"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
<de.danoeh.antennapod.ui.common.SquareImageView
android:id="@+id/imgvCover"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_gravity="center"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginTop="8dp"
+ android:layout_marginHorizontal="16dp"
android:layout_weight="0"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:importantForAccessibility="no"
@@ -29,33 +34,142 @@
android:id="@+id/cover_fragment_text_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_marginTop="8dp"
- android:layout_marginBottom="8dp">
+ android:layout_marginVertical="8dp"
+ android:gravity="center"
+ android:orientation="vertical">
<TextView
android:id="@+id/txtvPodcastTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="?android:selectableItemBackground"
android:ellipsize="none"
android:gravity="center_horizontal"
android:maxLines="2"
- android:textSize="@dimen/text_size_small"
+ android:paddingTop="2dp"
+ android:paddingBottom="2dp"
android:textColor="?android:attr/textColorSecondary"
- android:textIsSelectable="true"
+ android:textIsSelectable="false"
+ android:textSize="@dimen/text_size_small"
tools:text="Podcast" />
<TextView
android:id="@+id/txtvEpisodeTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="?android:selectableItemBackground"
android:ellipsize="none"
android:gravity="center_horizontal"
- android:textSize="@dimen/text_size_small"
android:maxLines="2"
+ android:paddingTop="2dp"
+ android:paddingBottom="2dp"
android:textColor="?android:attr/textColorPrimary"
- android:textIsSelectable="true"
+ android:textIsSelectable="false"
+ android:textSize="@dimen/text_size_small"
tools:text="Episode" />
+
+ <Space
+ android:id="@+id/vertical_divider"
+ android:layout_width="match_parent"
+ android:layout_height="8dp"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+ <Space
+ android:id="@+id/details_spacer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:id="@+id/episode_details"
+ android:layout_width="wrap_content"
+ android:layout_height="36dp"
+ android:layout_weight="0"
+ android:baselineAligned="false"
+ android:orientation="horizontal"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp">
+
+ <LinearLayout
+ android:id="@+id/openDescription"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="8dp"
+ android:background="@drawable/grey_border"
+ android:clickable="true"
+ android:focusable="true"
+ android:gravity="center"
+ android:minWidth="150dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/description_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="36dp"
+ android:contentDescription="@string/shownotes_contentdescription"
+ android:padding="2dp"
+ app:srcCompat="@drawable/ic_info" />
+
+ <TextView
+ android:id="@+id/shownotes_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="none"
+ android:layout_marginLeft="2dp"
+ android:layout_marginStart="2dp"
+ android:gravity="center_horizontal"
+ android:maxLines="2"
+ android:text="@string/shownotes_label"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="16sp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/chapterButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="8dp"
+ android:layout_weight="1"
+ android:background="@drawable/grey_border"
+ android:clickable="true"
+ android:focusable="true"
+ android:gravity="center"
+ android:minWidth="150dp"
+ android:orientation="horizontal"
+ android:visibility="gone">
+
+ <ImageButton
+ android:id="@+id/butPrevChapter"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/prev_chapter"
+ android:scaleType="fitCenter"
+ app:srcCompat="@drawable/ic_chapter_prev" />
+
+ <TextView
+ android:id="@+id/chapters_label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:text="@string/chapters_label"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="@dimen/text_size_navdrawer" />
+
+ <ImageButton
+ android:id="@+id/butNextChapter"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/next_chapter"
+ android:scaleType="fitCenter"
+ app:srcCompat="@drawable/ic_chapter_next" />
+ </LinearLayout>
+
</LinearLayout>
</LinearLayout>
diff --git a/app/src/main/res/layout/downloadlist_item.xml b/app/src/main/res/layout/downloadlist_item.xml
deleted file mode 100644
index 7a4c2fede..000000000
--- a/app/src/main/res/layout/downloadlist_item.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:baselineAligned="false"
- android:descendantFocusability="blocksDescendants">
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
- android:layout_marginEnd="@dimen/listitem_threeline_textrightpadding"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginLeft="16dp"
- android:layout_marginStart="16dp"
- android:layout_weight="1"
- android:orientation="vertical">
- <TextView
- android:id="@+id/txtvTitle"
- style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- tools:text="@sample/episodes.json/data/title"
- android:ellipsize="end"/>
- <TextView
- android:id="@+id/txtvStatus"
- style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- tools:text="Media file · 10MB / 20MB"/>
- </LinearLayout>
- <include layout="@layout/secondary_action"/>
-</LinearLayout>
diff --git a/app/src/main/res/layout/downloadlog_item.xml b/app/src/main/res/layout/downloadlog_item.xml
index 60c916cdc..f55e0f25e 100644
--- a/app/src/main/res/layout/downloadlog_item.xml
+++ b/app/src/main/res/layout/downloadlog_item.xml
@@ -10,19 +10,6 @@
android:baselineAligned="false"
android:descendantFocusability="blocksDescendants">
- <com.joanzapata.iconify.widget.IconTextView
- android:id="@+id/txtvIcon"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
- android:layout_marginStart="@dimen/listitem_threeline_textleftpadding"
- android:textSize="40dp"
- android:gravity="center"
- tools:text="X"
- tools:ignore="SpUsage"/>
-
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
@@ -38,49 +25,43 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:id="@+id/status"
android:orientation="horizontal"
android:gravity="center_vertical">
- <TextView
- android:id="@+id/txtvType"
- style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
+ <com.joanzapata.iconify.widget.IconTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- tools:text="Media file"/>
- <TextView
- style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"
- android:layout_marginLeft="4dp"
- android:layout_marginStart="4dp"
- android:text="·"
- tools:background="@android:color/holo_blue_light"/>
+ android:padding="2dp"
+ android:id="@+id/txtvIcon"
+ android:textSize="18sp"
+ android:gravity="center"/>
+
<TextView
- android:id="@+id/txtvDate"
- style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
- android:layout_width="wrap_content"
+ android:id="@+id/txtvTitle"
+ style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- tools:text="January 23"/>
+ tools:text="@sample/episodes.json/data/title"
+ android:maxLines="1"
+ android:ellipsize="end"
+ tools:background="@android:color/holo_blue_light"/>
</LinearLayout>
+
<TextView
- android:id="@+id/txtvTitle"
- style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
- android:layout_width="match_parent"
+ android:id="@+id/status"
+ style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- tools:text="@sample/episodes.json/data/title"
- android:ellipsize="end"
- tools:background="@android:color/holo_blue_light"/>
+ tools:text="Media file - 01/01/1970"/>
<TextView
android:id="@+id/txtvReason"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
- android:textColor="?android:attr/textColorSecondary"
+ android:textColor="@color/download_failed_red"
tools:text="@string/design_time_downloaded_log_failure_reason"/>
<TextView
@@ -94,5 +75,4 @@
</LinearLayout>
<include layout="@layout/secondary_action"/>
-
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/app/src/main/res/layout/edit_tags_dialog.xml b/app/src/main/res/layout/edit_tags_dialog.xml
new file mode 100644
index 000000000..2e7774cea
--- /dev/null
+++ b/app/src/main/res/layout/edit_tags_dialog.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/tagsRecycler"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <CheckBox
+ android:id="@+id/rootFolderCheckbox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/feed_folders_include_root" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="text"
+ android:ems="10"
+ android:id="@+id/newTagEditText"/>
+
+ <ImageButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:srcCompat="@drawable/ic_add"
+ android:contentDescription="@string/new_label"
+ android:id="@+id/newTagButton"/>
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/empty_view_layout.xml b/app/src/main/res/layout/empty_view_layout.xml
index da2040d93..c6de8d2d0 100644
--- a/app/src/main/res/layout/empty_view_layout.xml
+++ b/app/src/main/res/layout/empty_view_layout.xml
@@ -14,7 +14,7 @@
android:layout_width="32dp"
android:layout_height="32dp"
android:visibility="gone"
- tools:src="@drawable/ic_feed_black"
+ tools:src="@drawable/ic_feed"
tools:visibility="visible"/>
<TextView
diff --git a/app/src/main/res/layout/episodes_apply_action_fragment.xml b/app/src/main/res/layout/episodes_apply_action_fragment.xml
index 304588e3e..78827a12a 100644
--- a/app/src/main/res/layout/episodes_apply_action_fragment.xml
+++ b/app/src/main/res/layout/episodes_apply_action_fragment.xml
@@ -47,7 +47,7 @@
android:id="@+id/fabSD"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:sdMainFabClosedSrc="?attr/batch_edit_fab_icon"
+ app:sdMainFabClosedSrc="@drawable/ic_fab_edit"
app:sdOverlayLayout="@id/fabSDOverlay"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
diff --git a/app/src/main/res/layout/external_player_fragment.xml b/app/src/main/res/layout/external_player_fragment.xml
index f36baec26..fa881e622 100644
--- a/app/src/main/res/layout/external_player_fragment.xml
+++ b/app/src/main/res/layout/external_player_fragment.xml
@@ -57,16 +57,16 @@
</LinearLayout>
- <ImageButton
+ <de.danoeh.antennapod.view.PlayButton
android:id="@+id/butPlay"
android:layout_width="52dp"
android:layout_height="match_parent"
android:contentDescription="@string/pause_label"
android:background="?attr/selectableItemBackground"
- app:srcCompat="?attr/av_play"
+ app:srcCompat="@drawable/ic_play_48dp"
android:scaleType="fitCenter"
android:padding="8dp"
- tools:src="@drawable/ic_av_play_white_48dp"/>
+ tools:src="@drawable/ic_play_48dp"/>
</LinearLayout>
diff --git a/app/src/main/res/layout/feed_refresh_dialog.xml b/app/src/main/res/layout/feed_refresh_dialog.xml
new file mode 100644
index 000000000..02b49a6c6
--- /dev/null
+++ b/app/src/main/res/layout/feed_refresh_dialog.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RadioGroup
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/radioGroup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp">
+
+ <RadioButton
+ android:id="@+id/intervalRadioButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/feed_refresh_interval"/>
+
+ <Spinner
+ android:id="@+id/spinner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ <RadioButton
+ android:id="@+id/timeRadioButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/feed_refresh_time" />
+
+ <TimePicker
+ android:id="@+id/timePicker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:timePickerMode="spinner"
+ android:visibility="gone" />
+
+ <RadioButton
+ android:id="@+id/disableRadioButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/feed_refresh_never" />
+</RadioGroup>
diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml
index a09733164..d753cbda1 100644
--- a/app/src/main/res/layout/feedinfo.xml
+++ b/app/src/main/res/layout/feedinfo.xml
@@ -91,6 +91,28 @@
tools:text="http://www.example.com/feed" />
<TextView
+ android:id="@+id/lblSupport"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="4dp"
+ android:text="@string/support_funding_label"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="18sp"
+ tools:background="@android:color/holo_red_light" />
+
+ <TextView
+ android:id="@+id/txtvFundingUrl"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="8"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:linksClickable="true"
+ android:autoLink="web"
+ tools:background="@android:color/holo_green_dark" />
+
+ <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml
index 049182803..f070b4fd0 100644
--- a/app/src/main/res/layout/feeditem_fragment.xml
+++ b/app/src/main/res/layout/feeditem_fragment.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/content_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -126,7 +127,7 @@
android:layout_marginEnd="8dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
- tools:src="@drawable/ic_settings_black" />
+ tools:src="@drawable/ic_settings" />
<TextView
android:textAppearance="@style/TextAppearance.AppCompat.Button"
@@ -154,7 +155,7 @@
android:layout_marginEnd="8dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
- tools:src="@drawable/ic_settings_black" />
+ tools:src="@drawable/ic_settings" />
<TextView
android:textAppearance="@style/TextAppearance.AppCompat.Button"
@@ -185,7 +186,8 @@
<de.danoeh.antennapod.view.NestedScrollableHost
android:layout_below="@id/header"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ app:preferVertical="3">
<de.danoeh.antennapod.view.ShownotesWebView
android:id="@+id/webvDescription"
diff --git a/app/src/main/res/layout/feeditemlist_header.xml b/app/src/main/res/layout/feeditemlist_header.xml
index 2b59845f7..540baf4a7 100644
--- a/app/src/main/res/layout/feeditemlist_header.xml
+++ b/app/src/main/res/layout/feeditemlist_header.xml
@@ -117,4 +117,15 @@
android:gravity="center"
tools:visibility="visible"
tools:text="(i) Information"/>
+
+ <TextView
+ android:id="@+id/txtvUpdatesDisabled"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="2dp"
+ android:background="?android:attr/windowBackground"
+ android:visibility="gone"
+ android:gravity="center"
+ tools:visibility="visible"
+ tools:text="Updates disabled"/>
</LinearLayout>
diff --git a/app/src/main/res/layout/feeditemlist_item.xml b/app/src/main/res/layout/feeditemlist_item.xml
index e1f382e46..37b88d1b5 100644
--- a/app/src/main/res/layout/feeditemlist_item.xml
+++ b/app/src/main/res/layout/feeditemlist_item.xml
@@ -35,7 +35,7 @@
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:scaleType="fitCenter"
- android:src="?attr/dragview_background"
+ app:srcCompat="?attr/dragview_background"
android:paddingStart="0dp"
android:paddingLeft="0dp"
android:paddingEnd="4dp"
@@ -114,24 +114,21 @@
<ImageView
android:layout_width="14sp"
android:layout_height="14sp"
- app:srcCompat="?attr/type_video"
- tools:srcCompat="@drawable/ic_videocam_black_24dp"
+ app:srcCompat="@drawable/ic_videocam"
android:contentDescription="@string/media_type_video_label"
android:id="@+id/ivIsVideo"/>
<ImageView
android:layout_width="14sp"
android:layout_height="14sp"
- app:srcCompat="?attr/ic_unfav"
- tools:srcCompat="@drawable/ic_star_black"
+ app:srcCompat="@drawable/ic_star"
android:contentDescription="@string/is_favorite_label"
android:id="@+id/isFavorite"/>
<ImageView
android:layout_width="14sp"
android:layout_height="14sp"
- app:srcCompat="?attr/stat_playlist"
- tools:srcCompat="@drawable/ic_playlist_black"
+ app:srcCompat="@drawable/ic_playlist"
android:contentDescription="@string/in_queue_label"
android:id="@+id/ivInPlaylist"/>
diff --git a/app/src/main/res/layout/feedsettings.xml b/app/src/main/res/layout/feedsettings.xml
index ec53703ab..acd1089bd 100644
--- a/app/src/main/res/layout/feedsettings.xml
+++ b/app/src/main/res/layout/feedsettings.xml
@@ -15,7 +15,7 @@
android:elevation="4dp"
android:id="@+id/toolbar"/>
- <FrameLayout
+ <androidx.fragment.app.FragmentContainerView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
diff --git a/app/src/main/res/layout/fragment_subscriptions.xml b/app/src/main/res/layout/fragment_subscriptions.xml
index d08e0c501..61d33f534 100644
--- a/app/src/main/res/layout/fragment_subscriptions.xml
+++ b/app/src/main/res/layout/fragment_subscriptions.xml
@@ -12,6 +12,7 @@
android:theme="?attr/actionBarTheme"
android:layout_alignParentTop="true"
app:title="@string/subscriptions_label"
+ app:navigationIcon="?homeAsUpIndicator"
android:id="@+id/toolbar"/>
<TextView
@@ -64,5 +65,5 @@
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:contentDescription="@string/add_feed_label"
- app:srcCompat="@drawable/ic_add_white"/>
+ app:srcCompat="@drawable/ic_add"/>
</RelativeLayout>
diff --git a/app/src/main/res/layout/gpodnet_podcast_listitem.xml b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
index 99991e919..56d351d62 100644
--- a/app/src/main/res/layout/gpodnet_podcast_listitem.xml
+++ b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
@@ -65,7 +65,7 @@
<ImageView
android:layout_width="14sp"
android:layout_height="14sp"
- app:srcCompat="?attr/feed"/>
+ app:srcCompat="@drawable/ic_feed"/>
<TextView
android:id="@+id/txtvSubscribers"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
diff --git a/app/src/main/res/layout/gpodnetauth_credentials.xml b/app/src/main/res/layout/gpodnetauth_credentials.xml
index 9fcf67cff..8cf6941c9 100644
--- a/app/src/main/res/layout/gpodnetauth_credentials.xml
+++ b/app/src/main/res/layout/gpodnetauth_credentials.xml
@@ -2,6 +2,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -15,7 +16,7 @@
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
- android:src="@drawable/gpodder_icon"/>
+ app:srcCompat="@drawable/gpodder_icon"/>
<TextView
android:id="@+id/createAccountButton"
diff --git a/app/src/main/res/layout/gpodnetauth_finish.xml b/app/src/main/res/layout/gpodnetauth_finish.xml
index f0bcfd4dc..8eced7304 100644
--- a/app/src/main/res/layout/gpodnetauth_finish.xml
+++ b/app/src/main/res/layout/gpodnetauth_finish.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -9,7 +10,7 @@
android:id="@id/icon"
android:layout_width="64dp"
android:layout_height="64dp"
- android:src="@drawable/gpodder_icon" />
+ app:srcCompat="@drawable/gpodder_icon" />
<TextView
android:id="@+id/txtvDescription"
diff --git a/app/src/main/res/layout/item_description_fragment.xml b/app/src/main/res/layout/item_description_fragment.xml
index 3766cf805..469cd4098 100644
--- a/app/src/main/res/layout/item_description_fragment.xml
+++ b/app/src/main/res/layout/item_description_fragment.xml
@@ -1,18 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
-<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<de.danoeh.antennapod.view.NestedScrollableHost xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fillViewport="false">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:fillViewport="false"
+ app:preferVertical="10"
+ android:nestedScrollingEnabled="true">
- <de.danoeh.antennapod.view.NestedScrollableHost
+ <de.danoeh.antennapod.view.ShownotesWebView
+ android:id="@+id/webview"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"/>
- <de.danoeh.antennapod.view.ShownotesWebView
- android:id="@+id/webview"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </de.danoeh.antennapod.view.NestedScrollableHost>
-
-</androidx.core.widget.NestedScrollView> \ No newline at end of file
+</de.danoeh.antennapod.view.NestedScrollableHost> \ No newline at end of file
diff --git a/app/src/main/res/layout/nav_list.xml b/app/src/main/res/layout/nav_list.xml
index ed850cc86..a24ea3fba 100644
--- a/app/src/main/res/layout/nav_list.xml
+++ b/app/src/main/res/layout/nav_list.xml
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -18,7 +19,6 @@
android:focusable="true">
<ImageView
- android:id="@+id/imgvCover"
android:layout_width="@dimen/thumbnail_length_navlist"
android:layout_height="@dimen/thumbnail_length_navlist"
android:layout_marginBottom="4dp"
@@ -30,7 +30,7 @@
android:cropToPadding="true"
android:padding="8dp"
android:scaleType="centerCrop"
- android:src="?attr/ic_settings"
+ app:srcCompat="@drawable/ic_settings"
tools:background="@android:color/holo_orange_dark"
tools:src="@android:drawable/sym_def_app_icon" />
@@ -57,7 +57,7 @@
android:background="?android:attr/listDivider"
tools:background="@android:color/holo_red_dark" />
- <ListView
+ <androidx.recyclerview.widget.RecyclerView
android:id="@+id/nav_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/nav_listitem.xml b/app/src/main/res/layout/nav_listitem.xml
index 3610ce56e..607b328e6 100644
--- a/app/src/main/res/layout/nav_listitem.xml
+++ b/app/src/main/res/layout/nav_listitem.xml
@@ -70,10 +70,11 @@
android:lines="1"
android:textColor="?android:attr/textColorTertiary"
android:textSize="14sp"
- android:layout_marginLeft="12dp"
- android:layout_marginStart="12dp"
- android:layout_marginRight="@dimen/listitem_icon_rightpadding"
- android:layout_marginEnd="@dimen/listitem_icon_rightpadding"
+ android:padding="8dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
+ android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
diff --git a/app/src/main/res/layout/onlinefeedview_activity.xml b/app/src/main/res/layout/onlinefeedview_activity.xml
index 909d676f0..f1297baf4 100644
--- a/app/src/main/res/layout/onlinefeedview_activity.xml
+++ b/app/src/main/res/layout/onlinefeedview_activity.xml
@@ -118,6 +118,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
+ android:dropDownWidth="match_parent"
android:padding="8dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_micro" />
diff --git a/app/src/main/res/layout/queue_fragment.xml b/app/src/main/res/layout/queue_fragment.xml
index 6a1851648..3bcd4819f 100644
--- a/app/src/main/res/layout/queue_fragment.xml
+++ b/app/src/main/res/layout/queue_fragment.xml
@@ -32,12 +32,19 @@
android:layout_below="@id/info_bar"
android:background="?android:attr/listDivider"/>
- <de.danoeh.antennapod.view.EpisodeItemListRecyclerView
- android:id="@+id/recyclerView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingHorizontal="@dimen/additional_horizontal_spacing"
- android:layout_below="@id/divider" />
+ <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+ android:id="@+id/swipeRefresh"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/divider">
+
+ <de.danoeh.antennapod.view.EpisodeItemListRecyclerView
+ android:id="@+id/recyclerView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="@dimen/additional_horizontal_spacing" />
+
+ </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<ProgressBar
android:id="@+id/progLoading"
diff --git a/app/src/main/res/layout/settings_activity.xml b/app/src/main/res/layout/settings_activity.xml
new file mode 100644
index 000000000..91fe40373
--- /dev/null
+++ b/app/src/main/res/layout/settings_activity.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/settingsContainer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/app/src/main/res/layout/storage_error.xml b/app/src/main/res/layout/storage_error.xml
index 7b0579927..6a7fda341 100644
--- a/app/src/main/res/layout/storage_error.xml
+++ b/app/src/main/res/layout/storage_error.xml
@@ -14,7 +14,7 @@
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_margin="8dp"
- app:srcCompat="?attr/storage" />
+ app:srcCompat="@drawable/ic_storage" />
<TextView
android:id="@+id/textView1"
diff --git a/app/src/main/res/layout/videoplayer_activity.xml b/app/src/main/res/layout/videoplayer_activity.xml
index e0632ef41..fcc1c5f15 100644
--- a/app/src/main/res/layout/videoplayer_activity.xml
+++ b/app/src/main/res/layout/videoplayer_activity.xml
@@ -6,16 +6,16 @@
xmlns:tools="http://schemas.android.com/tools"
android:background="@color/black"
android:orientation="vertical"
- android:id="@+id/videoframe">
+ android:id="@+id/videoPlayerContainer">
<de.danoeh.antennapod.view.AspectRatioVideoView
- android:id="@+id/videoview"
+ android:id="@+id/videoView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<ProgressBar
- android:id="@+id/progressIndicator"
+ android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@@ -23,7 +23,7 @@
android:visibility="invisible" />
<LinearLayout
- android:id="@+id/controls"
+ android:id="@+id/controlsContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@@ -33,36 +33,36 @@
android:orientation="horizontal">
<ImageButton
- android:id="@+id/butRev"
+ android:id="@+id/rewindButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/rewind_label"
- app:srcCompat="@drawable/ic_av_fast_rewind_white_80dp" />
+ app:srcCompat="@drawable/ic_fast_rewind_video_white" />
- <ImageButton
- android:id="@+id/butPlay"
+ <de.danoeh.antennapod.view.PlayButton
+ android:id="@+id/playButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/pause_label"
- app:srcCompat="@drawable/ic_av_pause_white_80dp" />
+ app:srcCompat="@drawable/ic_pause_video_white" />
<ImageButton
- android:id="@+id/butFF"
+ android:id="@+id/fastForwardButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/fast_forward_label"
- app:srcCompat="@drawable/ic_av_fast_forward_white_80dp" />
+ app:srcCompat="@drawable/ic_fast_forward_video_white" />
</LinearLayout>
<ImageView
- android:id="@+id/skip_animation"
+ android:id="@+id/skipAnimationImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
@@ -70,14 +70,14 @@
android:layout_gravity="center"/>
<LinearLayout
- android:id="@+id/overlay"
+ android:id="@+id/bottomControlsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:orientation="vertical">
<androidx.cardview.widget.CardView
- android:id="@+id/cardViewSeek"
+ android:id="@+id/seekCardView"
android:alpha="0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -89,7 +89,7 @@
tools:alpha="1">
<TextView
- android:id="@+id/txtvSeek"
+ android:id="@+id/seekPositionLabel"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -104,7 +104,6 @@
</androidx.cardview.widget.CardView>
<RelativeLayout
- android:id="@+id/timecontrol"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#80000000"
@@ -112,7 +111,7 @@
android:paddingTop="8dp">
<TextView
- android:id="@+id/txtvPosition"
+ android:id="@+id/positionLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
@@ -129,7 +128,7 @@
android:textStyle="bold" />
<TextView
- android:id="@+id/txtvLength"
+ android:id="@+id/durationLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
@@ -149,10 +148,10 @@
android:id="@+id/sbPosition"
android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_toLeftOf="@+id/txtvLength"
- android:layout_toStartOf="@+id/txtvLength"
- android:layout_toRightOf="@+id/txtvPosition"
- android:layout_toEndOf="@+id/txtvPosition"
+ android:layout_toLeftOf="@+id/durationLabel"
+ android:layout_toStartOf="@+id/durationLabel"
+ android:layout_toRightOf="@+id/positionLabel"
+ android:layout_toEndOf="@+id/positionLabel"
android:layout_centerInParent="true"
android:max="500" />
diff --git a/app/src/main/res/menu/downloads.xml b/app/src/main/res/menu/downloads.xml
index c8ee20e35..142f251fc 100644
--- a/app/src/main/res/menu/downloads.xml
+++ b/app/src/main/res/menu/downloads.xml
@@ -5,14 +5,14 @@
android:id="@+id/episode_actions"
android:menuCategory="container"
android:title="@string/multi_select"
- android:icon="?attr/checkbox_multiple"
+ android:icon="@drawable/ic_check_multiple"
android:visible="false"
app:showAsAction="ifRoom" />
<item
android:id="@+id/clear_logs_item"
android:menuCategory="container"
android:title="@string/clear_history_label"
- android:icon="?attr/ic_delete"
+ android:icon="@drawable/ic_delete"
android:visible="false"
app:showAsAction="ifRoom" />
<item
@@ -20,5 +20,5 @@
android:title="@string/refresh_label"
android:menuCategory="container"
app:showAsAction="ifRoom"
- android:icon="?attr/navigation_refresh"/>
+ android:icon="@drawable/ic_refresh"/>
</menu>
diff --git a/app/src/main/res/menu/episodes.xml b/app/src/main/res/menu/episodes.xml
index 2fac77375..22121c905 100644
--- a/app/src/main/res/menu/episodes.xml
+++ b/app/src/main/res/menu/episodes.xml
@@ -5,7 +5,7 @@
<item
android:id="@+id/action_search"
- android:icon="?attr/action_search"
+ android:icon="@drawable/ic_search"
custom:showAsAction="collapseActionView|always"
custom:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
@@ -15,11 +15,11 @@
android:title="@string/refresh_label"
android:menuCategory="container"
custom:showAsAction="always"
- android:icon="?attr/navigation_refresh"/>
+ android:icon="@drawable/ic_refresh"/>
<item
android:id="@+id/filter_items"
- android:icon="?attr/ic_filter"
+ android:icon="@drawable/ic_filter"
android:menuCategory="container"
android:title="@string/filter"
android:visible="false"
@@ -31,7 +31,7 @@
android:menuCategory="container"
custom:showAsAction="collapseActionView"
android:visible="false"
- android:icon="?attr/navigation_accept"/>
+ android:icon="@drawable/ic_check"/>
<item
android:id="@+id/remove_all_new_flags_item"
@@ -39,6 +39,6 @@
android:menuCategory="container"
custom:showAsAction="collapseActionView"
android:visible="false"
- android:icon="?attr/navigation_accept"/>
+ android:icon="@drawable/ic_check"/>
</menu>
diff --git a/app/src/main/res/menu/episodes_apply_action_options.xml b/app/src/main/res/menu/episodes_apply_action_options.xml
index 181300fc5..221ec4d59 100644
--- a/app/src/main/res/menu/episodes_apply_action_options.xml
+++ b/app/src/main/res/menu/episodes_apply_action_options.xml
@@ -4,7 +4,7 @@
<item
android:id="@+id/sort"
- android:icon="?attr/ic_sort"
+ android:icon="@drawable/ic_sort"
android:title="@string/sort"
app:showAsAction="always">
<menu>
@@ -25,7 +25,7 @@
<item
android:id="@+id/select_options"
- android:icon="?attr/ic_filter"
+ android:icon="@drawable/ic_filter"
android:title="@string/filter"
app:showAsAction="always">
diff --git a/app/src/main/res/menu/episodes_apply_action_speeddial.xml b/app/src/main/res/menu/episodes_apply_action_speeddial.xml
index 370cedd90..a2f509ec5 100644
--- a/app/src/main/res/menu/episodes_apply_action_speeddial.xml
+++ b/app/src/main/res/menu/episodes_apply_action_speeddial.xml
@@ -6,29 +6,29 @@
visually it will be shown at the bottom of the list of actions.
-->
<item android:id="@+id/delete_batch"
- android:icon="?attr/ic_delete"
+ android:icon="@drawable/ic_delete"
android:title="@string/delete_episode_label"
/>
<item android:id="@+id/download_batch"
- android:icon="?attr/av_download"
+ android:icon="@drawable/ic_download"
android:title="@string/download_label"
/>
<item android:id="@+id/mark_unread_batch"
- android:icon="?attr/navigation_cancel"
+ android:icon="@drawable/ic_cancel"
android:title="@string/mark_unread_label"
/>
<item
android:id="@+id/mark_read_batch"
- android:icon="?attr/navigation_accept"
+ android:icon="@drawable/ic_check"
android:title="@string/mark_read_label"
/>
<item android:id="@+id/remove_from_queue_batch"
- android:icon="?attr/content_remove_from_queue"
+ android:icon="@drawable/ic_remove"
android:title="@string/remove_from_queue_label"
/>
<item
android:id="@+id/add_to_queue_batch"
- android:icon="?attr/content_new"
+ android:icon="@drawable/ic_add"
android:title="@string/add_to_queue_label"
/>
</menu>
diff --git a/app/src/main/res/menu/feedinfo.xml b/app/src/main/res/menu/feedinfo.xml
index a5fbe0c20..83ad079b4 100644
--- a/app/src/main/res/menu/feedinfo.xml
+++ b/app/src/main/res/menu/feedinfo.xml
@@ -3,7 +3,7 @@
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/visit_website_item"
- android:icon="?attr/location_web_site"
+ android:icon="@drawable/ic_web"
custom:showAsAction="ifRoom|collapseActionView"
android:title="@string/visit_website_label"
android:visible="true"/>
@@ -11,7 +11,7 @@
android:id="@+id/share_parent"
custom:showAsAction="ifRoom"
android:title="@string/share_label_with_ellipses"
- android:icon="?attr/ic_share"
+ android:icon="@drawable/ic_share"
android:visible="true">
<menu android:id="@+id/share_submenu">
<item
diff --git a/app/src/main/res/menu/feeditem_options.xml b/app/src/main/res/menu/feeditem_options.xml
index c29229e37..5b33539e1 100644
--- a/app/src/main/res/menu/feeditem_options.xml
+++ b/app/src/main/res/menu/feeditem_options.xml
@@ -64,7 +64,7 @@
<item
android:id="@+id/visit_website_item"
- android:icon="?attr/location_web_site"
+ android:icon="@drawable/ic_web"
custom:showAsAction="ifRoom|collapseActionView"
android:title="@string/visit_website_label">
</item>
diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml
index 3614cfffa..85e7a95ba 100644
--- a/app/src/main/res/menu/feedlist.xml
+++ b/app/src/main/res/menu/feedlist.xml
@@ -4,21 +4,21 @@
<item
android:id="@+id/sort_items"
- android:icon="?attr/ic_sort"
+ android:icon="@drawable/ic_sort"
android:menuCategory="container"
android:title="@string/sort"
custom:showAsAction="always">
</item>
<item
android:id="@+id/filter_items"
- android:icon="?attr/ic_filter"
+ android:icon="@drawable/ic_filter"
android:menuCategory="container"
android:title="@string/filter"
custom:showAsAction="always">
</item>
<item
android:id="@+id/refresh_item"
- android:icon="?attr/navigation_refresh"
+ android:icon="@drawable/ic_refresh"
android:menuCategory="container"
android:title="@string/refresh_label"
custom:showAsAction="always">
@@ -32,7 +32,7 @@
<item
android:id="@+id/action_search"
- android:icon="?attr/action_search"
+ android:icon="@drawable/ic_search"
custom:showAsAction="always|collapseActionView"
custom:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
@@ -40,13 +40,13 @@
<item
android:id="@+id/episode_actions"
android:menuCategory="container"
- android:icon="?attr/checkbox_multiple"
+ android:icon="@drawable/ic_check_multiple"
android:title="@string/multi_select"
custom:showAsAction="collapseActionView">
</item>
<item
android:id="@+id/visit_website_item"
- android:icon="?attr/location_web_site"
+ android:icon="@drawable/ic_web"
android:menuCategory="container"
custom:showAsAction="collapseActionView"
android:title="@string/visit_website_label"
@@ -79,7 +79,7 @@
<item
android:id="@+id/remove_item"
- android:icon="?attr/ic_delete"
+ android:icon="@drawable/ic_delete"
android:menuCategory="container"
android:title="@string/remove_feed_label"
android:visible="true"
diff --git a/app/src/main/res/menu/mediaplayer.xml b/app/src/main/res/menu/mediaplayer.xml
index 8afdba369..a9f15317b 100644
--- a/app/src/main/res/menu/mediaplayer.xml
+++ b/app/src/main/res/menu/mediaplayer.xml
@@ -4,40 +4,40 @@
<item
android:id="@+id/add_to_favorites_item"
- android:icon="?attr/ic_fav"
+ android:icon="@drawable/ic_star_border"
android:title="@string/add_to_favorite_label"
custom:showAsAction="always">
</item>
<item
android:id="@+id/remove_from_favorites_item"
- android:icon="?attr/ic_unfav"
+ android:icon="@drawable/ic_star"
android:title="@string/remove_from_favorite_label"
custom:showAsAction="always">
</item>
<item
android:id="@+id/disable_sleeptimer_item"
- android:icon="?attr/ic_sleep_off"
+ android:icon="@drawable/ic_sleep_off"
custom:showAsAction="always"
android:title="@string/sleep_timer_label">
</item>
<item
android:id="@+id/set_sleeptimer_item"
- android:icon="?attr/ic_sleep"
+ android:icon="@drawable/ic_sleep"
custom:showAsAction="always"
android:title="@string/set_sleeptimer_label">
</item>
<item
android:id="@+id/audio_controls"
- android:icon="?attr/ic_sliders"
+ android:icon="@drawable/ic_sliders"
android:title="@string/audio_controls"
custom:showAsAction="always">
</item>
<item
android:id="@+id/open_feed_item"
- android:icon="?attr/feed"
+ android:icon="@drawable/ic_feed"
custom:showAsAction="collapseActionView"
android:title="@string/open_podcast"
android:visible="false">
@@ -45,7 +45,7 @@
<item
android:id="@+id/visit_website_item"
- android:icon="?attr/location_web_site"
+ android:icon="@drawable/ic_web"
custom:showAsAction="collapseActionView"
android:title="@string/visit_website_label"
android:visible="false">
diff --git a/app/src/main/res/menu/online_search.xml b/app/src/main/res/menu/online_search.xml
index 93d93157a..374a054fa 100644
--- a/app/src/main/res/menu/online_search.xml
+++ b/app/src/main/res/menu/online_search.xml
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
- android:icon="?attr/action_search"
+ android:icon="@drawable/ic_search"
custom:showAsAction="collapseActionView|ifRoom"
custom:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
diff --git a/app/src/main/res/menu/opml_selection_options.xml b/app/src/main/res/menu/opml_selection_options.xml
index 8b3310dc2..1b7bc9331 100644
--- a/app/src/main/res/menu/opml_selection_options.xml
+++ b/app/src/main/res/menu/opml_selection_options.xml
@@ -4,14 +4,14 @@
<item
android:id="@id/select_all_item"
- android:icon="?attr/ic_select_all"
+ android:icon="@drawable/ic_select_all"
android:title="@string/select_all_label"
custom:showAsAction="ifRoom">
</item>
<item
android:id="@id/deselect_all_item"
- android:icon="?attr/ic_select_none"
+ android:icon="@drawable/ic_select_none"
android:title="@string/deselect_all_label"
custom:showAsAction="ifRoom">
</item>
diff --git a/app/src/main/res/menu/playback_history.xml b/app/src/main/res/menu/playback_history.xml
index 5362f0a25..f8cfd76b0 100644
--- a/app/src/main/res/menu/playback_history.xml
+++ b/app/src/main/res/menu/playback_history.xml
@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/clear_history_item"
- android:icon="?attr/ic_delete"
+ android:icon="@drawable/ic_delete"
android:title="@string/clear_history_label"
app:showAsAction="ifRoom"/>
</menu>
diff --git a/app/src/main/res/menu/queue.xml b/app/src/main/res/menu/queue.xml
index 43702e07f..adf44b8b1 100644
--- a/app/src/main/res/menu/queue.xml
+++ b/app/src/main/res/menu/queue.xml
@@ -14,11 +14,11 @@
android:title="@string/refresh_label"
android:menuCategory="container"
custom:showAsAction="always"
- android:icon="?attr/navigation_refresh"/>
+ android:icon="@drawable/ic_refresh"/>
<item
android:id="@+id/action_search"
- android:icon="?attr/action_search"
+ android:icon="@drawable/ic_search"
custom:showAsAction="collapseActionView|ifRoom"
custom:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
@@ -114,7 +114,7 @@
android:id="@+id/clear_queue"
android:title="@string/clear_queue_label"
custom:showAsAction="collapseActionView"
- android:icon="?attr/navigation_accept"/>
+ android:icon="@drawable/ic_check"/>
<item
android:id="@+id/episode_actions"
diff --git a/app/src/main/res/menu/search.xml b/app/src/main/res/menu/search.xml
index d3a2477be..f6ef15516 100644
--- a/app/src/main/res/menu/search.xml
+++ b/app/src/main/res/menu/search.xml
@@ -4,7 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
- android:icon="?attr/action_search"
+ android:icon="@drawable/ic_search"
app:showAsAction="collapseActionView|always"
app:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
diff --git a/app/src/main/res/menu/statistics.xml b/app/src/main/res/menu/statistics.xml
index eb2a51550..9e4b7fab1 100644
--- a/app/src/main/res/menu/statistics.xml
+++ b/app/src/main/res/menu/statistics.xml
@@ -10,7 +10,7 @@
<item
android:id="@+id/statistics_mode"
- android:icon="?attr/ic_filter"
+ android:icon="@drawable/ic_filter"
android:title="@string/statistics_mode"
custom:showAsAction="never">
</item>
diff --git a/app/src/main/res/menu/subscriptions.xml b/app/src/main/res/menu/subscriptions.xml
index 99acc4bb6..b1cc89eb6 100644
--- a/app/src/main/res/menu/subscriptions.xml
+++ b/app/src/main/res/menu/subscriptions.xml
@@ -7,7 +7,7 @@
android:title="@string/refresh_label"
android:menuCategory="container"
custom:showAsAction="always"
- android:icon="?attr/navigation_refresh"/>
+ android:icon="@drawable/ic_refresh"/>
<item
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 000000000..4875b3f53
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="NestedScrollableHost">
+ <attr name="scrollDirection" format="enum">
+ <enum name="both" value="0"/>
+ <enum name="vertical" value="1"/>
+ <enum name="horizontal" value="2"/>
+ </attr>
+ <attr name="preferHorizontal" format="integer"/>
+ <attr name="preferVertical" format="integer"/>
+ </declare-styleable>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/svg.xml b/app/src/main/res/values/svg.xml
new file mode 100644
index 000000000..6fd4ff0c4
--- /dev/null
+++ b/app/src/main/res/values/svg.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="svg_animatable_play" translatable="false">M 8 5 L 8 12 L 19 12 L 19 12 M 8 19 L 8 12 L 19 12 L 19 12</string>
+ <string name="svg_animatable_pause" translatable="false">M 5 6 L 5 10 L 19 10 L 19 6 M 5 18 L 5 14 L 19 14 L 19 18</string>
+</resources>
diff --git a/app/src/main/res/xml/feed_settings.xml b/app/src/main/res/xml/feed_settings.xml
index 13288fbda..457ff6e5b 100644
--- a/app/src/main/res/xml/feed_settings.xml
+++ b/app/src/main/res/xml/feed_settings.xml
@@ -5,7 +5,7 @@
<SwitchPreferenceCompat
android:key="keepUpdated"
- android:icon="?attr/navigation_refresh"
+ android:icon="@drawable/ic_refresh"
android:title="@string/keep_updated"
android:summary="@string/keep_updated_summary"/>
@@ -13,32 +13,38 @@
android:key="episodeNotification"
android:defaultValue="false"
android:dependency="keepUpdated"
- android:icon="?attr/ic_notifications"
+ android:icon="@drawable/ic_notifications"
android:title="@string/episode_notification"
android:summary="@string/episode_notification_summary"/>
<Preference
android:key="authentication"
- android:icon="?attr/ic_key"
+ android:icon="@drawable/ic_key"
android:title="@string/authentication_label"
android:summary="@string/authentication_descr"/>
+ <Preference
+ android:key="tags"
+ android:icon="@drawable/ic_folder"
+ android:title="@string/feed_folders_label"
+ android:summary="@string/feed_folders_summary"/>
+
<ListPreference
android:key="feedPlaybackSpeed"
- android:icon="?attr/ic_settings_speed"
+ android:icon="@drawable/ic_playback_speed"
android:title="@string/playback_speed"
android:summary="@string/pref_feed_playback_speed_sum"/>
<Preference
android:key="feedAutoSkip"
- android:icon="?attr/ic_settings_skip"
+ android:icon="@drawable/ic_skip_24dp"
android:summary="@string/pref_feed_skip_sum"
android:title="@string/pref_feed_skip" />
<ListPreference
android:entries="@array/spnAutoDeleteItems"
android:entryValues="@array/spnAutoDeleteValues"
- android:icon="?attr/ic_delete"
+ android:icon="@drawable/ic_delete"
android:title="@string/auto_delete_label"
android:summary="@string/feed_auto_download_global"
android:key="autoDelete"/>
@@ -46,7 +52,7 @@
<ListPreference
android:entries="@array/spnVolumeReductionItems"
android:entryValues="@array/spnVolumeReductionValues"
- android:icon="?attr/ic_volume_adaption"
+ android:icon="@drawable/ic_volume_adaption"
android:summary="@string/feed_volume_reduction_summary"
android:title="@string/feed_volume_reduction"
android:defaultValue="off"
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 805dff47d..523c7cd0f 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -13,41 +13,41 @@
android:key="prefScreenInterface"
android:title="@string/user_interface_label"
android:summary="@string/user_interface_sum"
- android:icon="?attr/ic_appearance" />
+ android:icon="@drawable/ic_appearance" />
<Preference
android:key="prefScreenPlayback"
android:title="@string/playback_pref"
android:summary="@string/playback_pref_sum"
- android:icon="?attr/ic_settings_playback" />
+ android:icon="@drawable/ic_play_24dp" />
<Preference
android:key="prefScreenNetwork"
android:title="@string/network_pref"
android:summary="@string/network_pref_sum"
- android:icon="?attr/ic_network" />
+ android:icon="@drawable/ic_network" />
<Preference
android:key="prefScreenGpodder"
android:title="@string/synchronization_pref"
android:summary="@string/synchronization_sum"
- android:icon="?attr/ic_unfav" />
+ android:icon="@drawable/ic_star" />
<Preference
android:key="prefScreenStorage"
android:title="@string/storage_pref"
android:summary="@string/storage_sum"
- android:icon="?attr/storage" />
+ android:icon="@drawable/ic_storage" />
<Preference
android:key="notifications"
android:title="@string/notification_pref_fragment"
- android:icon="?attr/ic_notifications"/>
+ android:icon="@drawable/ic_notifications"/>
<Preference
android:key="statistics"
android:title="@string/statistics_label"
- android:icon="?attr/statistics" />
+ android:icon="@drawable/ic_statistics" />
<PreferenceCategory
android:key="project"
@@ -55,18 +55,22 @@
<Preference
android:key="prefDocumentation"
android:title="@string/documentation_support"
- android:icon="?attr/ic_questionmark" />
+ android:icon="@drawable/ic_questionmark" />
<Preference
android:key="prefViewForum"
android:title="@string/visit_user_forum"
- android:icon="?attr/ic_chat" />
+ android:icon="@drawable/ic_chat" />
+ <Preference
+ android:key="prefContribute"
+ android:title="@string/pref_contribute"
+ android:icon="@drawable/ic_contribute" />
<Preference
android:key="prefSendBugReport"
android:title="@string/bug_report_title"
- android:icon="?attr/ic_bug" />
+ android:icon="@drawable/ic_bug" />
<Preference
android:key="prefAbout"
android:title="@string/about_pref"
- android:icon="?attr/action_about" />
+ android:icon="@drawable/ic_info" />
</PreferenceCategory>
</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_about.xml b/app/src/main/res/xml/preferences_about.xml
index f56b7f2ac..1312d5466 100644
--- a/app/src/main/res/xml/preferences_about.xml
+++ b/app/src/main/res/xml/preferences_about.xml
@@ -7,21 +7,21 @@
<Preference
android:key="about_version"
android:title="@string/antennapod_version"
- android:icon="?attr/ic_unfav"
+ android:icon="@drawable/ic_star"
android:summary="1.7.2 (asd8qs)"/>
<Preference
android:key="about_contributors"
- android:icon="?attr/ic_settings"
+ android:icon="@drawable/ic_settings"
android:summary="@string/contributors_summary"
android:title="@string/contributors"/>
<Preference
android:key="about_privacy_policy"
- android:icon="?attr/ic_questionmark"
+ android:icon="@drawable/ic_questionmark"
android:summary="www.antennapod.org/privacy"
android:title="@string/privacy_policy"/>
<Preference
android:key="about_licenses"
- android:icon="?attr/action_about"
+ android:icon="@drawable/ic_info"
android:summary="@string/licenses_summary"
android:title="@string/licenses"/>
diff --git a/app/src/main/res/xml/preferences_network.xml b/app/src/main/res/xml/preferences_network.xml
index 0dbd1acee..428c891ad 100644
--- a/app/src/main/res/xml/preferences_network.xml
+++ b/app/src/main/res/xml/preferences_network.xml
@@ -6,8 +6,8 @@
<PreferenceCategory android:title="@string/automation">
<Preference
android:key="prefAutoUpdateIntervall"
- android:summary="@string/pref_autoUpdateIntervallOrTime_sum"
- android:title="@string/pref_autoUpdateIntervallOrTime_title"/>
+ android:summary="@string/feed_refresh_sum"
+ android:title="@string/feed_refresh_title"/>
<Preference
android:summary="@string/pref_automatic_download_sum"
android:key="prefAutoDownloadSettings"