diff options
author | ByteHamster <info@bytehamster.com> | 2021-01-02 16:58:43 +0100 |
---|---|---|
committer | ByteHamster <info@bytehamster.com> | 2021-01-02 16:58:43 +0100 |
commit | 3ed33794327f547c8892d30958a78770dae56d45 (patch) | |
tree | 29095e84b7e8051e6e4bd58e6d7d66578807e614 /core/src/main/java/de/danoeh/antennapod | |
parent | c9635473cb69acfb883f70868f6a8e04670241b1 (diff) | |
parent | d1426f97740103d548db007e3ad2c918d90f8bf0 (diff) | |
download | AntennaPod-3ed33794327f547c8892d30958a78770dae56d45.zip |
Merge branch 'develop' into folders
Diffstat (limited to 'core/src/main/java/de/danoeh/antennapod')
37 files changed, 369 insertions, 433 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/DBTasksCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/DBTasksCallbacks.java deleted file mode 100644 index 11a6b2c9f..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/DBTasksCallbacks.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.danoeh.antennapod.core; - -import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm; -import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm; - -/** - * Callbacks for the DBTasks class of the storage module. - */ -public interface DBTasksCallbacks { - - /** - * Returns the client's implementation of the AutomaticDownloadAlgorithm interface. - */ - AutomaticDownloadAlgorithm getAutomaticDownloadAlgorithm(); - - /** - * Returns the client's implementation of the EpisodeCacheCleanupAlgorithm interface. - */ - EpisodeCleanupAlgorithm getEpisodeCacheCleanupAlgorithm(); -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java index ad3fb8d42..ae9b47629 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java @@ -37,7 +37,7 @@ public interface DownloadServiceCallbacks { * <p/> * The PendingIntent takes users to an activity where they can look at all successful and failed downloads. * - * @return A non-null PendingIntent for the notification or null if shouldCreateReport()==false + * @return A non-null PendingIntent for the notification */ PendingIntent getReportNotificationContentIntent(Context context); @@ -47,14 +47,8 @@ public interface DownloadServiceCallbacks { * <p/> * The PendingIntent takes users to an activity where they can look at their episode queue. * - * @return A non-null PendingIntent for the notification or null if shouldCreateReport()==false + * @return A non-null PendingIntent for the notification */ PendingIntent getAutoDownloadReportNotificationContentIntent(Context context); - - /** - * Returns true if the DownloadService should create a report that shows the number of failed - * downloads when the service shuts down. - */ - boolean shouldCreateReport(); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/PlaybackServiceCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/PlaybackServiceCallbacks.java index 194ee65ae..3dcaac4dc 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/PlaybackServiceCallbacks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/PlaybackServiceCallbacks.java @@ -19,11 +19,4 @@ public interface PlaybackServiceCallbacks { * @return A non-null activity intent. */ Intent getPlayerActivityIntent(Context context, MediaType mediaType, boolean remotePlayback); - - /** - * Returns true if the PlaybackService should load new episodes from the queue when playback ends - * and false if the PlaybackService should ignore the queue and load no more episodes when playback - * finishes. - */ - boolean useQueue(); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java deleted file mode 100644 index 4504b2e7f..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java +++ /dev/null @@ -1,61 +0,0 @@ -package de.danoeh.antennapod.core.asynctask; - -import android.app.ProgressDialog; -import android.content.Context; -import android.os.AsyncTask; - -import java.util.concurrent.ExecutionException; - -import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.core.feed.Feed; -import de.danoeh.antennapod.core.service.playback.PlaybackService; -import de.danoeh.antennapod.core.storage.DBWriter; -import de.danoeh.antennapod.core.util.IntentUtils; - -/** Removes a feed in the background. */ -public class FeedRemover extends AsyncTask<Void, Void, Void> { - private final Context context; - private ProgressDialog dialog; - private final Feed feed; - public boolean skipOnCompletion = false; - - public FeedRemover(Context context, Feed feed) { - super(); - this.context = context; - this.feed = feed; - } - - @Override - protected Void doInBackground(Void... params) { - try { - DBWriter.deleteFeed(context, feed.getId()).get(); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } - return null; - } - - @Override - protected void onPostExecute(Void result) { - if(dialog != null && dialog.isShowing()) { - dialog.dismiss(); - } - if(skipOnCompletion) { - IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE); - } - } - - @Override - protected void onPreExecute() { - dialog = new ProgressDialog(context); - dialog.setMessage(context.getString(R.string.feed_remover_msg)); - dialog.setIndeterminate(true); - dialog.setCancelable(false); - dialog.show(); - } - - public void executeAsync() { - executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java index 4c11d0489..c05e2e9f1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java +++ b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java @@ -20,6 +20,7 @@ import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.math.BigInteger; +import java.nio.charset.Charset; import java.security.DigestInputStream; import java.security.DigestOutputStream; import java.security.MessageDigest; @@ -34,7 +35,6 @@ import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; -import de.danoeh.antennapod.core.util.LangUtils; public class OpmlBackupAgent extends BackupAgentHelper { private static final String OPML_BACKUP_KEY = "opml"; @@ -73,9 +73,9 @@ public class OpmlBackupAgent extends BackupAgentHelper { try { digester = MessageDigest.getInstance("MD5"); writer = new OutputStreamWriter(new DigestOutputStream(byteStream, digester), - LangUtils.UTF_8); + Charset.forName("UTF-8")); } catch (NoSuchAlgorithmException e) { - writer = new OutputStreamWriter(byteStream, LangUtils.UTF_8); + writer = new OutputStreamWriter(byteStream, Charset.forName("UTF-8")); } try { @@ -138,9 +138,9 @@ public class OpmlBackupAgent extends BackupAgentHelper { try { digester = MessageDigest.getInstance("MD5"); reader = new InputStreamReader(new DigestInputStream(data, digester), - LangUtils.UTF_8); + Charset.forName("UTF-8")); } catch (NoSuchAlgorithmException e) { - reader = new InputStreamReader(data, LangUtils.UTF_8); + reader = new InputStreamReader(data, Charset.forName("UTF-8")); } try { diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java index e8e478a86..787f0e5e7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java @@ -1,10 +1,8 @@ package de.danoeh.antennapod.core.feed; import android.text.TextUtils; -import android.util.Log; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import de.danoeh.antennapod.core.storage.DBReader; diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java index 88945b930..4857e899d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java @@ -5,6 +5,7 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.database.Cursor; import android.media.MediaMetadataRetriever; +import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.Nullable; @@ -165,13 +166,20 @@ public class FeedMedia extends FeedFile implements Playable { */ public MediaBrowserCompat.MediaItem getMediaItem() { Playable p = this; - MediaDescriptionCompat description = new MediaDescriptionCompat.Builder() + MediaDescriptionCompat.Builder builder = new MediaDescriptionCompat.Builder() .setMediaId(String.valueOf(id)) .setTitle(p.getEpisodeTitle()) .setDescription(p.getFeedTitle()) - .setSubtitle(p.getFeedTitle()) - .build(); - return new MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE); + .setSubtitle(p.getFeedTitle()); + if (item != null) { + // getImageLocation() also loads embedded images, which we can not send to external devices + if (item.getImageUrl() != null) { + builder.setIconUri(Uri.parse(item.getImageUrl())); + } else if (item.getFeed() != null && item.getFeed().getImageLocation() != null) { + builder.setIconUri(Uri.parse(item.getFeed().getImageLocation())); + } + } + return new MediaBrowserCompat.MediaItem(builder.build(), MediaBrowserCompat.MediaItem.FLAG_PLAYABLE); } /** 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 971808eb4..4e59fd750 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 @@ -10,6 +10,7 @@ import androidx.documentfile.provider.DocumentFile; import org.apache.commons.lang3.StringUtils; +import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -22,7 +23,6 @@ import java.util.List; import java.util.Locale; import java.util.Set; import java.util.UUID; -import java.util.concurrent.ExecutionException; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.service.download.DownloadStatus; @@ -35,16 +35,28 @@ import de.danoeh.antennapod.core.util.DownloadError; public class LocalFeedUpdater { public static void updateFeed(Feed feed, Context context) { + try { + tryUpdateFeed(feed, context); + + if (mustReportDownloadSuccessful(feed)) { + reportSuccess(feed); + } + } catch (Exception e) { + e.printStackTrace(); + reportError(feed, e.getMessage()); + } + } + + private static void tryUpdateFeed(Feed feed, Context context) throws IOException { String uriString = feed.getDownload_url().replace(Feed.PREFIX_LOCAL_FOLDER, ""); DocumentFile documentFolder = DocumentFile.fromTreeUri(context, Uri.parse(uriString)); if (documentFolder == null) { - reportError(feed, "Unable to retrieve document tree." + throw new IOException("Unable to retrieve document tree. " + "Try re-connecting the folder on the podcast info page."); - return; } if (!documentFolder.exists() || !documentFolder.canRead()) { - reportError(feed, "Cannot read local directory. Try re-connecting the folder on the podcast info page."); - return; + throw new IOException("Cannot read local directory. " + + "Try re-connecting the folder on the podcast info page."); } if (feed.getItems() == null) { @@ -97,25 +109,17 @@ public class LocalFeedUpdater { // set default feed image feed.setImageUrl(getDefaultIconUrl(context)); } - if (feed.getPreferences().getAutoDownload()) { - feed.getPreferences().setAutoDownload(false); - feed.getPreferences().setAutoDeleteAction(FeedPreferences.AutoDeleteAction.NO); - try { - DBWriter.setFeedPreferences(feed.getPreferences()).get(); - } catch (ExecutionException | InterruptedException e) { - e.printStackTrace(); - } - } + + feed.getPreferences().setAutoDownload(false); + feed.getPreferences().setAutoDeleteAction(FeedPreferences.AutoDeleteAction.NO); + feed.setDescription(context.getString(R.string.local_feed_description)); + feed.setAuthor(context.getString(R.string.local_folder)); // update items, delete items without existing file; // only delete items if the folder contains at least one element to avoid accidentally // deleting played state or position in case the folder is temporarily unavailable. boolean removeUnlistedItems = (newItems.size() >= 1); DBTasks.updateFeed(context, feed, removeUnlistedItems); - - if (mustReportDownloadSuccessful(feed)) { - reportSuccess(feed); - } } /** @@ -139,46 +143,50 @@ public class LocalFeedUpdater { } private static FeedItem createFeedItem(Feed feed, DocumentFile file, Context context) { - String uuid = UUID.randomUUID().toString(); + FeedItem item = new FeedItem(0, file.getName(), UUID.randomUUID().toString(), + file.getName(), new Date(file.lastModified()), FeedItem.UNPLAYED, feed); + item.setAutoDownload(false); + + long size = file.length(); + FeedMedia media = new FeedMedia(0, item, 0, 0, size, file.getType(), + file.getUri().toString(), file.getUri().toString(), false, null, 0, 0); + item.setMedia(media); + + try { + loadMetadata(item, file, context); + } catch (Exception e) { + item.setDescription(e.getMessage()); + } + + return item; + } + private static void loadMetadata(FeedItem item, DocumentFile file, Context context) throws Exception { MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); mediaMetadataRetriever.setDataSource(context, file.getUri()); - String dateStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE); - Date date = null; + String dateStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE); if (!TextUtils.isEmpty(dateStr)) { try { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.getDefault()); - date = simpleDateFormat.parse(dateStr); + item.setPubDate(simpleDateFormat.parse(dateStr)); } catch (ParseException parseException) { - date = DateUtils.parse(dateStr); - if (date == null) { - date = new Date(file.lastModified()); + Date date = DateUtils.parse(dateStr); + if (date != null) { + item.setPubDate(date); } } - } else { - date = new Date(file.lastModified()); } - FeedItem item = new FeedItem(0, file.getName(), uuid, file.getName(), date, - FeedItem.UNPLAYED, feed); - item.setAutoDownload(false); - - String durationStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); String title = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE); if (!TextUtils.isEmpty(title)) { item.setTitle(title); } - //add the media to the item - long duration = Long.parseLong(durationStr); - long size = file.length(); - FeedMedia media = new FeedMedia(0, item, (int) duration, 0, size, file.getType(), - file.getUri().toString(), file.getUri().toString(), false, null, 0, 0); - media.setHasEmbeddedPicture(mediaMetadataRetriever.getEmbeddedPicture() != null); - item.setMedia(media); + String durationStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); + item.getMedia().setDuration((int) Long.parseLong(durationStr)); - return item; + item.getMedia().setHasEmbeddedPicture(mediaMetadataRetriever.getEmbeddedPicture() != null); } private static void reportError(Feed feed, String reasonDetailed) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java b/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java index 0d5ecbb71..d6740994d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.core.feed.util; +import android.util.Log; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; @@ -14,6 +15,7 @@ import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL; * Utility class to use the appropriate playback speed based on {@link PlaybackPreferences} */ public final class PlaybackSpeedUtils { + private static final String TAG = "PlaybackSpeedUtils"; private PlaybackSpeedUtils() { } @@ -33,8 +35,10 @@ public final class PlaybackSpeedUtils { FeedItem item = ((FeedMedia) media).getItem(); if (item != null) { Feed feed = item.getFeed(); - if (feed != null) { + if (feed != null && feed.getPreferences() != null) { playbackSpeed = feed.getPreferences().getFeedPlaybackSpeed(); + } else { + Log.d(TAG, "Can not get feed specific playback speed: " + feed); } } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java index ab4247cef..b3adc567e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java @@ -10,7 +10,6 @@ import com.bumptech.glide.annotation.GlideModule; import com.bumptech.glide.load.DecodeFormat; import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory; import com.bumptech.glide.load.model.StringLoader; -import com.bumptech.glide.load.model.UriLoader; import com.bumptech.glide.module.AppGlideModule; import de.danoeh.antennapod.core.util.EmbeddedChapterImage; diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java index 35a9d987b..519d625e2 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java @@ -10,17 +10,13 @@ import com.bumptech.glide.load.model.ModelLoader; import com.bumptech.glide.load.model.ModelLoaderFactory; import com.bumptech.glide.load.model.MultiModelLoaderFactory; import com.bumptech.glide.signature.ObjectKey; -import de.danoeh.antennapod.core.ClientConfig; -import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import de.danoeh.antennapod.core.util.EmbeddedChapterImage; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.net.URL; import java.nio.ByteBuffer; -import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import org.apache.commons.io.IOUtils; diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java index 56dd95fe6..ed9c519a6 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java @@ -301,10 +301,30 @@ public class UserPreferences { * @return {@code true} if download reports are shown, {@code false} otherwise */ public static boolean showDownloadReport() { + if (Build.VERSION.SDK_INT >= 26) { + return true; // System handles notification preferences + } + return prefs.getBoolean(PREF_SHOW_DOWNLOAD_REPORT, true); + } + + /** + * Used for migration of the preference to system notification channels. + */ + public static boolean getShowDownloadReportRaw() { return prefs.getBoolean(PREF_SHOW_DOWNLOAD_REPORT, true); } public static boolean showAutoDownloadReport() { + if (Build.VERSION.SDK_INT >= 26) { + return true; // System handles notification preferences + } + return prefs.getBoolean(PREF_SHOW_AUTO_DOWNLOAD_REPORT, false); + } + + /** + * Used for migration of the preference to system notification channels. + */ + public static boolean getShowAutoDownloadReportRaw() { return prefs.getBoolean(PREF_SHOW_AUTO_DOWNLOAD_REPORT, false); } @@ -728,6 +748,16 @@ public class UserPreferences { } public static boolean gpodnetNotificationsEnabled() { + if (Build.VERSION.SDK_INT >= 26) { + return true; // System handles notification preferences + } + return prefs.getBoolean(PREF_GPODNET_NOTIFICATIONS, true); + } + + /** + * Used for migration of the preference to system notification channels. + */ + public static boolean getGpodnetNotificationsEnabledRaw() { return prefs.getBoolean(PREF_GPODNET_NOTIFICATIONS, true); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java index de106a01e..5a2c653d6 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java @@ -39,7 +39,6 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.event.FeedItemEvent; import de.danoeh.antennapod.core.feed.Feed; @@ -168,6 +167,7 @@ public class DownloadService extends Service { startForeground(R.id.notification_downloading, notification); syncExecutor.execute(() -> onDownloadQueued(intent)); } else if (numberOfDownloads.get() == 0) { + stopForeground(true); stopSelf(); } else { Log.d(TAG, "onStartCommand: Unknown intent"); @@ -205,8 +205,7 @@ public class DownloadService extends Service { isRunning = false; boolean showAutoDownloadReport = UserPreferences.showAutoDownloadReport(); - if (ClientConfig.downloadServiceCallbacks.shouldCreateReport() - && (UserPreferences.showDownloadReport() || showAutoDownloadReport)) { + if (UserPreferences.showDownloadReport() || showAutoDownloadReport) { notificationManager.updateReport(reportQueue, showAutoDownloadReport); reportQueue.clear(); } @@ -429,7 +428,7 @@ public class DownloadService extends Service { + ", cleanupMedia=" + cleanupMedia); if (cleanupMedia) { - ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm() + UserPreferences.getEpisodeCleanupAlgorithm() .makeRoomForEpisodes(getApplicationContext(), requests.size()); } @@ -553,6 +552,7 @@ public class DownloadService extends Service { if (numberOfDownloads.get() <= 0 && DownloadRequester.getInstance().hasNoDownloads()) { Log.d(TAG, "Number of downloads is " + numberOfDownloads.get() + ", attempting shutdown"); + stopForeground(true); stopSelf(); if (notificationUpdater != null) { notificationUpdater.run(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java index 0715d50dd..fb6009c02 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java @@ -148,7 +148,7 @@ public class DownloadServiceNotification { id = R.id.notification_auto_download_report; content = createAutoDownloadNotificationContent(reportQueue); } else { - channelId = NotificationUtils.CHANNEL_ID_ERROR; + channelId = NotificationUtils.CHANNEL_ID_DOWNLOAD_ERROR; titleId = R.string.download_report_title; iconId = R.drawable.ic_notification_sync_error; intent = ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(context); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java index 65b7ed7d1..393592cf9 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java @@ -4,7 +4,6 @@ import androidx.annotation.NonNull; import android.text.TextUtils; import android.util.Log; -import de.danoeh.antennapod.core.service.BasicAuthorizationInterceptor; import okhttp3.CacheControl; import org.apache.commons.io.IOUtils; @@ -21,14 +20,12 @@ import java.net.UnknownHostException; import java.util.Collections; import java.util.Date; -import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.util.DateUtils; import de.danoeh.antennapod.core.util.DownloadError; import de.danoeh.antennapod.core.util.StorageUtils; import de.danoeh.antennapod.core.util.URIUtil; -import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Protocol; import okhttp3.Request; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java index 041d26bd4..386e5e6f7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java @@ -3,7 +3,6 @@ package de.danoeh.antennapod.core.service.download.handler; import android.util.Log; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.service.download.DownloadRequest; -import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.storage.DBWriter; /** 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 ae5d62872..325b04e9a 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 @@ -1,7 +1,6 @@ package de.danoeh.antennapod.core.service.playback; import android.content.Context; -import android.media.AudioAttributes; import android.media.AudioManager; import android.os.PowerManager; import androidx.annotation.NonNull; 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 60075dda6..c1500d78b 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 @@ -7,6 +7,7 @@ import android.app.UiModeManager; import android.bluetooth.BluetoothA2dp; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -51,7 +52,6 @@ import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.core.event.FeedItemEvent; import de.danoeh.antennapod.core.event.MessageEvent; import de.danoeh.antennapod.core.event.PlaybackPositionEvent; import de.danoeh.antennapod.core.event.ServiceEvent; @@ -149,7 +149,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { public static final String ACTION_PAUSE_PLAY_CURRENT_EPISODE = "action.de.danoeh.antennapod.core.service.pausePlayCurrentEpisode"; /** - * Custom action used by Android Wear + * Custom action used by Android Wear, Android Auto */ private static final String CUSTOM_ACTION_FAST_FORWARD = "action.de.danoeh.antennapod.core.service.fastForward"; private static final String CUSTOM_ACTION_REWIND = "action.de.danoeh.antennapod.core.service.rewind"; @@ -370,9 +370,26 @@ public class PlaybackService extends MediaBrowserServiceCompat { } private MediaBrowserCompat.MediaItem createBrowsableMediaItemForRoot() { + Uri uri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(getResources().getResourcePackageName(R.drawable.ic_playlist_black)) + .appendPath(getResources().getResourceTypeName(R.drawable.ic_playlist_black)) + .appendPath(getResources().getResourceEntryName(R.drawable.ic_playlist_black)) + .build(); + + String subtitle = ""; + try { + int count = taskManager.getQueue().size(); + subtitle = getResources().getQuantityString(R.plurals.num_episodes, count, count); + } catch (InterruptedException e) { + e.printStackTrace(); + } + MediaDescriptionCompat description = new MediaDescriptionCompat.Builder() + .setIconUri(uri) .setMediaId(getResources().getString(R.string.queue_label)) .setTitle(getResources().getString(R.string.queue_label)) + .setSubtitle(subtitle) .build(); return new MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE); @@ -517,11 +534,12 @@ public class PlaybackService extends MediaBrowserServiceCompat { .observeOn(AndroidSchedulers.mainThread()) .subscribe( playableLoaded -> { - mediaPlayer.playMediaObject(playable, stream, startWhenPrepared, + mediaPlayer.playMediaObject(playableLoaded, stream, startWhenPrepared, prepareImmediately); - addPlayableToQueue(playable); + addPlayableToQueue(playableLoaded); }, error -> { Log.d(TAG, "Playable was not found. Stopping service."); + error.printStackTrace(); stateManager.stopService(); }); return Service.START_NOT_STICKY; @@ -729,6 +747,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { addPlayableToQueue(playable); }, error -> { Log.d(TAG, "Playable was not loaded from preferences. Stopping service."); + error.printStackTrace(); stateManager.stopService(); }); } @@ -971,10 +990,6 @@ public class PlaybackService extends MediaBrowserServiceCompat { Log.d(TAG, "getNextInQueue(), but playable not an instance of FeedMedia, so not proceeding"); return null; } - if (!ClientConfig.playbackServiceCallbacks.useQueue()) { - Log.d(TAG, "getNextInQueue(), but queue not in use by this app"); - return null; - } Log.d(TAG, "getNextInQueue()"); FeedMedia media = (FeedMedia) currentMedia; try { @@ -1022,6 +1037,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { */ private void onPlaybackEnded(MediaType mediaType, boolean stopPlaying) { Log.d(TAG, "Playback ended"); + PlaybackPreferences.clearCurrentlyPlayingTemporaryPlaybackSpeed(); if (stopPlaying) { taskManager.cancelPositionSaver(); cancelPositionObserver(); @@ -1060,7 +1076,6 @@ public class PlaybackService extends MediaBrowserServiceCompat { */ private void onPostPlayback(final Playable playable, boolean ended, boolean skipped, boolean playingNext) { - PlaybackPreferences.clearCurrentlyPlayingTemporaryPlaybackSpeed(); if (playable == null) { Log.e(TAG, "Cannot do post-playback processing: media was null"); return; @@ -1206,6 +1221,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { sessionState.setState(state, getCurrentPosition(), getCurrentPlaybackSpeed()); long capabilities = PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_REWIND + | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_FAST_FORWARD | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SEEK_TO; @@ -1236,17 +1252,24 @@ public class PlaybackService extends MediaBrowserServiceCompat { CUSTOM_ACTION_FAST_FORWARD, getString(R.string.fast_forward_label), R.drawable.ic_notification_fast_forward) .build()); + } else { + // This would give the PIP of videos a play button + capabilities = capabilities | PlaybackStateCompat.ACTION_PLAY; + if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_WATCH) { + flavorHelper.sessionStateAddActionForWear(sessionState, + CUSTOM_ACTION_REWIND, + getString(R.string.rewind_label), + android.R.drawable.ic_media_rew); + flavorHelper.sessionStateAddActionForWear(sessionState, + CUSTOM_ACTION_FAST_FORWARD, + getString(R.string.fast_forward_label), + android.R.drawable.ic_media_ff); + flavorHelper.mediaSessionSetExtraForWear(mediaSession); + } } sessionState.setActions(capabilities); - flavorHelper.sessionStateAddActionForWear(sessionState, - CUSTOM_ACTION_REWIND, getString(R.string.rewind_label), android.R.drawable.ic_media_rew); - flavorHelper.sessionStateAddActionForWear(sessionState, - CUSTOM_ACTION_FAST_FORWARD, getString(R.string.fast_forward_label), android.R.drawable.ic_media_ff); - - flavorHelper.mediaSessionSetExtraForWear(mediaSession); - mediaSession.setPlaybackState(sessionState.build()); } 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 632ac07ea..9d249620d 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 @@ -69,7 +69,7 @@ public class PlaybackServiceNotificationBuilder { } public void loadIcon() { - int iconSize = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width); + int iconSize = (int) (128 * context.getResources().getDisplayMetrics().density); try { icon = Glide.with(context) .asBitmap() diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java index 720d6a9d9..78c105e38 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java +++ b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java @@ -70,4 +70,36 @@ public class BackportCaCerts { + "0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB\n" + "NVOFBkpdn627G190\n" + "-----END CERTIFICATE-----"; + + public static final String LETSENCRYPT_ISRG = "-----BEGIN CERTIFICATE-----\n" + + "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" + + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" + + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n" + + "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n" + + "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n" + + "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n" + + "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n" + + "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n" + + "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n" + + "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n" + + "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n" + + "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n" + + "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n" + + "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n" + + "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n" + + "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n" + + "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" + + "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n" + + "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n" + + "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n" + + "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n" + + "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n" + + "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n" + + "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n" + + "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n" + + "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n" + + "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n" + + "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" + + "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" + + "-----END CERTIFICATE-----"; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java index b8fe950b2..81d2a0709 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java +++ b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java @@ -45,6 +45,8 @@ public class BackportTrustManager { new ByteArrayInputStream(BackportCaCerts.COMODO.getBytes(Charset.forName("UTF-8"))))); keystore.setCertificateEntry("SECTIGO_USER_TRUST_CA", cf.generateCertificate( new ByteArrayInputStream(BackportCaCerts.SECTIGO_USER_TRUST.getBytes(Charset.forName("UTF-8"))))); + keystore.setCertificateEntry("LETSENCRYPT_ISRG_CA", cf.generateCertificate( + new ByteArrayInputStream(BackportCaCerts.LETSENCRYPT_ISRG.getBytes(Charset.forName("UTF-8"))))); List<X509TrustManager> managers = new ArrayList<>(); managers.add(getSystemTrustManager(keystore)); diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java index 7ec4db5dd..061d6cf3f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java @@ -65,7 +65,7 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm { Iterator<FeedItem> it = candidates.iterator(); while (it.hasNext()) { FeedItem item = it.next(); - if (!item.isAutoDownloadable()) { + if (!item.isAutoDownloadable() || 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 baeca9bb9..8721dcd08 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 @@ -7,6 +7,7 @@ import androidx.collection.ArrayMap; import android.text.TextUtils; import android.util.Log; +import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -773,7 +774,7 @@ public final class DBReader { feedTotalTime += media.getDuration() / 1000; if (media.isDownloaded()) { - totalDownloadSize = totalDownloadSize + media.getSize(); + totalDownloadSize += new File(media.getFile_url()).length(); episodesDownloadCount++; } 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 c059e696a..ec39e7144 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 @@ -6,7 +6,6 @@ import android.database.Cursor; import android.os.Looper; import android.text.TextUtils; import android.util.Log; -import androidx.annotation.VisibleForTesting; import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.event.FeedItemEvent; @@ -290,7 +289,7 @@ public final class DBTasks { */ public static Future<?> autodownloadUndownloadedItems(final Context context) { Log.d(TAG, "autodownloadUndownloadedItems"); - return autodownloadExec.submit(ClientConfig.dbTasksCallbacks.getAutomaticDownloadAlgorithm() + return autodownloadExec.submit(ClientConfig.automaticDownloadAlgorithm .autoDownloadUndownloadedItems(context)); } @@ -304,7 +303,7 @@ public final class DBTasks { * @param context Used for accessing the DB. */ public static void performAutoCleanup(final Context context) { - ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm().performCleanup(context); + UserPreferences.getEpisodeCleanupAlgorithm().performCleanup(context); } /** @@ -445,7 +444,8 @@ public final class DBTasks { // as the most recent item // (if the most recent date is null then we can assume there are no items // and this is the first, hence 'new') - if (priorMostRecentDate == null + // New items that do not have a pubDate set are always marked as new + if (item.getPubDate() == null || priorMostRecentDate == null || priorMostRecentDate.before(item.getPubDate()) || priorMostRecentDate.equals(item.getPubDate())) { Log.d(TAG, "Marking item published on " + item.getPubDate() 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 9e6041df3..84cc4b6a8 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 @@ -21,8 +21,8 @@ import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; -import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.event.DownloadLogEvent; import de.danoeh.antennapod.core.event.FavoritesEvent; @@ -74,6 +74,18 @@ public class DBWriter { } /** + * Wait until all threads are finished to avoid the "Illegal connection pointer" error of + * Robolectric. Call this method only for unit tests. + */ + public static void tearDownTests() { + try { + dbExec.awaitTermination(1, TimeUnit.SECONDS); + } catch (InterruptedException e) { + // ignore error + } + } + + /** * Deletes a downloaded FeedMedia file from the storage device. * * @param context A context that is used for opening a database connection. diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java index 2f48cfc07..ea62065fc 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java @@ -3,10 +3,8 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; import androidx.annotation.NonNull; import de.danoeh.antennapod.core.feed.Feed; -import de.danoeh.antennapod.core.feed.FeedComponent; import de.danoeh.antennapod.core.feed.FeedItem; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index 935b06cd6..4a4f94053 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -1,6 +1,5 @@ package de.danoeh.antennapod.core.storage; -import android.annotation.SuppressLint; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -195,11 +194,11 @@ public class PodDBAdapter { + TABLE_NAME_FEED_ITEMS + "_" + KEY_FEED + " ON " + TABLE_NAME_FEED_ITEMS + " (" + KEY_FEED + ")"; - static final String CREATE_INDEX_FEEDITEMS_PUBDATE = "CREATE INDEX IF NOT EXISTS " + static final String CREATE_INDEX_FEEDITEMS_PUBDATE = "CREATE INDEX " + TABLE_NAME_FEED_ITEMS + "_" + KEY_PUBDATE + " ON " + TABLE_NAME_FEED_ITEMS + " (" + KEY_PUBDATE + ")"; - static final String CREATE_INDEX_FEEDITEMS_READ = "CREATE INDEX IF NOT EXISTS " + static final String CREATE_INDEX_FEEDITEMS_READ = "CREATE INDEX " + TABLE_NAME_FEED_ITEMS + "_" + KEY_READ + " ON " + TABLE_NAME_FEED_ITEMS + " (" + KEY_READ + ")"; @@ -316,46 +315,44 @@ public class PodDBAdapter { + JOIN_FEED_ITEM_AND_MEDIA; private static Context context; + private static PodDBAdapter instance; - private static volatile SQLiteDatabase db; + private final SQLiteDatabase db; + private final PodDBHelper dbHelper; public static void init(Context context) { PodDBAdapter.context = context.getApplicationContext(); } - // Bill Pugh Singleton Implementation - private static class SingletonHolder { - private static final PodDBHelper dbHelper = new PodDBHelper(PodDBAdapter.context, DATABASE_NAME, null); - private static final PodDBAdapter dbAdapter = new PodDBAdapter(); - } - public static PodDBAdapter getInstance() { - return SingletonHolder.dbAdapter; + if (instance == null) { + instance = new PodDBAdapter(); + } + return instance; } private PodDBAdapter() { + dbHelper = new PodDBHelper(PodDBAdapter.context, DATABASE_NAME, null); + db = openDb(); } - public synchronized PodDBAdapter open() { - if (db == null || !db.isOpen() || db.isReadOnly()) { - db = openDb(); - } - return this; - } - - @SuppressLint("NewApi") private SQLiteDatabase openDb() { SQLiteDatabase newDb; try { - newDb = SingletonHolder.dbHelper.getWritableDatabase(); + newDb = dbHelper.getWritableDatabase(); newDb.disableWriteAheadLogging(); } catch (SQLException ex) { Log.e(TAG, Log.getStackTraceString(ex)); - newDb = SingletonHolder.dbHelper.getReadableDatabase(); + newDb = dbHelper.getReadableDatabase(); } return newDb; } + public synchronized PodDBAdapter open() { + // do nothing + return this; + } + public synchronized void close() { // do nothing } @@ -371,8 +368,8 @@ public class PodDBAdapter { * <a href="https://github.com/robolectric/robolectric/issues/1890">robolectric/robolectric#1890</a>.</p> */ public static void tearDownTests() { - db = null; - SingletonHolder.dbHelper.close(); + getInstance().dbHelper.close(); + instance = null; } public static boolean deleteDatabase() { @@ -380,7 +377,7 @@ public class PodDBAdapter { adapter.open(); try { for (String tableName : ALL_TABLES) { - db.delete(tableName, "1", null); + adapter.db.delete(tableName, "1", null); } return true; } finally { 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 1f5d9b75f..7563ab715 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 @@ -80,7 +80,7 @@ public class SyncService extends Worker { if (!GpodnetPreferences.loggedIn()) { return Result.success(); } - syncServiceImpl = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetService.DEFAULT_BASE_HOST); + syncServiceImpl = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetPreferences.getHostname()); SharedPreferences.Editor prefs = getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) .edit(); prefs.putLong(PREF_LAST_SYNC_ATTEMPT_TIMESTAMP, System.currentTimeMillis()).apply(); @@ -485,7 +485,11 @@ public class SyncService extends Worker { } private void updateErrorNotification(SyncServiceException exception) { - Log.d(TAG, "Posting error notification"); + 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(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java index e15ab2fdc..833ff33f1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java @@ -22,14 +22,7 @@ public class DateUtils { private DateUtils(){} private static final String TAG = "DateUtils"; - private static final TimeZone defaultTimezone = TimeZone.getTimeZone("GMT"); - private static final SimpleDateFormat dateFormatParser = new SimpleDateFormat("", Locale.US); - - static { - dateFormatParser.setLenient(false); - dateFormatParser.setTimeZone(defaultTimezone); - } public static Date parse(final String input) { if (input == null) { @@ -99,12 +92,16 @@ public class DateUtils { "EEE d MMM yyyy HH:mm:ss 'GMT'Z (z)" }; + SimpleDateFormat parser = new SimpleDateFormat("", Locale.US); + parser.setLenient(false); + parser.setTimeZone(defaultTimezone); + ParsePosition pos = new ParsePosition(0); for (String pattern : patterns) { - dateFormatParser.applyPattern(pattern); + parser.applyPattern(pattern); pos.setIndex(0); try { - Date result = dateFormatParser.parse(date, pos); + Date result = parser.parse(date, pos); if (result != null && pos.getIndex() == date.length()) { return result; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java deleted file mode 100644 index 20af6415e..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java +++ /dev/null @@ -1,124 +0,0 @@ -package de.danoeh.antennapod.core.util; - -import androidx.collection.ArrayMap; - -import java.nio.charset.Charset; - -public class LangUtils { - - private LangUtils(){} - - public static final Charset UTF_8 = Charset.forName("UTF-8"); - - private static final ArrayMap<String, String> languages; - static { - languages = new ArrayMap<>(); - languages.put("af", "Afrikaans"); - languages.put("sq", "Albanian"); - languages.put("sq", "Albanian"); - languages.put("eu", "Basque"); - languages.put("be", "Belarusian"); - languages.put("bg", "Bulgarian"); - languages.put("ca", "Catalan"); - languages.put("Chinese (Simplified)", "zh-cn"); - languages.put("Chinese (Traditional)", "zh-tw"); - languages.put("hr", "Croatian"); - languages.put("cs", "Czech"); - languages.put("da", "Danish"); - languages.put("nl", "Dutch"); - languages.put("nl-be", "Dutch (Belgium)"); - languages.put("nl-nl", "Dutch (Netherlands)"); - languages.put("en", "English"); - languages.put("en-au", "English (Australia)"); - languages.put("en-bz", "English (Belize)"); - languages.put("en-ca", "English (Canada)"); - languages.put("en-ie", "English (Ireland)"); - languages.put("en-jm", "English (Jamaica)"); - languages.put("en-nz", "English (New Zealand)"); - languages.put("en-ph", "English (Phillipines)"); - languages.put("en-za", "English (South Africa)"); - languages.put("en-tt", "English (Trinidad)"); - languages.put("en-gb", "English (United Kingdom)"); - languages.put("en-us", "English (United States)"); - languages.put("en-zw", "English (Zimbabwe)"); - languages.put("et", "Estonian"); - languages.put("fo", "Faeroese"); - languages.put("fi", "Finnish"); - languages.put("fr", "French"); - languages.put("fr-be", "French (Belgium)"); - languages.put("fr-ca", "French (Canada)"); - languages.put("fr-fr", "French (France)"); - languages.put("fr-lu", "French (Luxembourg)"); - languages.put("fr-mc", "French (Monaco)"); - languages.put("fr-ch", "French (Switzerland)"); - languages.put("gl", "Galician"); - languages.put("gd", "Gaelic"); - languages.put("de", "German"); - languages.put("de-at", "German (Austria)"); - languages.put("de-de", "German (Germany)"); - languages.put("de-li", "German (Liechtenstein)"); - languages.put("de-lu", "German (Luxembourg)"); - languages.put("de-ch", "German (Switzerland)"); - languages.put("el", "Greek"); - languages.put("haw", "Hawaiian"); - languages.put("hu", "Hungarian"); - languages.put("is", "Icelandic"); - languages.put("in", "Indonesian"); - languages.put("ga", "Irish"); - languages.put("it", "Italian"); - languages.put("it-it", "Italian (Italy)"); - languages.put("it-ch", "Italian (Switzerland)"); - languages.put("ja", "Japanese"); - languages.put("ko", "Korean"); - languages.put("mk", "Macedonian"); - languages.put("no", "Norwegian"); - languages.put("pl", "Polish"); - languages.put("pt", "Portugese"); - languages.put("pt-br", "Portugese (Brazil)"); - languages.put("pt-pt", "Portugese (Portugal"); - languages.put("ro", "Romanian"); - languages.put("ro-mo", "Romanian (Moldova)"); - languages.put("ro-ro", "Romanian (Romania"); - languages.put("ru", "Russian"); - languages.put("ru-mo", "Russian (Moldova)"); - languages.put("ru-ru", "Russian (Russia)"); - languages.put("sr", "Serbian"); - languages.put("sk", "Slovak"); - languages.put("sl", "Slovenian"); - languages.put("es", "Spanish"); - languages.put("es-ar", "Spanish (Argentinia)"); - languages.put("es=bo", "Spanish (Bolivia)"); - languages.put("es-cl", "Spanish (Chile)"); - languages.put("es-co", "Spanish (Colombia)"); - languages.put("es-cr", "Spanish (Costa Rica)"); - languages.put("es-do", "Spanish (Dominican Republic)"); - languages.put("es-ec", "Spanish (Ecuador)"); - languages.put("es-sv", "Spanish (El Salvador)"); - languages.put("es-gt", "Spanish (Guatemala)"); - languages.put("es-hn", "Spanish (Honduras)"); - languages.put("es-mx", "Spanish (Mexico)"); - languages.put("es-ni", "Spanish (Nicaragua)"); - languages.put("es-pa", "Spanish (Panama)"); - languages.put("es-py", "Spanish (Paraguay)"); - languages.put("es-pe", "Spanish (Peru)"); - languages.put("es-pr", "Spanish (Puerto Rico)"); - languages.put("es-es", "Spanish (Spain)"); - languages.put("es-uy", "Spanish (Uruguay)"); - languages.put("es-ve", "Spanish (Venezuela)"); - languages.put("sv", "Swedish"); - languages.put("sv-fi", "Swedish (Finland)"); - languages.put("sv-se", "Swedish (Sweden)"); - languages.put("tr", "Turkish"); - languages.put("uk", "Ukranian"); - } - - /** Finds language string for key or returns the language key if it can't be found. */ - public static String getLanguageString(String key) { - String language = languages.get(key); - if (language != null) { - return language; - } else { - return key; - } - } -} 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 3e9e8327e..2622d81aa 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 @@ -19,74 +19,77 @@ import de.danoeh.antennapod.core.feed.FeedMedia; /** Utility methods for sharing data */ public class ShareUtils { - private static final String TAG = "ShareUtils"; - - private ShareUtils() {} - - public static void shareLink(Context context, String text) { - Intent i = new Intent(Intent.ACTION_SEND); - i.setType("text/plain"); - i.putExtra(Intent.EXTRA_TEXT, text); - context.startActivity(Intent.createChooser(i, context.getString(R.string.share_url_label))); - } + private static final String TAG = "ShareUtils"; - public static void shareFeedlink(Context context, Feed feed) { - shareLink(context, feed.getTitle() + ": " + feed.getLink()); - } - - public static void shareFeedDownloadLink(Context context, Feed feed) { - shareLink(context, feed.getTitle() + ": " + feed.getDownload_url()); - } + private ShareUtils() { + } - public static void shareFeedItemLink(Context context, FeedItem item) { - shareFeedItemLink(context, item, false); - } + public static void shareLink(Context context, String text) { + Intent i = new Intent(Intent.ACTION_SEND); + i.setType("text/plain"); + i.putExtra(Intent.EXTRA_TEXT, text); + context.startActivity(Intent.createChooser(i, context.getString(R.string.share_url_label))); + } - public static void shareFeedItemDownloadLink(Context context, FeedItem item) { - shareFeedItemDownloadLink(context, item, false); - } + public static void shareFeedlink(Context context, Feed feed) { + shareLink(context, feed.getTitle() + ": " + feed.getLink()); + } - private static String getItemShareText(FeedItem item) { - return item.getFeed().getTitle() + ": " + item.getTitle(); - } + public static void shareFeedDownloadLink(Context context, Feed feed) { + shareLink(context, feed.getTitle() + ": " + feed.getDownload_url()); + } + + public static void shareFeedItemLink(Context context, FeedItem item) { + shareFeedItemLink(context, item, false); + } + + public static void shareFeedItemDownloadLink(Context context, FeedItem item) { + shareFeedItemDownloadLink(context, item, false); + } + + private static String getItemShareText(FeedItem item) { + return item.getFeed().getTitle() + ": " + item.getTitle(); + } public static boolean hasLinkToShare(FeedItem item) { - return FeedItemUtil.getLinkWithFallback(item) != null; + return FeedItemUtil.getLinkWithFallback(item) != null; } - public static void shareFeedItemLink(Context context, FeedItem item, boolean withPosition) { - String text = getItemShareText(item) + " " + FeedItemUtil.getLinkWithFallback(item); - if(withPosition) { - int pos = item.getMedia().getPosition(); - text += " [" + Converter.getDurationStringLong(pos) + "]"; - } - shareLink(context, text); - } + public static void shareFeedItemLink(Context context, FeedItem item, boolean withPosition) { + String text = getItemShareText(item) + " " + FeedItemUtil.getLinkWithFallback(item); + if (withPosition) { + int pos = item.getMedia().getPosition(); + text += " [" + Converter.getDurationStringLong(pos) + "]"; + } + shareLink(context, text); + } - public static void shareFeedItemDownloadLink(Context context, FeedItem item, boolean withPosition) { - String text = getItemShareText(item) + " " + item.getMedia().getDownload_url(); - if(withPosition) { - int pos = item.getMedia().getPosition(); - text += " [" + Converter.getDurationStringLong(pos) + "]"; - } - shareLink(context, text); - } + public static void shareFeedItemDownloadLink(Context context, FeedItem item, boolean withPosition) { + String text = getItemShareText(item) + " " + item.getMedia().getDownload_url(); + if (withPosition) { + int pos = item.getMedia().getPosition(); + text += "#t=" + pos / 1000; + text += " [" + Converter.getDurationStringLong(pos) + "]"; + } + shareLink(context, text); + } - public static void shareFeedItemFile(Context context, FeedMedia media) { - Intent i = new Intent(Intent.ACTION_SEND); - i.setType(media.getMime_type()); - Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority), - new File(media.getLocalMediaUrl())); - i.putExtra(Intent.EXTRA_STREAM, fileUri); - i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { - List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY); - for (ResolveInfo resolveInfo : resInfoList) { - String packageName = resolveInfo.activityInfo.packageName; - context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); - } - } - context.startActivity(Intent.createChooser(i, context.getString(R.string.share_file_label))); - Log.e(TAG, "shareFeedItemFile called"); - } + public static void shareFeedItemFile(Context context, FeedMedia media) { + Intent i = new Intent(Intent.ACTION_SEND); + i.setType(media.getMime_type()); + Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority), + new File(media.getLocalMediaUrl())); + i.putExtra(Intent.EXTRA_STREAM, fileUri); + i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + List<ResolveInfo> resInfoList = context.getPackageManager() + .queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY); + for (ResolveInfo resolveInfo : resInfoList) { + String packageName = resolveInfo.activityInfo.packageName; + context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } + } + context.startActivity(Intent.createChooser(i, context.getString(R.string.share_file_label))); + Log.e(TAG, "shareFeedItemFile called"); + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java index ac7f4848c..cb7db1709 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java @@ -9,7 +9,6 @@ import de.danoeh.antennapod.core.BuildConfig; import okhttp3.HttpUrl; import java.util.ArrayList; -import java.util.Collections; import java.util.List; /** diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java index ad81a1d17..766986bed 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java @@ -14,8 +14,12 @@ public class FeedItemPubdateComparator implements Comparator<FeedItem> { */ @Override public int compare(FeedItem lhs, FeedItem rhs) { - if (rhs.getPubDate() == null || lhs.getPubDate() == null) { + if (rhs.getPubDate() == null && lhs.getPubDate() == null) { return 0; + } else if (rhs.getPubDate() == null) { + return 1; + } else if (lhs.getPubDate() == null) { + return -1; } return rhs.getPubDate().compareTo(lhs.getPubDate()); } 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 ddbe68938..3101eac34 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 @@ -2,28 +2,36 @@ package de.danoeh.antennapod.core.util.gui; import android.app.NotificationChannel; +import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.Context; import android.os.Build; import androidx.annotation.RequiresApi; import de.danoeh.antennapod.core.R; +import de.danoeh.antennapod.core.preferences.UserPreferences; public class NotificationUtils { public static final String CHANNEL_ID_USER_ACTION = "user_action"; public static final String CHANNEL_ID_DOWNLOADING = "downloading"; public static final String CHANNEL_ID_PLAYING = "playing"; - public static final String CHANNEL_ID_ERROR = "error"; + 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 GROUP_ID_ERRORS = "group_errors"; + public static final String GROUP_ID_NEWS = "group_news"; + public static void createChannels(Context context) { - if (android.os.Build.VERSION.SDK_INT < 26) { + if (Build.VERSION.SDK_INT < 26) { return; } NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); if (mNotificationManager != null) { + mNotificationManager.createNotificationChannelGroup(createGroupErrors(context)); + mNotificationManager.createNotificationChannelGroup(createGroupNews(context)); + mNotificationManager.createNotificationChannel(createChannelUserAction(context)); mNotificationManager.createNotificationChannel(createChannelDownloading(context)); mNotificationManager.createNotificationChannel(createChannelPlaying(context)); @@ -35,36 +43,43 @@ public class NotificationUtils { @RequiresApi(api = Build.VERSION_CODES.O) private static NotificationChannel createChannelUserAction(Context c) { - NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_USER_ACTION, + NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_USER_ACTION, c.getString(R.string.notification_channel_user_action), NotificationManager.IMPORTANCE_HIGH); - mChannel.setDescription(c.getString(R.string.notification_channel_user_action_description)); - return mChannel; + notificationChannel.setDescription(c.getString(R.string.notification_channel_user_action_description)); + notificationChannel.setGroup(GROUP_ID_ERRORS); + return notificationChannel; } @RequiresApi(api = Build.VERSION_CODES.O) private static NotificationChannel createChannelDownloading(Context c) { - NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_DOWNLOADING, + NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_DOWNLOADING, c.getString(R.string.notification_channel_downloading), NotificationManager.IMPORTANCE_LOW); - mChannel.setDescription(c.getString(R.string.notification_channel_downloading_description)); - mChannel.setShowBadge(false); - return mChannel; + notificationChannel.setDescription(c.getString(R.string.notification_channel_downloading_description)); + notificationChannel.setShowBadge(false); + return notificationChannel; } @RequiresApi(api = Build.VERSION_CODES.O) private static NotificationChannel createChannelPlaying(Context c) { - NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_PLAYING, + NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_PLAYING, c.getString(R.string.notification_channel_playing), NotificationManager.IMPORTANCE_LOW); - mChannel.setDescription(c.getString(R.string.notification_channel_playing_description)); - mChannel.setShowBadge(false); - return mChannel; + notificationChannel.setDescription(c.getString(R.string.notification_channel_playing_description)); + notificationChannel.setShowBadge(false); + return notificationChannel; } @RequiresApi(api = Build.VERSION_CODES.O) private static NotificationChannel createChannelError(Context c) { - NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_ERROR, - c.getString(R.string.notification_channel_error), NotificationManager.IMPORTANCE_HIGH); - mChannel.setDescription(c.getString(R.string.notification_channel_error_description)); - return mChannel; + NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_DOWNLOAD_ERROR, + c.getString(R.string.notification_channel_download_error), NotificationManager.IMPORTANCE_HIGH); + notificationChannel.setDescription(c.getString(R.string.notification_channel_download_error_description)); + notificationChannel.setGroup(GROUP_ID_ERRORS); + + if (!UserPreferences.getShowDownloadReportRaw()) { + // Migration from app managed setting: disable notification + notificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); + } + return notificationChannel; } @RequiresApi(api = Build.VERSION_CODES.O) @@ -72,14 +87,38 @@ public class NotificationUtils { NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_SYNC_ERROR, c.getString(R.string.notification_channel_sync_error), NotificationManager.IMPORTANCE_HIGH); notificationChannel.setDescription(c.getString(R.string.notification_channel_sync_error_description)); + notificationChannel.setGroup(GROUP_ID_ERRORS); + + if (!UserPreferences.getGpodnetNotificationsEnabledRaw()) { + // Migration from app managed setting: disable notification + notificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); + } return notificationChannel; } @RequiresApi(api = Build.VERSION_CODES.O) private static NotificationChannel createChannelAutoDownload(Context c) { - NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_AUTO_DOWNLOAD, - c.getString(R.string.notification_channel_auto_download), NotificationManager.IMPORTANCE_DEFAULT); - mChannel.setDescription(c.getString(R.string.notification_channel_episode_auto_download)); - return mChannel; + NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_AUTO_DOWNLOAD, + c.getString(R.string.notification_channel_auto_download), NotificationManager.IMPORTANCE_NONE); + notificationChannel.setDescription(c.getString(R.string.notification_channel_episode_auto_download)); + notificationChannel.setGroup(GROUP_ID_NEWS); + + if (UserPreferences.getShowAutoDownloadReportRaw()) { + // Migration from app managed setting: enable notification + notificationChannel.setImportance(NotificationManager.IMPORTANCE_DEFAULT); + } + return notificationChannel; + } + + @RequiresApi(api = Build.VERSION_CODES.O) + private static NotificationChannelGroup createGroupErrors(Context c) { + return new NotificationChannelGroup(GROUP_ID_ERRORS, + c.getString(R.string.notification_group_errors)); + } + + @RequiresApi(api = Build.VERSION_CODES.O) + private static NotificationChannelGroup createGroupNews(Context c) { + return new NotificationChannelGroup(GROUP_ID_NEWS, + c.getString(R.string.notification_group_news)); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java index fecb14d25..0467c0a78 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java @@ -1,7 +1,6 @@ package de.danoeh.antennapod.core.util.playback; import android.content.Context; -import android.content.SharedPreferences; import androidx.preference.PreferenceManager; import android.util.Log; import android.view.SurfaceHolder; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java index 425a07f4a..e1b4c967c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java @@ -564,6 +564,13 @@ public class PlaybackController { } } + public void extendSleepTimer(long extendTime) { + long timeLeft = getSleepTimerTimeLeft(); + if (playbackService != null && timeLeft != INVALID_TIME) { + setSleepTimer(timeLeft + extendTime); + } + } + public void setSleepTimer(long time) { if (playbackService != null) { playbackService.setSleepTimer(time); diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java index b12967264..107399e60 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java @@ -2,7 +2,6 @@ package de.danoeh.antennapod.core.util.playback; import android.content.Context; import android.content.Intent; -import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; |