diff options
Diffstat (limited to 'core')
32 files changed, 179 insertions, 164 deletions
diff --git a/core/src/debug/res/mipmap-hdpi/ic_launcher.png b/core/src/debug/res/mipmap-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 6f8022e25..000000000 --- a/core/src/debug/res/mipmap-hdpi/ic_launcher.png +++ /dev/null diff --git a/core/src/debug/res/mipmap-hdpi/ic_launcher_background.png b/core/src/debug/res/mipmap-hdpi/ic_launcher_background.png Binary files differnew file mode 100644 index 000000000..da2b8d47b --- /dev/null +++ b/core/src/debug/res/mipmap-hdpi/ic_launcher_background.png diff --git a/core/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png b/core/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 000000000..dd7c03e27 --- /dev/null +++ b/core/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png diff --git a/core/src/debug/res/mipmap-mdpi/ic_launcher.png b/core/src/debug/res/mipmap-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index d542d555f..000000000 --- a/core/src/debug/res/mipmap-mdpi/ic_launcher.png +++ /dev/null diff --git a/core/src/debug/res/mipmap-mdpi/ic_launcher_background.png b/core/src/debug/res/mipmap-mdpi/ic_launcher_background.png Binary files differnew file mode 100644 index 000000000..701d43516 --- /dev/null +++ b/core/src/debug/res/mipmap-mdpi/ic_launcher_background.png diff --git a/core/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png b/core/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 000000000..b7a85063f --- /dev/null +++ b/core/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png diff --git a/core/src/debug/res/mipmap-xhdpi/ic_launcher.png b/core/src/debug/res/mipmap-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index a02ec4ca8..000000000 --- a/core/src/debug/res/mipmap-xhdpi/ic_launcher.png +++ /dev/null diff --git a/core/src/debug/res/mipmap-xhdpi/ic_launcher_background.png b/core/src/debug/res/mipmap-xhdpi/ic_launcher_background.png Binary files differnew file mode 100644 index 000000000..1adaed041 --- /dev/null +++ b/core/src/debug/res/mipmap-xhdpi/ic_launcher_background.png diff --git a/core/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png b/core/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 000000000..4d5a15f39 --- /dev/null +++ b/core/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/core/src/debug/res/mipmap-xxhdpi/ic_launcher.png b/core/src/debug/res/mipmap-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 066f9e5a5..000000000 --- a/core/src/debug/res/mipmap-xxhdpi/ic_launcher.png +++ /dev/null diff --git a/core/src/debug/res/mipmap-xxhdpi/ic_launcher_background.png b/core/src/debug/res/mipmap-xxhdpi/ic_launcher_background.png Binary files differnew file mode 100644 index 000000000..31d4b272e --- /dev/null +++ b/core/src/debug/res/mipmap-xxhdpi/ic_launcher_background.png diff --git a/core/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png b/core/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 000000000..75caa42c9 --- /dev/null +++ b/core/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/core/src/debug/res/mipmap-xxxhdpi/ic_launcher.png b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 7dbab284c..000000000 --- a/core/src/debug/res/mipmap-xxxhdpi/ic_launcher.png +++ /dev/null diff --git a/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.png b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.png Binary files differnew file mode 100644 index 000000000..9edb9ab01 --- /dev/null +++ b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.png diff --git a/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 000000000..630363954 --- /dev/null +++ b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png Binary files differnew file mode 100644 index 000000000..d7e12bda3 --- /dev/null +++ b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png diff --git a/core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java b/core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java index 3b9d6a08d..829add126 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java @@ -3,30 +3,12 @@ package de.danoeh.antennapod.core.menuhandler; import android.view.Menu; import android.view.MenuItem; -import de.danoeh.antennapod.core.R; - /** * Utilities for menu items */ public class MenuItemUtils { /** - * @param menu The menu that the MenuItem belongs to - * @param resId The id of the MenuItem - */ - public static void updateRefreshMenuItem(Menu menu, int resId, boolean isRefreshing) { - // expand actionview if feeds are being downloaded, collapse otherwise - MenuItem refreshItem = menu.findItem(resId); - if (isRefreshing) { - if (refreshItem.getActionView() == null) { - refreshItem.setActionView(R.layout.refresh_action_view); - } - } else { - refreshItem.setActionView(null); - } - } - - /** * When pressing a context menu item, Android calls onContextItemSelected * for ALL fragments in arbitrary order, not just for the fragment that the * context menu was created from. This assigns the listener to every menu item, diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java index 08bdd39bc..161af58e1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java +++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java @@ -17,6 +17,8 @@ import de.danoeh.antennapod.core.ClientConfigurator; public class MediaButtonReceiver extends BroadcastReceiver { private static final String TAG = "MediaButtonReceiver"; public static final String EXTRA_KEYCODE = "de.danoeh.antennapod.core.service.extra.MediaButtonReceiver.KEYCODE"; + public static final String EXTRA_CUSTOM_ACTION = + "de.danoeh.antennapod.core.service.extra.MediaButtonReceiver.CUSTOM_ACTION"; public static final String EXTRA_SOURCE = "de.danoeh.antennapod.core.service.extra.MediaButtonReceiver.SOURCE"; public static final String EXTRA_HARDWAREBUTTON = "de.danoeh.antennapod.core.service.extra.MediaButtonReceiver.HARDWAREBUTTON"; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java index 5c8dead81..02102db14 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java @@ -63,10 +63,12 @@ import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; import de.danoeh.antennapod.core.receiver.MediaButtonReceiver; import de.danoeh.antennapod.core.service.QuickSettingsTileService; +import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager.SleepTimer; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.FeedSearcher; import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink; +import de.danoeh.antennapod.core.util.ChapterUtils; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.FeedUtil; import de.danoeh.antennapod.core.util.IntentUtils; @@ -84,12 +86,12 @@ import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent; import de.danoeh.antennapod.event.settings.SkipIntroEndingChangedEvent; import de.danoeh.antennapod.event.settings.SpeedPresetChangedEvent; import de.danoeh.antennapod.event.settings.VolumeAdaptionChangedEvent; +import de.danoeh.antennapod.model.feed.Chapter; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedItemFilter; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.model.feed.FeedPreferences; -import de.danoeh.antennapod.model.feed.SortOrder; import de.danoeh.antennapod.model.playback.MediaType; import de.danoeh.antennapod.model.playback.Playable; import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer; @@ -127,6 +129,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { private static final String CUSTOM_ACTION_REWIND = "action.de.danoeh.antennapod.core.service.rewind"; private static final String CUSTOM_ACTION_CHANGE_PLAYBACK_SPEED = "action.de.danoeh.antennapod.core.service.changePlaybackSpeed"; + public static final String CUSTOM_ACTION_NEXT_CHAPTER = "action.de.danoeh.antennapod.core.service.next_chapter"; /** * Set a max number of episodes to load for Android Auto, otherwise there could be performance issues @@ -327,7 +330,8 @@ public class PlaybackService extends MediaBrowserServiceCompat { if (rootHints != null && rootHints.getBoolean(BrowserRoot.EXTRA_RECENT)) { Bundle extras = new Bundle(); extras.putBoolean(BrowserRoot.EXTRA_RECENT, true); - return new BrowserRoot(getResources().getString(R.string.recently_played_episodes), extras); + Log.d(TAG, "OnGetRoot: Returning BrowserRoot " + R.string.current_playing_episode); + return new BrowserRoot(getResources().getString(R.string.current_playing_episode), extras); } // Name visible in Android Auto @@ -408,6 +412,11 @@ public class PlaybackService extends MediaBrowserServiceCompat { private List<MediaBrowserCompat.MediaItem> loadChildrenSynchronous(@NonNull String parentId) { List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>(); if (parentId.equals(getResources().getString(R.string.app_name))) { + long currentlyPlaying = PlaybackPreferences.getCurrentPlayerStatus(); + if (currentlyPlaying == PlaybackPreferences.PLAYER_STATUS_PLAYING + || currentlyPlaying == PlaybackPreferences.PLAYER_STATUS_PAUSED) { + mediaItems.add(createBrowsableMediaItem(R.string.current_playing_episode, R.drawable.ic_play_48dp, 1)); + } mediaItems.add(createBrowsableMediaItem(R.string.queue_label, R.drawable.ic_playlist_play_black, DBReader.getTotalEpisodeCount(new FeedItemFilter(FeedItemFilter.QUEUED)))); mediaItems.add(createBrowsableMediaItem(R.string.downloads_label, R.drawable.ic_download_black, @@ -429,11 +438,12 @@ public class PlaybackService extends MediaBrowserServiceCompat { new FeedItemFilter(FeedItemFilter.DOWNLOADED), UserPreferences.getDownloadsSortedOrder()); } else if (parentId.equals(getResources().getString(R.string.episodes_label))) { feedItems = DBReader.getEpisodes(0, MAX_ANDROID_AUTO_EPISODES_PER_FEED, - new FeedItemFilter(FeedItemFilter.UNPLAYED), SortOrder.DATE_NEW_OLD); + new FeedItemFilter(FeedItemFilter.UNPLAYED), UserPreferences.getAllEpisodesSortOrder()); } else if (parentId.startsWith("FeedId:")) { long feedId = Long.parseLong(parentId.split(":")[1]); - feedItems = DBReader.getFeedItemList(DBReader.getFeed(feedId)); - } else if (parentId.equals(getString(R.string.recently_played_episodes))) { + Feed feed = DBReader.getFeed(feedId); + feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), feed.getSortOrder()); + } else if (parentId.equals(getString(R.string.current_playing_episode))) { Playable playable = PlaybackPreferences.createInstanceFromPreferences(this); if (playable instanceof FeedMedia) { feedItems = Collections.singletonList(((FeedMedia) playable).getItem()); @@ -476,9 +486,10 @@ public class PlaybackService extends MediaBrowserServiceCompat { notificationManager.cancel(R.id.notification_streaming_confirmation); final int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1); + final String customAction = intent.getStringExtra(MediaButtonReceiver.EXTRA_CUSTOM_ACTION); final boolean hardwareButton = intent.getBooleanExtra(MediaButtonReceiver.EXTRA_HARDWAREBUTTON, false); Playable playable = intent.getParcelableExtra(PlaybackServiceInterface.EXTRA_PLAYABLE); - if (keycode == -1 && playable == null) { + if (keycode == -1 && playable == null && customAction == null) { Log.e(TAG, "PlaybackService was started with no arguments"); stateManager.stopService(); return Service.START_NOT_STICKY; @@ -502,7 +513,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { stateManager.stopService(); return Service.START_NOT_STICKY; } - } else { + } else if (playable != null) { stateManager.validStartCommandWasReceived(); boolean allowStreamThisTime = intent.getBooleanExtra( PlaybackServiceInterface.EXTRA_ALLOW_STREAM_THIS_TIME, false); @@ -530,6 +541,8 @@ public class PlaybackService extends MediaBrowserServiceCompat { stateManager.stopService(); }); return Service.START_NOT_STICKY; + } else { + mediaSession.getController().getTransportControls().sendCustomAction(customAction, null); } } @@ -777,6 +790,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { @Override public void onChapterLoaded(Playable media) { sendNotificationBroadcast(PlaybackServiceInterface.NOTIFICATION_TYPE_RELOAD, 0); + updateMediaSession(mediaPlayer.getPlayerStatus()); } }; @@ -965,7 +979,10 @@ public class PlaybackService extends MediaBrowserServiceCompat { if (event.isOver()) { mediaPlayer.pause(true, true); mediaPlayer.setVolume(1.0f, 1.0f); - } else if (event.getTimeLeft() < PlaybackServiceTaskManager.SleepTimer.NOTIFICATION_THRESHOLD) { + int newPosition = mediaPlayer.getPosition() - (int) SleepTimer.NOTIFICATION_THRESHOLD / 2; + newPosition = Math.max(newPosition, 0); + seekTo(newPosition); + } else if (event.getTimeLeft() < SleepTimer.NOTIFICATION_THRESHOLD) { final float[] multiplicators = {0.1f, 0.2f, 0.3f, 0.3f, 0.3f, 0.4f, 0.4f, 0.4f, 0.6f, 0.8f}; float multiplicator = multiplicators[Math.max(0, (int) event.getTimeLeft() / 1000)]; Log.d(TAG, "onSleepTimerAlmostExpired: " + multiplicator); @@ -1244,20 +1261,35 @@ public class PlaybackService extends MediaBrowserServiceCompat { WearMediaSession.addWearExtrasToAction(fastForwardBuilder); sessionState.addCustomAction(fastForwardBuilder.build()); - sessionState.addCustomAction( - new PlaybackStateCompat.CustomAction.Builder( - CUSTOM_ACTION_CHANGE_PLAYBACK_SPEED, - getString(R.string.playback_speed), - R.drawable.ic_notification_playback_speed + if (UserPreferences.showPlaybackSpeedOnFullNotification()) { + sessionState.addCustomAction( + new PlaybackStateCompat.CustomAction.Builder( + CUSTOM_ACTION_CHANGE_PLAYBACK_SPEED, + getString(R.string.playback_speed), + R.drawable.ic_notification_playback_speed ).build() - ); - sessionState.addCustomAction( + ); + } + + if (UserPreferences.showNextChapterOnFullNotification()) { + if (getPlayable() != null && getPlayable().getChapters() != null) { + sessionState.addCustomAction( + new PlaybackStateCompat.CustomAction.Builder( + CUSTOM_ACTION_NEXT_CHAPTER, + getString(R.string.next_chapter), R.drawable.ic_notification_next_chapter) + .build()); + } + } + + if (UserPreferences.showSkipOnFullNotification()) { + sessionState.addCustomAction( new PlaybackStateCompat.CustomAction.Builder( - CUSTOM_ACTION_SKIP_TO_NEXT, - getString(R.string.skip_episode_label), - R.drawable.ic_notification_skip + CUSTOM_ACTION_SKIP_TO_NEXT, + getString(R.string.skip_episode_label), + R.drawable.ic_notification_skip ).build() - ); + ); + } WearMediaSession.mediaSessionSetExtraForWear(mediaSession); @@ -1765,6 +1797,12 @@ public class PlaybackService extends MediaBrowserServiceCompat { public void onPlayFromSearch(String query, Bundle extras) { Log.d(TAG, "onPlayFromSearch query=" + query + " extras=" + extras.toString()); + if (query.equals("")) { + Log.d(TAG, "onPlayFromSearch called with empty query, resuming from the last position"); + startPlayingFromPreferences(); + return; + } + List<FeedItem> results = FeedSearcher.searchFeedItems(query, 0); if (results.size() > 0 && results.get(0).getMedia() != null) { FeedMedia media = results.get(0).getMedia(); @@ -1800,6 +1838,26 @@ public class PlaybackService extends MediaBrowserServiceCompat { seekDelta(-UserPreferences.getRewindSecs() * 1000); } + public void onNextChapter() { + List<Chapter> chapters = mediaPlayer.getPlayable().getChapters(); + if (chapters == null) { + // No chapters, just fallback to next episode + mediaPlayer.skip(); + return; + } + + int nextChapter = ChapterUtils.getCurrentChapterIndex( + mediaPlayer.getPlayable(), mediaPlayer.getPosition()) + 1; + + if (chapters.size() < nextChapter + 1) { + // We are on the last chapter, just fallback to the next episode + mediaPlayer.skip(); + return; + } + + mediaPlayer.seekTo((int) chapters.get(nextChapter).getStart()); + } + @Override public void onFastForward() { Log.d(TAG, "onFastForward()"); @@ -1872,6 +1930,8 @@ public class PlaybackService extends MediaBrowserServiceCompat { onRewind(); } else if (CUSTOM_ACTION_SKIP_TO_NEXT.equals(action)) { mediaPlayer.skip(); + } else if (CUSTOM_ACTION_NEXT_CHAPTER.equals(action)) { + onNextChapter(); } else if (CUSTOM_ACTION_CHANGE_PLAYBACK_SPEED.equals(action)) { List<Float> selectedSpeeds = UserPreferences.getPlaybackSpeedArray(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java index fe98dbc8f..471dc7454 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java @@ -175,14 +175,12 @@ public class PlaybackServiceNotificationBuilder { ArrayList<Integer> compactActionList = new ArrayList<>(); int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction - // always let them rewind + PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_REWIND, numActions); notification.addAction(R.drawable.ic_notification_fast_rewind, context.getString(R.string.rewind_label), rewindButtonPendingIntent); - if (UserPreferences.showRewindOnCompactNotification()) { - compactActionList.add(numActions); - } + compactActionList.add(numActions); numActions++; if (playerStatus == PlayerStatus.PLAYING) { @@ -205,19 +203,24 @@ public class PlaybackServiceNotificationBuilder { KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions); notification.addAction(R.drawable.ic_notification_fast_forward, context.getString(R.string.fast_forward_label), ffButtonPendingIntent); - if (UserPreferences.showFastForwardOnCompactNotification()) { - compactActionList.add(numActions); - } + compactActionList.add(numActions); numActions++; - PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction( - KeyEvent.KEYCODE_MEDIA_NEXT, numActions); - notification.addAction(R.drawable.ic_notification_skip, context.getString(R.string.skip_episode_label), - skipButtonPendingIntent); - if (UserPreferences.showSkipOnCompactNotification()) { - compactActionList.add(numActions); + if (UserPreferences.showNextChapterOnFullNotification() && playable.getChapters() != null) { + PendingIntent nextChapterPendingIntent = getPendingIntentForCustomMediaAction( + PlaybackService.CUSTOM_ACTION_NEXT_CHAPTER, numActions); + notification.addAction(R.drawable.ic_notification_next_chapter, context.getString(R.string.next_chapter), + nextChapterPendingIntent); + numActions++; + } + + if (UserPreferences.showSkipOnFullNotification()) { + PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction( + KeyEvent.KEYCODE_MEDIA_NEXT, numActions); + notification.addAction(R.drawable.ic_notification_skip, context.getString(R.string.skip_episode_label), + skipButtonPendingIntent); + numActions++; } - numActions++; PendingIntent stopButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_STOP, numActions); @@ -242,6 +245,20 @@ public class PlaybackServiceNotificationBuilder { } } + private PendingIntent getPendingIntentForCustomMediaAction(String action, int requestCode) { + Intent intent = new Intent(context, PlaybackService.class); + intent.setAction("MediaAction" + action); + intent.putExtra(MediaButtonReceiver.EXTRA_CUSTOM_ACTION, action); + + if (Build.VERSION.SDK_INT >= 26) { + return PendingIntent.getForegroundService(context, requestCode, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + } else { + return PendingIntent.getService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT + | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0)); + } + } + public void setMediaSessionToken(MediaSessionCompat.Token mediaSessionToken) { this.mediaSessionToken = mediaSessionToken; } @@ -253,4 +270,4 @@ public class PlaybackServiceNotificationBuilder { public PlayerStatus getPlayerStatus() { return playerStatus; } -}
\ No newline at end of file +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java index dbbfba379..ecfe5f4dd 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java @@ -69,7 +69,9 @@ public class AutomaticDownloadAlgorithm { Iterator<FeedItem> it = candidates.iterator(); while (it.hasNext()) { FeedItem item = it.next(); - if (!item.isAutoDownloadable(System.currentTimeMillis()) + if (!item.isAutoDownloadEnabled() + || item.isDownloaded() + || !item.hasMedia() || PlaybackStatus.isPlaying(item.getMedia()) || item.getFeed().isLocalFeed()) { it.remove(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java index 084b3a7ad..492dff759 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java @@ -15,9 +15,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import de.danoeh.antennapod.core.util.FeedItemPermutors; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.comparator.DownloadResultComparator; -import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator; import de.danoeh.antennapod.core.util.comparator.PlaybackCompletionDateComparator; import de.danoeh.antennapod.model.feed.Chapter; import de.danoeh.antennapod.model.feed.Feed; @@ -170,13 +170,18 @@ public final class DBReader { } public static List<FeedItem> getFeedItemList(final Feed feed, final FeedItemFilter filter) { + return getFeedItemList(feed, filter, SortOrder.DATE_NEW_OLD); + } + + public static List<FeedItem> getFeedItemList(final Feed feed, final FeedItemFilter filter, SortOrder sortOrder) { Log.d(TAG, "getFeedItemList() called with: " + "feed = [" + feed + "]"); PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); try (Cursor cursor = adapter.getItemsOfFeedCursor(feed, filter)) { List<FeedItem> items = extractItemlistFromCursor(adapter, cursor); - Collections.sort(items, new FeedItemPubdateComparator()); + FeedItemPermutors.getPermutor(sortOrder).reorder(items); + feed.setItems(items); for (FeedItem item : items) { item.setFeed(feed); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesser.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesser.java index 35d77ae4a..2ff9d8848 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesser.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesser.java @@ -28,7 +28,7 @@ public class FeedItemDuplicateGuesser { return titlesLookSimilar(item1, item2) && datesLookSimilar(item1, item2) && durationsLookSimilar(media1, media2) - && TextUtils.equals(media1.getMime_type(), media2.getMime_type()); + && mimeTypeLooksSimilar(media1, media2); } private static boolean sameAndNotEmpty(String string1, String string2) { @@ -52,6 +52,19 @@ public class FeedItemDuplicateGuesser { return Math.abs(media1.getDuration() - media2.getDuration()) < 10 * 60L * 1000L; } + private static boolean mimeTypeLooksSimilar(FeedMedia media1, FeedMedia media2) { + String mimeType1 = media1.getMime_type(); + String mimeType2 = media2.getMime_type(); + if (mimeType1 == null || mimeType2 == null) { + return true; + } + if (mimeType1.contains("/") && mimeType2.contains("/")) { + mimeType1 = mimeType1.substring(0, mimeType1.indexOf("/")); + mimeType2 = mimeType2.substring(0, mimeType2.indexOf("/")); + } + return TextUtils.equals(mimeType1, mimeType2); + } + private static boolean titlesLookSimilar(FeedItem item1, FeedItem item2) { return sameAndNotEmpty(canonicalizeTitle(item1.getTitle()), canonicalizeTitle(item2.getTitle())); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java index 13f2af762..630507487 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java @@ -10,6 +10,7 @@ import androidx.core.app.ShareCompat; import androidx.core.content.FileProvider; import java.io.File; +import java.net.URLEncoder; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.model.feed.Feed; @@ -33,12 +34,12 @@ public class ShareUtils { } public static void shareFeedLink(Context context, Feed feed) { - String text = feed.getTitle(); - if (feed.getLink() != null) { - text += "\n" + feed.getLink(); - } - text += "\n\n" + context.getResources().getString(R.string.share_rss_address_label) - + " " + feed.getDownload_url(); + String text = feed.getTitle() + + "\n\n" + + "https://antennapod.org/deeplink/subscribe/?url=" + + URLEncoder.encode(feed.getDownload_url()) + + "&title=" + + URLEncoder.encode(feed.getTitle()); shareLink(context, text); } diff --git a/core/src/main/res/drawable/ic_disc_alert.xml b/core/src/main/res/drawable/ic_disc_alert.xml new file mode 100644 index 000000000..6a2c11187 --- /dev/null +++ b/core/src/main/res/drawable/ic_disc_alert.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:width="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="?attr/action_icon_color" + android:pathData="M10 14C8.9 14 8 13.1 8 12C8 10.9 8.9 10 10 10C11.1 10 12 10.9 12 12S11.1 14 10 14M10 4C5.6 4 2 7.6 2 12S5.6 20 10 20 18 16.4 18 12 14.4 4 10 4M20 13H22V7H20M20 17H22V15H20V17Z" /> + +</vector> diff --git a/core/src/main/res/drawable/ic_disc_full.xml b/core/src/main/res/drawable/ic_disc_full.xml deleted file mode 100644 index 2aba1bc53..000000000 --- a/core/src/main/res/drawable/ic_disc_full.xml +++ /dev/null @@ -1,9 +0,0 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="20dp" - android:height="16dp" - android:viewportWidth="20" - android:viewportHeight="16"> - <path - android:pathData="M17.1 11.5s0-1.7 0-1.7s1.8 0 1.8 0s0 1.7 0 1.7s-1.8 0-1.8 0zm0-8s1.8 0 1.8 0s0 4.5 0 4.5s-1.8 0-1.8 0s0-4.5 0-4.5zm-8.9-2.6c2 0 3.6 0.7 5 2.1s2.1 3 2.1 5s-0.7 3.7-2.1 5s-3 2.1-5 2.1c-1.9 0-3.6-0.7-5-2.1s-2.1-3-2.1-5s0.7-3.6 2.1-5s3.1-2.1 5-2.1zm0 8.9c0.5 0 0.9-0.2 1.3-0.5s0.5-0.8 0.5-1.3s-0.2-0.9-0.5-1.3s-0.8-0.5-1.3-0.5s-0.9 0.2-1.2 0.5s-0.6 0.8-0.6 1.3s0.2 0.9 0.6 1.3s0.7 0.5 1.2 0.5z" - android:fillColor="?android:attr/textColorPrimary" /> -</vector>
\ No newline at end of file diff --git a/core/src/main/res/drawable/ic_paperclip.xml b/core/src/main/res/drawable/ic_paperclip.xml new file mode 100644 index 000000000..dd6673c13 --- /dev/null +++ b/core/src/main/res/drawable/ic_paperclip.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:height="12dp" + android:width="12dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <group> + <path + android:fillColor="?attr/action_icon_color" + android:pathData="M16.5,6V17.5A4,4 0 0,1 12.5,21.5A4,4 0 0,1 8.5,17.5V5A2.5,2.5 0 0,1 11,2.5A2.5,2.5 0 0,1 13.5,5V15.5A1,1 0 0,1 12.5,16.5A1,1 0 0,1 11.5,15.5V6H10V15.5A2.5,2.5 0 0,0 12.5,18A2.5,2.5 0 0,0 15,15.5V5A4,4 0 0,0 11,1A4,4 0 0,0 7,5V17.5A5.5,5.5 0 0,0 12.5,23A5.5,5.5 0 0,0 18,17.5V6H16.5Z" /> + + </group> + +</vector> diff --git a/core/src/main/res/drawable/ic_sliders.xml b/core/src/main/res/drawable/ic_sliders.xml deleted file mode 100644 index c6f3de7b4..000000000 --- a/core/src/main/res/drawable/ic_sliders.xml +++ /dev/null @@ -1,5 +0,0 @@ -<vector android:height="24dp" - android:viewportHeight="24.0" android:viewportWidth="24.0" - android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="?attr/action_icon_color" android:pathData="M3,17V19H9V17H3M3,5V7H13V5H3M13,21V19H21V17H13V15H11V21H13M7,9V11H3V13H7V15H9V9H7M21,13V11H11V13H21M15,9H17V7H21V5H17V3H15V9Z"/> -</vector> diff --git a/core/src/main/res/layout/refresh_action_view.xml b/core/src/main/res/layout/refresh_action_view.xml deleted file mode 100644 index d5b88922e..000000000 --- a/core/src/main/res/layout/refresh_action_view.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<ProgressBar - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_gravity="end" - android:padding="8dp" - android:indeterminateOnly="true"/> diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml index 50afe630b..7eeab886a 100644 --- a/core/src/main/res/values/arrays.xml +++ b/core/src/main/res/values/arrays.xml @@ -246,52 +246,10 @@ <item>DownloadsSection</item> </string-array> - <!-- sort for podcast screen, not for queue --> - <string-array name="feed_episodes_sort_options"> - <item>@string/sort_date_new_old</item> - <item>@string/sort_date_old_new</item> - <item>@string/sort_title_a_z</item> - <item>@string/sort_title_z_a</item> - <item>@string/sort_duration_short_long</item> - <item>@string/sort_duration_long_short</item> - </string-array> - - <!-- sort for local feed screen --> - <string-array name="local_feed_episodes_sort_options"> - <item>@string/sort_date_new_old</item> - <item>@string/sort_date_old_new</item> - <item>@string/sort_title_a_z</item> - <item>@string/sort_title_z_a</item> - <item>@string/sort_duration_short_long</item> - <item>@string/sort_duration_long_short</item> - <item>@string/sort_filename_a_z</item> - <item>@string/sort_filename_z_a</item> - </string-array> - - <string-array name="feed_episodes_sort_values"> - <item>DATE_NEW_OLD</item> - <item>DATE_OLD_NEW</item> - <item>EPISODE_TITLE_A_Z</item> - <item>EPISODE_TITLE_Z_A</item> - <item>DURATION_SHORT_LONG</item> - <item>DURATION_LONG_SHORT</item> - </string-array> - - <string-array name="local_feed_episodes_sort_values"> - <item>DATE_NEW_OLD</item> - <item>DATE_OLD_NEW</item> - <item>EPISODE_TITLE_A_Z</item> - <item>EPISODE_TITLE_Z_A</item> - <item>DURATION_SHORT_LONG</item> - <item>DURATION_LONG_SHORT</item> - <item>EPISODE_FILENAME_A_Z</item> - <item>EPISODE_FILENAME_Z_A</item> - </string-array> - - <string-array name="compact_notification_buttons_options"> - <item>@string/rewind_label</item> - <item>@string/fast_forward_label</item> + <string-array name="full_notification_buttons_options"> <item>@string/skip_episode_label</item> + <item>@string/next_chapter</item> + <item>@string/playback_speed</item> </string-array> <string-array name="default_page_values"> diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java index a08d0897d..62775b84b 100644 --- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java +++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java @@ -1,7 +1,6 @@ package de.danoeh.antennapod.core.feed; import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.model.feed.FeedMedia; import org.junit.Before; import org.junit.Test; @@ -11,13 +10,11 @@ import java.util.Date; import static de.danoeh.antennapod.core.feed.FeedItemMother.anyFeedItemWithImage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; public class FeedItemTest { private static final String TEXT_LONG = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; private static final String TEXT_SHORT = "Lorem ipsum"; - private static final long ONE_HOUR = 1000L * 3600L; private FeedItem original; private FeedItem changedFeedItem; @@ -139,36 +136,4 @@ public class FeedItemTest { item.setDescriptionIfLonger(contentEncoded); assertEquals(TEXT_LONG, item.getDescription()); } - - @Test - public void testAutoDownloadBackoff() { - FeedItem item = new FeedItem(); - item.setMedia(new FeedMedia(item, "https://example.com/file.mp3", 0, "audio/mpeg")); - - long now = ONE_HOUR; // In reality, this is System.currentTimeMillis() - assertTrue(item.isAutoDownloadable(now)); - item.increaseFailedAutoDownloadAttempts(now); - assertFalse(item.isAutoDownloadable(now)); - - now += ONE_HOUR; - assertTrue(item.isAutoDownloadable(now)); - item.increaseFailedAutoDownloadAttempts(now); - assertFalse(item.isAutoDownloadable(now)); - - now += ONE_HOUR; - assertFalse(item.isAutoDownloadable(now)); // Should backoff, so more than 1 hour needed - - now += ONE_HOUR; - assertTrue(item.isAutoDownloadable(now)); // Now it's enough - item.increaseFailedAutoDownloadAttempts(now); - item.increaseFailedAutoDownloadAttempts(now); - item.increaseFailedAutoDownloadAttempts(now); - - now += 1000L * ONE_HOUR; - assertFalse(item.isAutoDownloadable(now)); // Should have given up - item.increaseFailedAutoDownloadAttempts(now); - - now += 1000L * ONE_HOUR; - assertFalse(item.isAutoDownloadable(now)); // Still given up - } } diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesserTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesserTest.java index ac7cdee1f..f3c993066 100644 --- a/core/src/test/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesserTest.java +++ b/core/src/test/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesserTest.java @@ -44,6 +44,9 @@ public class FeedItemDuplicateGuesserTest { assertFalse(FeedItemDuplicateGuesser.seemDuplicates( item("id1", "Title", "example.com/episode1", 10, 5 * MINUTES, "audio/*"), item("id2", "Title", "example.com/episode2", 10, 5 * MINUTES, "video/*"))); + assertTrue(FeedItemDuplicateGuesser.seemDuplicates( + item("id1", "Title", "example.com/episode1", 10, 5 * MINUTES, "audio/mpeg"), + item("id2", "Title", "example.com/episode2", 10, 5 * MINUTES, "audio/mp3"))); assertFalse(FeedItemDuplicateGuesser.seemDuplicates( item("id1", "Title", "example.com/episode1", 5 * DAYS, 5 * MINUTES, "audio/*"), item("id2", "Title", "example.com/episode2", 2 * DAYS, 5 * MINUTES, "audio/*"))); |