diff options
Diffstat (limited to 'core/src/main')
23 files changed, 268 insertions, 121 deletions
diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml index e186a856f..71cf676a0 100644 --- a/core/src/main/AndroidManifest.xml +++ b/core/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" package="de.danoeh.antennapod.core"> + xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java index eae7a8426..d63fd50f2 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java @@ -38,7 +38,6 @@ import de.danoeh.antennapod.model.download.DownloadError; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedMedia; -import de.danoeh.antennapod.model.feed.FeedPreferences; import de.danoeh.antennapod.model.playback.MediaType; import de.danoeh.antennapod.parser.feed.util.MimeTypeUtils; import de.danoeh.antennapod.parser.media.id3.ID3ReaderException; @@ -125,13 +124,9 @@ public class LocalFeedUpdater { feed.setImageUrl(getImageUrl(allFiles, folderUri)); feed.getPreferences().setAutoDownload(false); - feed.getPreferences().setAutoDeleteAction(FeedPreferences.AutoDeleteAction.NEVER); feed.setDescription(context.getString(R.string.local_feed_description)); feed.setAuthor(context.getString(R.string.local_folder)); - if (newItems.isEmpty()) { - throw new IOException("Empty folder. Make sure that the folder is accessible and contains media files."); - } DBTasks.updateFeed(context, feed, true); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java index 659650bc1..eea4d2082 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java @@ -199,7 +199,8 @@ public class FeedUpdateWorker extends Worker { newEpisodesNotification.showIfNeeded(getApplicationContext(), feedSyncTask.getSavedFeed()); if (downloader.permanentRedirectUrl != null) { DBWriter.updateFeedDownloadURL(request.getSource(), downloader.permanentRedirectUrl); - } else if (feedSyncTask.getRedirectUrl() != null) { + } else if (feedSyncTask.getRedirectUrl() != null + && !feedSyncTask.getRedirectUrl().equals(request.getSource())) { DBWriter.updateFeedDownloadURL(request.getSource(), feedSyncTask.getRedirectUrl()); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java index 976b3504a..e80f9b47f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java @@ -158,11 +158,7 @@ public class EpisodeDownloadWorker extends Worker { downloader.call(); } catch (Exception e) { DBWriter.addDownloadStatus(downloader.getResult()); - if (EventBus.getDefault().hasSubscriberForEvent(MessageEvent.class)) { - sendMessage(request.getTitle(), true); - } else { - sendErrorNotification(); - } + sendErrorNotification(request.getTitle()); return Result.failure(); } @@ -195,11 +191,7 @@ public class EpisodeDownloadWorker extends Worker { || status.getReason() == DownloadError.ERROR_UNAUTHORIZED || status.getReason() == DownloadError.ERROR_IO_BLOCKED) { // Fail fast, these are probably unrecoverable - if (EventBus.getDefault().hasSubscriberForEvent(MessageEvent.class)) { - sendMessage(request.getTitle(), true); - } else { - sendErrorNotification(); - } + sendErrorNotification(request.getTitle()); return Result.failure(); } sendMessage(request.getTitle(), false); @@ -208,7 +200,7 @@ public class EpisodeDownloadWorker extends Worker { private Result retry3times() { if (isLastRunAttempt()) { - sendErrorNotification(); + sendErrorNotification(downloader.getDownloadRequest().getTitle()); return Result.failure(); } else { return Result.retry(); @@ -243,7 +235,12 @@ public class EpisodeDownloadWorker extends Worker { PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0)); } - private void sendErrorNotification() { + private void sendErrorNotification(String title) { + if (EventBus.getDefault().hasSubscriberForEvent(MessageEvent.class)) { + sendMessage(title, false); + return; + } + NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), NotificationUtils.CHANNEL_ID_DOWNLOAD_ERROR); builder.setTicker(getApplicationContext().getString(R.string.download_report_title)) diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java index c0cf9e077..01038435d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java @@ -94,7 +94,7 @@ public class NewEpisodesNotification { intent.setAction("NewEpisodes"); intent.setComponent(new ComponentName(context, "de.danoeh.antennapod.activity.MainActivity")); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - intent.putExtra("fragment_tag", "EpisodesFragment"); + intent.putExtra("fragment_tag", "NewEpisodesFragment"); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0)); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java index e0c5da00b..c275444d9 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java @@ -1,6 +1,7 @@ package de.danoeh.antennapod.core.service.playback; import android.content.Context; +import android.media.audiofx.LoudnessEnhancer; import android.net.Uri; import android.text.TextUtils; import android.util.Log; @@ -68,6 +69,8 @@ public class ExoPlayerWrapper { private PlaybackParameters playbackParameters; private DefaultTrackSelector trackSelector; + private LoudnessEnhancer loudnessEnhancer; + ExoPlayerWrapper(Context context) { this.context = context; createPlayer(); @@ -133,7 +136,14 @@ public class ExoPlayerWrapper { audioSeekCompleteListener.run(); } } + + @Override + public void onAudioSessionIdChanged(int audioSessionId) { + initLoudnessEnhancer(audioSessionId); + } }); + + initLoudnessEnhancer(exoPlayer.getAudioSessionId()); } public int getCurrentPosition() { @@ -235,7 +245,14 @@ public class ExoPlayerWrapper { } public void setVolume(float v, float v1) { - exoPlayer.setVolume(v); + if (v > 1) { + exoPlayer.setVolume(1f); + loudnessEnhancer.setEnabled(true); + loudnessEnhancer.setTargetGain((int) (1000 * (v - 1))); + } else { + exoPlayer.setVolume(v); + loudnessEnhancer.setEnabled(false); + } } public void start() { @@ -335,4 +352,16 @@ public class ExoPlayerWrapper { void setOnBufferingUpdateListener(Consumer<Integer> bufferingUpdateListener) { this.bufferingUpdateListener = bufferingUpdateListener; } + + private void initLoudnessEnhancer(int audioStreamId) { + LoudnessEnhancer newEnhancer = new LoudnessEnhancer(audioStreamId); + LoudnessEnhancer oldEnhancer = this.loudnessEnhancer; + if (oldEnhancer != null) { + newEnhancer.setEnabled(oldEnhancer.getEnabled()); + newEnhancer.setTargetGain((int) oldEnhancer.getTargetGain()); + oldEnhancer.release(); + } + + this.loudnessEnhancer = newEnhancer; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java index 9dc846cc2..e7d7d10c9 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java @@ -343,6 +343,12 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { t = 0; } + if (t >= getDuration()) { + Log.d(TAG, "Seek reached end of file, skipping to next episode"); + skip(); + return; + } + if (playerStatus == PlayerStatus.PLAYING || playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) { 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 6fc9035ca..c41ec93b1 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 @@ -20,7 +20,9 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Vibrator; import android.service.quicksettings.TileService; import android.support.v4.media.MediaBrowserCompat; @@ -33,6 +35,7 @@ import android.util.Log; import android.util.Pair; import android.view.KeyEvent; import android.view.SurfaceHolder; +import android.view.ViewConfiguration; import android.webkit.URLUtil; import android.widget.Toast; @@ -65,6 +68,7 @@ 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.FeedItemUtil; +import de.danoeh.antennapod.core.util.FeedUtil; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.gui.NotificationUtils; @@ -150,6 +154,8 @@ public class PlaybackService extends MediaBrowserServiceCompat { private CastStateListener castStateListener; private String autoSkippedFeedMediaId = null; + private int clickCount = 0; + private final Handler clickHandler = new Handler(Looper.getMainLooper()); /** * Used for Lollipop notifications, Android Wear, and Android Auto. @@ -556,6 +562,12 @@ public class PlaybackService extends MediaBrowserServiceCompat { @SuppressLint("LaunchActivityFromNotification") private void displayStreamingNotAllowedNotification(Intent originalIntent) { + if (EventBus.getDefault().hasSubscriberForEvent(MessageEvent.class)) { + EventBus.getDefault().post(new MessageEvent( + getString(R.string.confirm_mobile_streaming_notification_message))); + return; + } + Intent intentAllowThisTime = new Intent(originalIntent); intentAllowThisTime.setAction(PlaybackServiceInterface.EXTRA_ALLOW_STREAM_THIS_TIME); intentAllowThisTime.putExtra(PlaybackServiceInterface.EXTRA_ALLOW_STREAM_THIS_TIME, true); @@ -1098,7 +1110,8 @@ public class PlaybackService extends MediaBrowserServiceCompat { FeedPreferences.AutoDeleteAction action = item.getFeed().getPreferences().getCurrentAutoDelete(); boolean shouldAutoDelete = action == FeedPreferences.AutoDeleteAction.ALWAYS - || (action == FeedPreferences.AutoDeleteAction.GLOBAL && UserPreferences.isAutoDelete()); + || (action == FeedPreferences.AutoDeleteAction.GLOBAL + && FeedUtil.shouldAutoDeleteItemsOnThatFeed(item.getFeed())); if (shouldAutoDelete && (!item.isTagged(FeedItem.TAG_FAVORITE) || !UserPreferences.shouldFavoriteKeepEpisode())) { DBWriter.deleteFeedMediaOfItem(PlaybackService.this, media.getId()); @@ -1761,7 +1774,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { public void onPause() { Log.d(TAG, "onPause()"); if (getStatus() == PlayerStatus.PLAYING) { - pause(!UserPreferences.isPersistNotify(), true); + pause(!UserPreferences.isPersistNotify(), false); } } @@ -1823,7 +1836,24 @@ public class PlaybackService extends MediaBrowserServiceCompat { if (keyEvent != null && keyEvent.getAction() == KeyEvent.ACTION_DOWN && keyEvent.getRepeatCount() == 0) { - return handleKeycode(keyEvent.getKeyCode(), false); + int keyCode = keyEvent.getKeyCode(); + if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { + clickCount++; + clickHandler.removeCallbacksAndMessages(null); + clickHandler.postDelayed(() -> { + if (clickCount == 1) { + handleKeycode(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, false); + } else if (clickCount == 2) { + onFastForward(); + } else if (clickCount == 3) { + onRewind(); + } + clickCount = 0; + }, ViewConfiguration.getDoubleTapTimeout()); + return true; + } else { + return handleKeycode(keyCode, false); + } } } return false; diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java index d42f14591..d6a4fa1cb 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java @@ -281,7 +281,7 @@ public final class DBTasks { if (oldItem != null) { oldItem.updateFromOther(item); } else { - // item is new + Log.d(TAG, "Found new item: " + item.getTitle()); item.setFeed(savedFeed); if (idx >= savedFeed.getItems().size()) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java index b2cc46ee0..1453f701d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java @@ -2,15 +2,22 @@ package de.danoeh.antennapod.core.storage; import android.app.backup.BackupManager; import android.content.Context; +import android.net.Uri; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.NotificationManagerCompat; +import androidx.documentfile.provider.DocumentFile; +import com.google.common.util.concurrent.Futures; + +import de.danoeh.antennapod.core.event.DownloadLogEvent; +import de.danoeh.antennapod.core.feed.LocalFeedUpdater; import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface; import de.danoeh.antennapod.core.service.playback.PlaybackServiceInterface; import de.danoeh.antennapod.storage.database.PodDBAdapter; + import org.greenrobot.eventbus.EventBus; import java.io.File; @@ -25,7 +32,6 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.core.event.DownloadLogEvent; import de.danoeh.antennapod.event.FavoritesEvent; import de.danoeh.antennapod.event.FeedItemEvent; import de.danoeh.antennapod.event.FeedListUpdateEvent; @@ -94,7 +100,7 @@ public class DBWriter { */ public static Future<?> deleteFeedMediaOfItem(@NonNull final Context context, final long mediaId) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { final FeedMedia media = DBReader.getFeedMedia(mediaId); if (media != null) { boolean result = deleteFeedMediaSynchronous(context, media); @@ -106,10 +112,10 @@ public class DBWriter { }); } - private static boolean deleteFeedMediaSynchronous( - @NonNull Context context, @NonNull FeedMedia media) { + private static boolean deleteFeedMediaSynchronous(@NonNull Context context, @NonNull FeedMedia media) { Log.i(TAG, String.format(Locale.US, "Requested to delete FeedMedia [id=%d, title=%s, downloaded=%s", media.getId(), media.getEpisodeTitle(), media.isDownloaded())); + boolean localDelete = false; if (media.isDownloaded() || media.getFile_url() != null) { // delete downloaded media file File mediaFile = new File(media.getFile_url()); @@ -125,23 +131,38 @@ public class DBWriter { adapter.open(); adapter.setMedia(media); adapter.close(); + } else if (media.getFile_url().startsWith("content://")) { + // Local feed + DocumentFile documentFile = DocumentFile.fromSingleUri( + context, Uri.parse(media.getFile_url())); + if (documentFile == null || !documentFile.exists() || !documentFile.delete()) { + EventBus.getDefault().post(new MessageEvent(context.getString(R.string.delete_local_failed))); + return false; + } + localDelete = true; + } - if (media.getId() == PlaybackPreferences.getCurrentlyPlayingFeedMediaId()) { - PlaybackPreferences.writeNoMediaPlaying(); - IntentUtils.sendLocalBroadcast(context, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE); + if (media.getId() == PlaybackPreferences.getCurrentlyPlayingFeedMediaId()) { + PlaybackPreferences.writeNoMediaPlaying(); + IntentUtils.sendLocalBroadcast(context, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE); - NotificationManagerCompat nm = NotificationManagerCompat.from(context); - nm.cancel(R.id.notification_playing); - } + NotificationManagerCompat nm = NotificationManagerCompat.from(context); + nm.cancel(R.id.notification_playing); + } + if (localDelete) { + // Do full update of this feed to get rid of the item + LocalFeedUpdater.updateFeed(media.getItem().getFeed(), context.getApplicationContext(), null); + } else { // Gpodder: queue delete action for synchronization FeedItem item = media.getItem(); EpisodeAction action = new EpisodeAction.Builder(item, EpisodeAction.DELETE) .currentTimestamp() .build(); SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, action); + + EventBus.getDefault().post(FeedItemEvent.updated(media.getItem())); } - EventBus.getDefault().post(FeedItemEvent.updated(media.getItem())); return true; } @@ -152,7 +173,7 @@ public class DBWriter { * @param feedId ID of the Feed that should be deleted. */ public static Future<?> deleteFeed(final Context context, final long feedId) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { final Feed feed = DBReader.getFeed(feedId); if (feed == null) { return; @@ -183,7 +204,7 @@ public class DBWriter { */ @NonNull public static Future<?> deleteFeedItems(@NonNull Context context, @NonNull List<FeedItem> items) { - return dbExec.submit(() -> deleteFeedItemsSynchronous(context, items)); + return runOnDbThread(() -> deleteFeedItemsSynchronous(context, items)); } /** @@ -235,7 +256,7 @@ public class DBWriter { * Deletes the entire playback history. */ public static Future<?> clearPlaybackHistory() { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.clearPlaybackHistory(); @@ -248,7 +269,7 @@ public class DBWriter { * Deletes the entire download log. */ public static Future<?> clearDownloadLog() { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.clearDownloadLog(); @@ -281,7 +302,7 @@ public class DBWriter { * @param date PlaybackCompletionDate for <code>media</code> */ public static Future<?> addItemToPlaybackHistory(final FeedMedia media, Date date) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { Log.d(TAG, "Adding item to playback history"); media.setPlaybackCompletionDate(date); @@ -300,7 +321,7 @@ public class DBWriter { * @param status The DownloadStatus object. */ public static Future<?> addDownloadStatus(final DownloadResult status) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setDownloadStatus(status); @@ -322,7 +343,7 @@ public class DBWriter { */ public static Future<?> addQueueItemAt(final Context context, final long itemId, final int index, final boolean performAutoDownload) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); final List<FeedItem> queue = DBReader.getQueue(adapter); @@ -393,7 +414,7 @@ public class DBWriter { */ public static Future<?> addQueueItem(final Context context, final boolean performAutoDownload, final boolean markAsUnplayed, final long... itemIds) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { if (itemIds.length < 1) { return; } @@ -476,7 +497,7 @@ public class DBWriter { * Removes all FeedItem objects from the queue. */ public static Future<?> clearQueue() { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.clearQueue(); @@ -495,12 +516,12 @@ public class DBWriter { */ public static Future<?> removeQueueItem(final Context context, final boolean performAutoDownload, final FeedItem item) { - return dbExec.submit(() -> removeQueueItemSynchronous(context, performAutoDownload, item.getId())); + return runOnDbThread(() -> removeQueueItemSynchronous(context, performAutoDownload, item.getId())); } public static Future<?> removeQueueItem(final Context context, final boolean performAutoDownload, final long... itemIds) { - return dbExec.submit(() -> removeQueueItemSynchronous(context, performAutoDownload, itemIds)); + return runOnDbThread(() -> removeQueueItemSynchronous(context, performAutoDownload, itemIds)); } private static void removeQueueItemSynchronous(final Context context, @@ -562,7 +583,7 @@ public class DBWriter { } public static Future<?> addFavoriteItem(final FeedItem item) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance().open(); adapter.addFavoriteItem(item); adapter.close(); @@ -573,7 +594,7 @@ public class DBWriter { } public static Future<?> removeFavoriteItem(final FeedItem item) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance().open(); adapter.removeFavoriteItem(item); adapter.close(); @@ -590,7 +611,7 @@ public class DBWriter { * @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to */ public static Future<?> moveQueueItemToTop(final long itemId, final boolean broadcastUpdate) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { LongList queueIdList = DBReader.getQueueIDList(); int index = queueIdList.indexOf(itemId); if (index >= 0) { @@ -609,7 +630,7 @@ public class DBWriter { */ public static Future<?> moveQueueItemToBottom(final long itemId, final boolean broadcastUpdate) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { LongList queueIdList = DBReader.getQueueIDList(); int index = queueIdList.indexOf(itemId); if (index >= 0) { @@ -632,7 +653,7 @@ public class DBWriter { */ public static Future<?> moveQueueItem(final int from, final int to, final boolean broadcastUpdate) { - return dbExec.submit(() -> moveQueueItemHelper(from, to, broadcastUpdate)); + return runOnDbThread(() -> moveQueueItemHelper(from, to, broadcastUpdate)); } /** @@ -669,7 +690,7 @@ public class DBWriter { } public static Future<?> resetPagedFeedPage(Feed feed) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.resetPagedFeedPage(feed); @@ -699,7 +720,7 @@ public class DBWriter { */ public static Future<?> markItemPlayed(final int played, final boolean broadcastUpdate, final long... itemIds) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setFeedItemRead(played, itemIds); @@ -729,7 +750,7 @@ public class DBWriter { final int played, final long mediaId, final boolean resetMediaPosition) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setFeedItemRead(played, itemId, mediaId, @@ -746,7 +767,7 @@ public class DBWriter { * @param feedId ID of the Feed. */ public static Future<?> removeFeedNewFlag(final long feedId) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setFeedItems(FeedItem.NEW, FeedItem.UNPLAYED, feedId); @@ -760,7 +781,7 @@ public class DBWriter { * Sets the 'read'-attribute of all NEW FeedItems to UNPLAYED. */ public static Future<?> removeAllNewFlags() { - return dbExec.submit(() -> { + return runOnDbThread(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setFeedItems(FeedItem.NEW, FeedItem.UNPLAYED); @@ -771,7 +792,7 @@ public class DBWriter { } static Future<?> addNewFeed(final Context context, final Feed... feeds) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setCompleteFeed(feeds); @@ -789,7 +810,7 @@ public class DBWriter { } static Future<?> setCompleteFeed(final Feed... feeds) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setCompleteFeed(feeds); @@ -798,7 +819,7 @@ public class DBWriter { } public static Future<?> setItemList(final List<FeedItem> items) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.storeFeedItemlist(items); @@ -814,7 +835,7 @@ public class DBWriter { * @param media The FeedMedia object. */ public static Future<?> setFeedMedia(final FeedMedia media) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setMedia(media); @@ -828,7 +849,7 @@ public class DBWriter { * @param media The FeedMedia object. */ public static Future<?> setFeedMediaPlaybackInformation(final FeedMedia media) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setFeedMediaPlaybackInformation(media); @@ -843,7 +864,7 @@ public class DBWriter { * @param item The FeedItem object. */ public static Future<?> setFeedItem(final FeedItem item) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setSingleFeedItem(item); @@ -857,7 +878,7 @@ public class DBWriter { */ public static Future<?> updateFeedDownloadURL(final String original, final String updated) { Log.d(TAG, "updateFeedDownloadURL(original: " + original + ", updated: " + updated + ")"); - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setFeedDownloadUrl(original, updated); @@ -871,7 +892,7 @@ public class DBWriter { * @param preferences The FeedPreferences object. */ public static Future<?> setFeedPreferences(final FeedPreferences preferences) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setFeedPreferences(preferences); @@ -901,7 +922,7 @@ public class DBWriter { */ public static Future<?> setFeedLastUpdateFailed(final long feedId, final boolean lastUpdateFailed) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setFeedLastUpdateFailed(feedId, lastUpdateFailed); @@ -911,7 +932,7 @@ public class DBWriter { } public static Future<?> setFeedCustomTitle(Feed feed) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setFeedCustomTitle(feed.getId(), feed.getCustomTitle()); @@ -930,10 +951,10 @@ public class DBWriter { public static Future<?> reorderQueue(@Nullable SortOrder sortOrder, final boolean broadcastUpdate) { if (sortOrder == null) { Log.w(TAG, "reorderQueue() - sortOrder is null. Do nothing."); - return dbExec.submit(() -> { }); + return runOnDbThread(() -> { }); } final Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(sortOrder); - return dbExec.submit(() -> { + return runOnDbThread(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); final List<FeedItem> queue = DBReader.getQueue(adapter); @@ -960,7 +981,7 @@ public class DBWriter { public static Future<?> setFeedItemsFilter(final long feedId, final Set<String> filterValues) { Log.d(TAG, "setFeedItemsFilter() called with: " + "feedId = [" + feedId + "], filterValues = [" + filterValues + "]"); - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setFeedItemFilter(feedId, filterValues); @@ -974,7 +995,7 @@ public class DBWriter { * */ public static Future<?> setFeedItemSortOrder(long feedId, @Nullable SortOrder sortOrder) { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.setFeedItemSortOrder(feedId, sortOrder); @@ -988,11 +1009,24 @@ public class DBWriter { */ @NonNull public static Future<?> resetStatistics() { - return dbExec.submit(() -> { + return runOnDbThread(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); adapter.resetAllMediaPlayedDuration(); adapter.close(); }); } + + /** + * Submit to the DB thread only if caller is not already on the DB thread. Otherwise, + * just execute synchronously + */ + private static Future<?> runOnDbThread(Runnable runnable) { + if ("DatabaseExecutor".equals(Thread.currentThread().getName())) { + runnable.run(); + return Futures.immediateFuture(null); + } else { + return dbExec.submit(runnable); + } + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java index 7fe0c5e46..dc6b7ff80 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java @@ -22,6 +22,7 @@ import androidx.work.WorkerParameters; import de.danoeh.antennapod.core.util.download.FeedUpdateManager; import de.danoeh.antennapod.event.FeedUpdateRunningEvent; +import de.danoeh.antennapod.event.MessageEvent; import de.danoeh.antennapod.model.feed.FeedItemFilter; import de.danoeh.antennapod.model.feed.SortOrder; import org.apache.commons.lang3.StringUtils; @@ -298,13 +299,18 @@ public class SyncService extends Worker { } private void updateErrorNotification(Exception exception) { + Log.d(TAG, "Posting sync error notification"); + final String description = getApplicationContext().getString(R.string.gpodnetsync_error_descr) + + exception.getMessage(); + if (!UserPreferences.gpodnetNotificationsEnabled()) { Log.d(TAG, "Skipping sync error notification because of user setting"); return; } - Log.d(TAG, "Posting sync error notification"); - final String description = getApplicationContext().getString(R.string.gpodnetsync_error_descr) - + exception.getMessage(); + if (EventBus.getDefault().hasSubscriberForEvent(MessageEvent.class)) { + EventBus.getDefault().post(new MessageEvent(description)); + return; + } Intent intent = getApplicationContext().getPackageManager().getLaunchIntentForPackage( getApplicationContext().getPackageName()); diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/queue/SynchronizationQueueSink.java b/core/src/main/java/de/danoeh/antennapod/core/sync/queue/SynchronizationQueueSink.java index 55c415153..cfe3798a7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/sync/queue/SynchronizationQueueSink.java +++ b/core/src/main/java/de/danoeh/antennapod/core/sync/queue/SynchronizationQueueSink.java @@ -19,6 +19,12 @@ public class SynchronizationQueueSink { serviceStarterImpl.run(); } + public static void syncNowIfNotSyncedRecently() { + if (System.currentTimeMillis() - SynchronizationSettings.getLastSyncAttempt() > 1000 * 60 * 10) { + syncNow(); + } + } + public static void clearQueue(Context context) { LockingAsyncExecutor.executeLockedAsync(new SynchronizationQueueStorage(context)::clearQueue); } @@ -58,7 +64,7 @@ public class SynchronizationQueueSink { if (!SynchronizationSettings.isProviderConnected()) { return; } - if (media.getItem() == null) { + if (media.getItem() == null || media.getItem().getFeed().isLocalFeed()) { return; } if (media.getStartPosition() < 0 || (!completed && media.getStartPosition() >= media.getPosition())) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java index 12377791e..ec9c9e55e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java @@ -72,6 +72,12 @@ public class FeedItemPermutors { case SMART_SHUFFLE_NEW_OLD: permutor = (queue) -> smartShuffle(queue, false); break; + case SIZE_SMALL_LARGE: + comparator = (f1, f2) -> Long.compare(size(f1), size(f2)); + break; + case SIZE_LARGE_SMALL: + comparator = (f1, f2) -> Long.compare(size(f2), size(f1)); + break; } if (comparator != null) { @@ -97,6 +103,10 @@ public class FeedItemPermutors { return (item != null && item.getMedia() != null) ? item.getMedia().getDuration() : 0; } + private static long size(@Nullable FeedItem item) { + return (item != null && item.getMedia() != null) ? item.getMedia().getSize() : 0; + } + @NonNull private static String itemLink(@Nullable FeedItem item) { return (item != null && item.getLink() != null) diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedUtil.java new file mode 100644 index 000000000..201207816 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedUtil.java @@ -0,0 +1,13 @@ +package de.danoeh.antennapod.core.util; + +import de.danoeh.antennapod.model.feed.Feed; +import de.danoeh.antennapod.storage.preferences.UserPreferences; + +public abstract class FeedUtil { + public static boolean shouldAutoDeleteItemsOnThatFeed(Feed feed) { + if (!UserPreferences.isAutoDelete()) { + return false; + } + return !feed.isLocalFeed() || UserPreferences.isAutoDeleteLocal(); + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java index 79732ff32..d870cb1f8 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java @@ -18,7 +18,6 @@ public class NotificationUtils { public static final String CHANNEL_ID_PLAYING = "playing"; public static final String CHANNEL_ID_DOWNLOAD_ERROR = "error"; public static final String CHANNEL_ID_SYNC_ERROR = "sync_error"; - public static final String CHANNEL_ID_AUTO_DOWNLOAD = "auto_download"; public static final String CHANNEL_ID_EPISODE_NOTIFICATIONS = "episode_notifications"; public static final String GROUP_ID_ERRORS = "group_errors"; @@ -38,7 +37,6 @@ public class NotificationUtils { createChannelPlaying(context), createChannelError(context), createChannelSyncError(context), - createChannelAutoDownload(context), createChannelEpisodeNotification(context)); mNotificationManager.createNotificationChannelsCompat(channels); } @@ -98,20 +96,6 @@ public class NotificationUtils { return notificationChannel.build(); } - private static NotificationChannelCompat createChannelAutoDownload(final Context c) { - final NotificationChannelCompat.Builder notificationChannel = new NotificationChannelCompat.Builder( - CHANNEL_ID_AUTO_DOWNLOAD, NotificationManagerCompat.IMPORTANCE_NONE) - .setName(c.getString(R.string.notification_channel_auto_download)) - .setDescription(c.getString(R.string.notification_channel_episode_auto_download)) - .setGroup(GROUP_ID_NEWS); - - if (UserPreferences.getShowAutoDownloadReportRaw()) { - // Migration from app managed setting: enable notification - notificationChannel.setImportance(NotificationManagerCompat.IMPORTANCE_DEFAULT); - } - return notificationChannel.build(); - } - private static NotificationChannelCompat createChannelEpisodeNotification(final Context c) { return new NotificationChannelCompat.Builder( CHANNEL_ID_EPISODE_NOTIFICATIONS, NotificationManagerCompat.IMPORTANCE_DEFAULT) diff --git a/core/src/main/res/drawable/bg_rounded_corners.xml b/core/src/main/res/drawable/bg_rounded_corners.xml new file mode 100644 index 000000000..11b7710c4 --- /dev/null +++ b/core/src/main/res/drawable/bg_rounded_corners.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="8dp" /> +</shape> diff --git a/core/src/main/res/drawable/ic_close_white.xml b/core/src/main/res/drawable/ic_close_white.xml new file mode 100644 index 000000000..d37d3f963 --- /dev/null +++ b/core/src/main/res/drawable/ic_close_white.xml @@ -0,0 +1,11 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M17.656,6.343L12,12M12,12L6.343,17.656M12,12L17.656,17.656M12,12L6.343,6.343" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#ffffff"/> +</vector> diff --git a/core/src/main/res/drawable/ic_disc_full.xml b/core/src/main/res/drawable/ic_disc_full.xml new file mode 100644 index 000000000..2aba1bc53 --- /dev/null +++ b/core/src/main/res/drawable/ic_disc_full.xml @@ -0,0 +1,9 @@ +<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_error.xml b/core/src/main/res/drawable/ic_error.xml index 402f224b2..fc4fa2b9a 100644 --- a/core/src/main/res/drawable/ic_error.xml +++ b/core/src/main/res/drawable/ic_error.xml @@ -4,6 +4,9 @@ android:viewportWidth="24" android:viewportHeight="24"> <path - android:fillColor="#FFFFFF" + android:fillColor="?android:attr/colorBackground" + android:pathData="M12 1C5.9 1 1 5.9 1 12s4.9 11 11 11 11-4.9 11-11S18.1 1 12 1z" /> + <path + android:fillColor="?attr/icon_red" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z" /> </vector> diff --git a/core/src/main/res/layout/more_content_list_footer.xml b/core/src/main/res/layout/more_content_list_footer.xml index 2869ef7de..a3fcd5489 100644 --- a/core/src/main/res/layout/more_content_list_footer.xml +++ b/core/src/main/res/layout/more_content_list_footer.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:foreground="?attr/selectableItemBackground" - android:background="?android:attr/windowBackground" + android:background="?android:attr/colorBackground" android:gravity="center" android:padding="8dp"> @@ -31,6 +31,6 @@ android:text="@string/load_next_page_label" android:textColor="?android:attr/textColorPrimary" android:layout_marginLeft="8dp" - android:layout_marginRight="8dp"/> + android:layout_marginRight="8dp" /> -</LinearLayout>
\ No newline at end of file +</LinearLayout> diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml index 7749c6f3c..50afe630b 100644 --- a/core/src/main/res/values/arrays.xml +++ b/core/src/main/res/values/arrays.xml @@ -13,16 +13,22 @@ <item>never</item> </string-array> - <string-array name="spnVolumeReductionItems"> - <item>@string/feed_volume_reduction_off</item> - <item>@string/feed_volume_reduction_light</item> + <string-array name="spnVolumeAdaptationItems"> <item>@string/feed_volume_reduction_heavy</item> + <item>@string/feed_volume_reduction_light</item> + <item>@string/feed_volume_reduction_off</item> + <item>@string/feed_volume_boost_light</item> + <item>@string/feed_volume_boost_medium</item> + <item>@string/feed_volume_boost_heavy</item> </string-array> - <string-array name="spnVolumeReductionValues"> - <item>off</item> - <item>light</item> + <string-array name="spnVolumeAdaptationValues"> <item>heavy</item> + <item>light</item> + <item>off</item> + <item>light_boost</item> + <item>medium_boost</item> + <item>heavy_boost</item> </string-array> <string-array name="feed_refresh_interval_entries"> diff --git a/core/src/main/res/values/dimens.xml b/core/src/main/res/values/dimens.xml index cddff1c47..68d0e59ab 100644 --- a/core/src/main/res/values/dimens.xml +++ b/core/src/main/res/values/dimens.xml @@ -6,11 +6,10 @@ <dimen name="text_size_micro">12sp</dimen> <dimen name="text_size_small">14sp</dimen> <dimen name="text_size_navdrawer">16sp</dimen> - <dimen name="text_size_medium">18sp</dimen> <dimen name="text_size_large">22sp</dimen> <dimen name="thumbnail_length_itemlist">56dp</dimen> <dimen name="thumbnail_length_queue_item">56dp</dimen> - <dimen name="thumbnail_length_onlinefeedview">100dp</dimen> + <dimen name="thumbnail_length_onlinefeedview">92dp</dimen> <dimen name="feeditemlist_header_height">132dp</dimen> <dimen name="thumbnail_length_navlist">40dp</dimen> <dimen name="listitem_iconwithtext_height">48dp</dimen> diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml index f1dcd3200..da227d163 100644 --- a/core/src/main/res/values/styles.xml +++ b/core/src/main/res/values/styles.xml @@ -31,6 +31,7 @@ </style> <style name="Theme.AntennaPod.Light" parent="Theme.AntennaPod.Dynamic.Light"> + <item name="isMaterial3DynamicColorApplied">false</item> <item name="colorPrimary">@color/accent_light</item> <item name="colorOnPrimary">@color/white</item> <item name="colorAccent">@color/accent_light</item> @@ -39,9 +40,9 @@ <item name="colorPrimaryDark">@color/accent_light</item> <item name="colorPrimaryContainer">@color/accent_light</item> <item name="colorOnPrimaryContainer">@color/white</item> - <item name="android:windowBackground">@color/background_light</item> + <item name="android:colorBackground">@color/background_light</item> <item name="colorSurface">@color/background_light</item> - <item name="colorSurfaceVariant">#B8E4FF</item> + <item name="colorSurfaceVariant">#D3DCE0</item> </style> <style name="Theme.AntennaPod.Dynamic.Dark" parent="Theme.Base.AntennaPod.Dynamic.Dark"> @@ -73,6 +74,7 @@ </style> <style name="Theme.AntennaPod.Dark" parent="Theme.AntennaPod.Dynamic.Dark"> + <item name="isMaterial3DynamicColorApplied">false</item> <item name="colorPrimary">@color/accent_dark</item> <item name="colorOnPrimary">@color/black</item> <item name="colorAccent">@color/accent_dark</item> @@ -81,15 +83,15 @@ <item name="colorPrimaryDark">@color/accent_dark</item> <item name="colorPrimaryContainer">@color/accent_dark</item> <item name="colorOnPrimaryContainer">@color/black</item> - <item name="android:windowBackground">@color/background_darktheme</item> + <item name="android:colorBackground">@color/background_darktheme</item> <item name="colorSurface">@color/background_darktheme</item> - <item name="colorSurfaceVariant">#1B3B6A</item> + <item name="colorSurfaceVariant">#2F3B4F</item> </style> <style name="Theme.AntennaPod.Dynamic.TrueBlack" parent="Theme.AntennaPod.Dynamic.Dark"> <item name="android:textColorPrimary">@color/white</item> <item name="android:color">@color/white</item> - <item name="android:windowBackground">@color/black</item> + <item name="android:colorBackground">@color/black</item> <item name="colorSurface">@color/black</item> <item name="background_color">@color/black</item> <item name="background_elevated">@color/black</item> @@ -99,7 +101,7 @@ <style name="Theme.AntennaPod.TrueBlack" parent="Theme.AntennaPod.Dark"> <item name="android:textColorPrimary">@color/white</item> <item name="android:color">@color/white</item> - <item name="android:windowBackground">@color/black</item> + <item name="android:colorBackground">@color/black</item> <item name="colorSurface">@color/black</item> <item name="background_color">@color/black</item> <item name="background_elevated">@color/black</item> @@ -235,24 +237,24 @@ <item name="android:fontFamily">sans-serif-light</item> </style> - <style name="AntennaPod.TextView.ListItemPrimaryTitle" parent="@android:style/TextAppearance.Small"> - <item name="android:textSize">16sp</item> - <item name="android:textColor">?android:attr/textColorPrimary</item> + <style name="AntennaPod.TextView.ListItemPrimaryTitle" parent="@style/TextAppearance.Material3.BodyLarge"> + <item name="android:textColor">?attr/colorOnSurface</item> <item name="android:maxLines">2</item> <item name="android:ellipsize">end</item> + <item name="lineHeight">20sp</item> + <item name="android:lineHeight" tools:targetApi="p">20sp</item> </style> - <style name="AntennaPod.TextView.ListItemPrimaryTitle2" parent="@android:style/TextAppearance.Small"> - <item name="android:textSize">16sp</item> - <item name="android:textColor">?android:attr/textColorPrimary</item> + <style name="AntennaPod.TextView.ListItemSecondaryTitle" parent="@style/TextAppearance.Material3.BodyMedium"> + <item name="android:textColor">?attr/colorOnSurfaceVariant</item> + <item name="android:lines">1</item> <item name="android:ellipsize">end</item> </style> - <style name="AntennaPod.TextView.ListItemSecondaryTitle" parent="@android:style/TextAppearance.Small"> - <item name="android:textSize">14sp</item> - <item name="android:textColor">?android:attr/textColorSecondary</item> - <item name="android:lines">1</item> - <item name="android:ellipsize">end</item> + <style name="AntennaPod.TextView.ListItemBody" parent="@style/TextAppearance.Material3.BodyMedium"> + <item name="android:textColor">?attr/colorOnSurfaceVariant</item> + <item name="lineHeight">18sp</item> + <item name="android:lineHeight" tools:targetApi="p">18sp</item> </style> <style name="OutlinedButtonBetterContrast" parent="Widget.Material3.Button.OutlinedButton"> |