Merge pull request #3839 from shortspider/NotificationForAutoDownloads
Notification for Auto Downloads
This commit is contained in:
commit
38c1b559e3
|
@ -117,8 +117,7 @@ public class DownloadServiceTest {
|
|||
assertFalse("The media in test should not yet been downloaded",
|
||||
DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
|
||||
|
||||
DownloadRequester.getInstance().downloadMedia(false, InstrumentationRegistry.getTargetContext(),
|
||||
testMedia11.getItem());
|
||||
DownloadRequester.getInstance().downloadMedia(false, InstrumentationRegistry.getTargetContext(), true, testMedia11.getItem());
|
||||
Awaitility.await()
|
||||
.atMost(5000, TimeUnit.MILLISECONDS)
|
||||
.until(() -> feedItemEventListener.getEvents().size() >= numEventsExpected);
|
||||
|
@ -163,7 +162,7 @@ public class DownloadServiceTest {
|
|||
}
|
||||
|
||||
withFeedItemEventListener(feedItemEventListener -> {
|
||||
DownloadRequester.getInstance().downloadMedia(false, context, testMedia11.getItem());
|
||||
DownloadRequester.getInstance().downloadMedia(false, context, true, testMedia11.getItem());
|
||||
withDownloadEventListener(downloadEventListener ->
|
||||
Awaitility.await("download is actually running")
|
||||
.atMost(5000, TimeUnit.MILLISECONDS)
|
||||
|
|
|
@ -77,7 +77,7 @@ public class HttpDownloaderTest {
|
|||
|
||||
private Downloader download(String url, String title, boolean expectedResult, boolean deleteExisting, String username, String password, boolean deleteOnFail) {
|
||||
FeedFile feedFile = setupFeedFile(url, title, deleteExisting);
|
||||
DownloadRequest request = new DownloadRequest(feedFile.getFile_url(), url, title, 0, feedFile.getTypeAsInt(), username, password, deleteOnFail, null);
|
||||
DownloadRequest request = new DownloadRequest(feedFile.getFile_url(), url, title, 0, feedFile.getTypeAsInt(), username, password, deleteOnFail, null, false);
|
||||
Downloader downloader = new HttpDownloader(request);
|
||||
downloader.call();
|
||||
DownloadStatus status = downloader.getResult();
|
||||
|
@ -113,7 +113,7 @@ public class HttpDownloaderTest {
|
|||
public void testCancel() {
|
||||
final String url = httpServer.getBaseUrl() + "/delay/3";
|
||||
FeedFileImpl feedFile = setupFeedFile(url, "delay", true);
|
||||
final Downloader downloader = new HttpDownloader(new DownloadRequest(feedFile.getFile_url(), url, "delay", 0, feedFile.getTypeAsInt()));
|
||||
final Downloader downloader = new HttpDownloader(new DownloadRequest(feedFile.getFile_url(), url, "delay", 0, feedFile.getTypeAsInt(), null, null, true, null, false));
|
||||
Thread t = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
|
|
@ -238,7 +238,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||
feed.setFile_url(fileUrl);
|
||||
final DownloadRequest request = new DownloadRequest(feed.getFile_url(),
|
||||
feed.getDownload_url(), "OnlineFeed", 0, Feed.FEEDFILETYPE_FEED, username, password,
|
||||
true, null);
|
||||
true, null, true);
|
||||
|
||||
download = Observable.fromCallable(() -> {
|
||||
feeds = DBReader.getFeedList();
|
||||
|
|
|
@ -101,7 +101,7 @@ public class DownloadLogAdapter extends BaseAdapter {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
DBTasks.forceRefreshFeed(context, feed);
|
||||
DBTasks.forceRefreshFeed(context, feed, true);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ public class DownloadLogAdapter extends BaseAdapter {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadMedia(context, media.getItem());
|
||||
DownloadRequester.getInstance().downloadMedia(context, true, media.getItem());
|
||||
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -71,7 +71,7 @@ public class DownloadActionButton extends ItemActionButton {
|
|||
|
||||
private void downloadEpisode(Context context) {
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadMedia(context, item);
|
||||
DownloadRequester.getInstance().downloadMedia(context, true, item);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
|
||||
|
|
|
@ -48,7 +48,7 @@ class MobileDownloadHelper {
|
|||
private static void downloadFeedItems(Context context, FeedItem item) {
|
||||
allowMobileDownloadTimestamp = System.currentTimeMillis();
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadMedia(context, item);
|
||||
DownloadRequester.getInstance().downloadMedia(context, true, item);
|
||||
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -12,6 +12,7 @@ import de.danoeh.antennapod.core.DownloadServiceCallbacks;
|
|||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.fragment.DownloadsFragment;
|
||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||
|
||||
|
||||
public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks {
|
||||
|
@ -44,6 +45,13 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks {
|
|||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingIntent getAutoDownloadReportNotificationContentIntent(Context context) {
|
||||
Intent intent = new Intent(context, MainActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, QueueFragment.TAG);
|
||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCreateReport() {
|
||||
return true;
|
||||
|
|
|
@ -449,7 +449,7 @@ public class EpisodesApplyActionFragment extends Fragment {
|
|||
}
|
||||
}
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadMedia(getActivity(), toDownload.toArray(new FeedItem[0]));
|
||||
DownloadRequester.getInstance().downloadMedia(getActivity(), true, toDownload.toArray(new FeedItem[0]));
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
|
||||
|
|
|
@ -64,7 +64,7 @@ public class FeedMenuHandler {
|
|||
final Feed selectedFeed) throws DownloadRequestException {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.refresh_item:
|
||||
DBTasks.forceRefreshFeed(context, selectedFeed);
|
||||
DBTasks.forceRefreshFeed(context, selectedFeed, true);
|
||||
break;
|
||||
case R.id.refresh_complete_item:
|
||||
DBTasks.forceRefreshCompleteFeed(context, selectedFeed);
|
||||
|
|
|
@ -35,6 +35,12 @@
|
|||
android:key="prefShowDownloadReport"
|
||||
android:summary="@string/pref_showDownloadReport_sum"
|
||||
android:title="@string/pref_showDownloadReport_title"/>
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:enabled="true"
|
||||
android:key="prefShowAutoDownloadReport"
|
||||
android:summary="@string/pref_showAutoDownloadReport_sum"
|
||||
android:title="@string/pref_showAutoDownloadReport_title"/>
|
||||
<Preference
|
||||
android:key="prefProxy"
|
||||
android:summary="@string/pref_proxy_sum"
|
||||
|
|
|
@ -45,13 +45,13 @@ public class DownloadRequestTest {
|
|||
FeedFile item1 = createFeedItem(1);
|
||||
Bundle arg1 = new Bundle();
|
||||
arg1.putString("arg1", "value1");
|
||||
DownloadRequest request1 = new DownloadRequest.Builder(destStr, item1)
|
||||
DownloadRequest request1 = new DownloadRequest.Builder(destStr, item1, false)
|
||||
.withAuthentication(username1, password1)
|
||||
.withArguments(arg1)
|
||||
.build();
|
||||
|
||||
FeedFile item2 = createFeedItem(2);
|
||||
DownloadRequest request2 = new DownloadRequest.Builder(destStr, item2)
|
||||
DownloadRequest request2 = new DownloadRequest.Builder(destStr, item2, true)
|
||||
.withAuthentication(username2, password2)
|
||||
.build();
|
||||
|
||||
|
|
|
@ -41,10 +41,20 @@ public interface DownloadServiceCallbacks {
|
|||
*/
|
||||
PendingIntent getReportNotificationContentIntent(Context context);
|
||||
|
||||
/**
|
||||
* Returns a PendingIntent for notification that notifies the user about the episodes that have been automatically
|
||||
* downloaded.
|
||||
* <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
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ public class UserPreferences {
|
|||
public static final String PREF_COMPACT_NOTIFICATION_BUTTONS = "prefCompactNotificationButtons";
|
||||
public static final String PREF_LOCKSCREEN_BACKGROUND = "prefLockscreenBackground";
|
||||
private static final String PREF_SHOW_DOWNLOAD_REPORT = "prefShowDownloadReport";
|
||||
private static final String PREF_SHOW_AUTO_DOWNLOAD_REPORT = "prefShowAutoDownloadReport";
|
||||
public static final String PREF_BACK_BUTTON_BEHAVIOR = "prefBackButtonBehavior";
|
||||
private static final String PREF_BACK_BUTTON_GO_TO_PAGE = "prefBackButtonGoToPage";
|
||||
|
||||
|
@ -292,6 +293,10 @@ public class UserPreferences {
|
|||
return prefs.getBoolean(PREF_SHOW_DOWNLOAD_REPORT, true);
|
||||
}
|
||||
|
||||
public static boolean showAutoDownloadReport() {
|
||||
return prefs.getBoolean(PREF_SHOW_AUTO_DOWNLOAD_REPORT, false);
|
||||
}
|
||||
|
||||
public static boolean enqueueDownloadedEpisodes() {
|
||||
return prefs.getBoolean(PREF_ENQUEUE_DOWNLOADED, true);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public class FeedUpdateWorker extends Worker {
|
|||
ClientConfig.initialize(getApplicationContext());
|
||||
|
||||
if (NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed()) {
|
||||
DBTasks.refreshAllFeeds(getApplicationContext());
|
||||
DBTasks.refreshAllFeeds(getApplicationContext(), false);
|
||||
} else {
|
||||
Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
|
||||
}
|
||||
|
|
|
@ -29,59 +29,42 @@ public class DownloadRequest implements Parcelable {
|
|||
private long size;
|
||||
private int statusMsg;
|
||||
private boolean mediaEnqueued;
|
||||
private boolean initiatedByUser;
|
||||
|
||||
public DownloadRequest(@NonNull String destination,
|
||||
@NonNull String source,
|
||||
@NonNull String title,
|
||||
long feedfileId,
|
||||
int feedfileType,
|
||||
String username,
|
||||
String password,
|
||||
boolean deleteOnFailure,
|
||||
Bundle arguments) {
|
||||
public DownloadRequest(@NonNull String destination, @NonNull String source, @NonNull String title, long feedfileId,
|
||||
int feedfileType, String username, String password, boolean deleteOnFailure,
|
||||
Bundle arguments, boolean initiatedByUser) {
|
||||
this(destination, source, title, feedfileId, feedfileType, null, deleteOnFailure, username, password, false,
|
||||
arguments, initiatedByUser);
|
||||
}
|
||||
|
||||
private DownloadRequest(Builder builder) {
|
||||
this(builder.destination, builder.source, builder.title, builder.feedfileId, builder.feedfileType,
|
||||
builder.lastModified, builder.deleteOnFailure, builder.username, builder.password, false,
|
||||
builder.arguments != null ? builder.arguments : new Bundle(), builder.initiatedByUser);
|
||||
}
|
||||
|
||||
private DownloadRequest(Parcel in) {
|
||||
this(in.readString(), in.readString(), in.readString(), in.readLong(), in.readInt(), in.readString(),
|
||||
in.readByte() > 0, nullIfEmpty(in.readString()), nullIfEmpty(in.readString()), in.readByte() > 0,
|
||||
in.readBundle(), in.readByte() > 0);
|
||||
}
|
||||
|
||||
private DownloadRequest(String destination, String source, String title, long feedfileId, int feedfileType,
|
||||
String lastModified, boolean deleteOnFailure, String username, String password,
|
||||
boolean mediaEnqueued, Bundle arguments, boolean initiatedByUser) {
|
||||
this.destination = destination;
|
||||
this.source = source;
|
||||
this.title = title;
|
||||
this.feedfileId = feedfileId;
|
||||
this.feedfileType = feedfileType;
|
||||
this.lastModified = lastModified;
|
||||
this.deleteOnFailure = deleteOnFailure;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.deleteOnFailure = deleteOnFailure;
|
||||
this.mediaEnqueued = false;
|
||||
this.arguments = (arguments != null) ? arguments : new Bundle();
|
||||
}
|
||||
|
||||
public DownloadRequest(String destination, String source, String title,
|
||||
long feedfileId, int feedfileType) {
|
||||
this(destination, source, title, feedfileId, feedfileType, null, null, true, null);
|
||||
}
|
||||
|
||||
private DownloadRequest(Builder builder) {
|
||||
this.destination = builder.destination;
|
||||
this.source = builder.source;
|
||||
this.title = builder.title;
|
||||
this.feedfileId = builder.feedfileId;
|
||||
this.feedfileType = builder.feedfileType;
|
||||
this.username = builder.username;
|
||||
this.password = builder.password;
|
||||
this.lastModified = builder.lastModified;
|
||||
this.deleteOnFailure = builder.deleteOnFailure;
|
||||
this.arguments = (builder.arguments != null) ? builder.arguments : new Bundle();
|
||||
}
|
||||
|
||||
private DownloadRequest(Parcel in) {
|
||||
destination = in.readString();
|
||||
source = in.readString();
|
||||
title = in.readString();
|
||||
feedfileId = in.readLong();
|
||||
feedfileType = in.readInt();
|
||||
lastModified = in.readString();
|
||||
deleteOnFailure = (in.readByte() > 0);
|
||||
username = nullIfEmpty(in.readString());
|
||||
password = nullIfEmpty(in.readString());
|
||||
mediaEnqueued = (in.readByte() > 0);
|
||||
arguments = in.readBundle();
|
||||
this.mediaEnqueued = mediaEnqueued;
|
||||
this.arguments = arguments;
|
||||
this.initiatedByUser = initiatedByUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -107,6 +90,7 @@ public class DownloadRequest implements Parcelable {
|
|||
dest.writeString(nonNullString(password));
|
||||
dest.writeByte((mediaEnqueued) ? (byte) 1 : 0);
|
||||
dest.writeBundle(arguments);
|
||||
dest.writeByte(initiatedByUser ? (byte) 1 : 0);
|
||||
}
|
||||
|
||||
private static String nonNullString(String str) {
|
||||
|
@ -153,6 +137,7 @@ public class DownloadRequest implements Parcelable {
|
|||
if (username != null ? !username.equals(that.username) : that.username != null)
|
||||
return false;
|
||||
if (mediaEnqueued != that.mediaEnqueued) return false;
|
||||
if (initiatedByUser != that.initiatedByUser) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -258,6 +243,10 @@ public class DownloadRequest implements Parcelable {
|
|||
return mediaEnqueued;
|
||||
}
|
||||
|
||||
public boolean isInitiatedByUser() {
|
||||
return initiatedByUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the media is enqueued because of this download.
|
||||
* The state is helpful if the download is cancelled, and undoing the enqueue is needed.
|
||||
|
@ -281,13 +270,15 @@ public class DownloadRequest implements Parcelable {
|
|||
private final long feedfileId;
|
||||
private final int feedfileType;
|
||||
private Bundle arguments;
|
||||
private boolean initiatedByUser;
|
||||
|
||||
public Builder(@NonNull String destination, @NonNull FeedFile item) {
|
||||
public Builder(@NonNull String destination, @NonNull FeedFile item, boolean initiatedByUser) {
|
||||
this.destination = destination;
|
||||
this.source = URLChecker.prepareURL(item.getDownload_url());
|
||||
this.title = item.getHumanReadableIdentifier();
|
||||
this.feedfileId = item.getId();
|
||||
this.feedfileType = item.getTypeAsInt();
|
||||
this.initiatedByUser = initiatedByUser;
|
||||
}
|
||||
|
||||
public Builder deleteOnFailure(boolean deleteOnFailure) {
|
||||
|
|
|
@ -205,9 +205,10 @@ public class DownloadService extends Service {
|
|||
Log.d(TAG, "Service shutting down");
|
||||
isRunning = false;
|
||||
|
||||
boolean showAutoDownloadReport = UserPreferences.showAutoDownloadReport();
|
||||
if (ClientConfig.downloadServiceCallbacks.shouldCreateReport()
|
||||
&& UserPreferences.showDownloadReport()) {
|
||||
notificationManager.updateReport(reportQueue);
|
||||
&& (UserPreferences.showDownloadReport() || showAutoDownloadReport)) {
|
||||
notificationManager.updateReport(reportQueue, showAutoDownloadReport);
|
||||
reportQueue.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.service.download;
|
|||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
@ -19,6 +20,7 @@ import java.util.List;
|
|||
public class DownloadServiceNotification {
|
||||
private static final String TAG = "DownloadSvcNotification";
|
||||
private static final int REPORT_ID = 3;
|
||||
private static final int AUTO_REPORT_ID = 4;
|
||||
|
||||
private final Context context;
|
||||
private NotificationCompat.Builder notificationCompatBuilder;
|
||||
|
@ -91,12 +93,26 @@ public class DownloadServiceNotification {
|
|||
return TextUtils.join("\n", lines);
|
||||
}
|
||||
|
||||
private static String createAutoDownloadNotificationContent(List<DownloadStatus> statuses) {
|
||||
int length = statuses.size();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append("• ").append(statuses.get(i).getTitle());
|
||||
if (i != length - 1) {
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a notification at the end of the service lifecycle to notify the
|
||||
* user about the number of completed downloads. A report will only be
|
||||
* created if there is at least one failed download excluding images
|
||||
*/
|
||||
public void updateReport(List<DownloadStatus> reportQueue) {
|
||||
public void updateReport(List<DownloadStatus> reportQueue, boolean showAutoDownloadReport) {
|
||||
// check if report should be created
|
||||
boolean createReport = false;
|
||||
int successfulDownloads = 0;
|
||||
|
@ -107,34 +123,53 @@ public class DownloadServiceNotification {
|
|||
for (DownloadStatus status : reportQueue) {
|
||||
if (status.isSuccessful()) {
|
||||
successfulDownloads++;
|
||||
createReport |= showAutoDownloadReport && !status.isInitiatedByUser() && status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA;
|
||||
} else if (!status.isCancelled()) {
|
||||
createReport = true;
|
||||
failedDownloads++;
|
||||
createReport = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (createReport) {
|
||||
Log.d(TAG, "Creating report");
|
||||
|
||||
// create notification object
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context,
|
||||
NotificationUtils.CHANNEL_ID_ERROR)
|
||||
.setTicker(context.getString(R.string.download_report_title))
|
||||
.setContentTitle(context.getString(R.string.download_report_content_title))
|
||||
.setContentText(
|
||||
String.format(
|
||||
context.getString(R.string.download_report_content),
|
||||
successfulDownloads, failedDownloads)
|
||||
)
|
||||
.setSmallIcon(R.drawable.stat_notify_sync_error)
|
||||
.setContentIntent(
|
||||
ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(context)
|
||||
)
|
||||
.setAutoCancel(true);
|
||||
String channelId;
|
||||
int titleId;
|
||||
int iconId;
|
||||
int id;
|
||||
String content;
|
||||
PendingIntent intent;
|
||||
if (failedDownloads == 0) {
|
||||
// We are generating an auto-download report
|
||||
channelId = NotificationUtils.CHANNEL_ID_AUTO_DOWNLOAD;
|
||||
titleId = R.string.auto_download_report_title;
|
||||
iconId = R.drawable.auto_download_complete;
|
||||
intent = ClientConfig.downloadServiceCallbacks.getAutoDownloadReportNotificationContentIntent(context);
|
||||
id = AUTO_REPORT_ID;
|
||||
content = createAutoDownloadNotificationContent(reportQueue);
|
||||
} else {
|
||||
channelId = NotificationUtils.CHANNEL_ID_ERROR;
|
||||
titleId = R.string.download_report_title;
|
||||
iconId = R.drawable.stat_notify_sync_error;
|
||||
intent = ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(context);
|
||||
id = REPORT_ID;
|
||||
content = String.format(context.getString(R.string.download_report_content), successfulDownloads, failedDownloads);
|
||||
}
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId);
|
||||
builder.setTicker(context.getString(titleId))
|
||||
.setContentTitle(context.getString(titleId))
|
||||
.setContentText(content)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(content))
|
||||
.setSmallIcon(iconId)
|
||||
.setContentIntent(intent)
|
||||
.setAutoCancel(true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
||||
}
|
||||
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.notify(REPORT_ID, builder.build());
|
||||
nm.notify(id, builder.build());
|
||||
} else {
|
||||
Log.d(TAG, "No report is created");
|
||||
}
|
||||
|
|
|
@ -41,62 +41,23 @@ public class DownloadStatus {
|
|||
* FEEDFILETYPE_FEEDIMAGE or FEEDFILETYPE_FEEDMEDIA
|
||||
*/
|
||||
private final int feedfileType;
|
||||
private final boolean initiatedByUser;
|
||||
|
||||
// ------------------------------------ NOT STORED IN DB
|
||||
private boolean done;
|
||||
private boolean cancelled;
|
||||
|
||||
/** Constructor for restoring Download status entries from DB. */
|
||||
private DownloadStatus(long id, String title, long feedfileId,
|
||||
int feedfileType, boolean successful, DownloadError reason,
|
||||
Date completionDate, String reasonDetailed) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.done = true;
|
||||
this.feedfileId = feedfileId;
|
||||
this.reason = reason;
|
||||
this.successful = successful;
|
||||
this.completionDate = (Date) completionDate.clone();
|
||||
this.reasonDetailed = reasonDetailed;
|
||||
this.feedfileType = feedfileType;
|
||||
}
|
||||
|
||||
public DownloadStatus(@NonNull DownloadRequest request, DownloadError reason,
|
||||
boolean successful, boolean cancelled, String reasonDetailed) {
|
||||
this.title = request.getTitle();
|
||||
this.feedfileId = request.getFeedfileId();
|
||||
this.feedfileType = request.getFeedfileType();
|
||||
this.reason = reason;
|
||||
this.successful = successful;
|
||||
this.cancelled = cancelled;
|
||||
this.reasonDetailed = reasonDetailed;
|
||||
this.completionDate = new Date();
|
||||
public DownloadStatus(@NonNull DownloadRequest request, DownloadError reason, boolean successful, boolean cancelled,
|
||||
String reasonDetailed) {
|
||||
this(0, request.getTitle(), request.getFeedfileId(), request.getFeedfileType(), successful, cancelled, false,
|
||||
reason, new Date(), reasonDetailed, request.isInitiatedByUser());
|
||||
}
|
||||
|
||||
/** Constructor for creating new completed downloads. */
|
||||
public DownloadStatus(@NonNull FeedFile feedfile, String title, DownloadError reason,
|
||||
boolean successful, String reasonDetailed) {
|
||||
this.title = title;
|
||||
this.done = true;
|
||||
this.feedfileId = feedfile.getId();
|
||||
this.feedfileType = feedfile.getTypeAsInt();
|
||||
this.reason = reason;
|
||||
this.successful = successful;
|
||||
this.completionDate = new Date();
|
||||
this.reasonDetailed = reasonDetailed;
|
||||
}
|
||||
|
||||
/** Constructor for creating new completed downloads. */
|
||||
public DownloadStatus(long feedfileId, int feedfileType, String title,
|
||||
DownloadError reason, boolean successful, String reasonDetailed) {
|
||||
this.title = title;
|
||||
this.done = true;
|
||||
this.feedfileId = feedfileId;
|
||||
this.feedfileType = feedfileType;
|
||||
this.reason = reason;
|
||||
this.successful = successful;
|
||||
this.completionDate = new Date();
|
||||
this.reasonDetailed = reasonDetailed;
|
||||
public DownloadStatus(@NonNull FeedFile feedfile, String title, DownloadError reason, boolean successful,
|
||||
String reasonDetailed, boolean initiatedByUser) {
|
||||
this(0, title, feedfile.getId(), feedfile.getTypeAsInt(), successful, false, true, reason, new Date(),
|
||||
reasonDetailed, initiatedByUser);
|
||||
}
|
||||
|
||||
public static DownloadStatus fromCursor(Cursor cursor) {
|
||||
|
@ -109,18 +70,27 @@ public class DownloadStatus {
|
|||
int indexCompletionDate = cursor.getColumnIndex(PodDBAdapter.KEY_COMPLETION_DATE);
|
||||
int indexReasonDetailed = cursor.getColumnIndex(PodDBAdapter.KEY_REASON_DETAILED);
|
||||
|
||||
long id = cursor.getLong(indexId);
|
||||
String title = cursor.getString(indexTitle);
|
||||
long feedfileId = cursor.getLong(indexFeedFile);
|
||||
int feedfileType = cursor.getInt(indexFileFileType);
|
||||
boolean successful = cursor.getInt(indexSuccessful) > 0;
|
||||
int reason = cursor.getInt(indexReason);
|
||||
Date completionDate = new Date(cursor.getLong(indexCompletionDate));
|
||||
String reasonDetailed = cursor.getString(indexReasonDetailed);
|
||||
return new DownloadStatus(cursor.getLong(indexId), cursor.getString(indexTitle), cursor.getLong(indexFeedFile),
|
||||
cursor.getInt(indexFileFileType), cursor.getInt(indexSuccessful) > 0, false, true,
|
||||
DownloadError.fromCode(cursor.getInt(indexReason)),
|
||||
new Date(cursor.getLong(indexCompletionDate)),
|
||||
cursor.getString(indexReasonDetailed), false);
|
||||
}
|
||||
|
||||
return new DownloadStatus(id, title, feedfileId,
|
||||
feedfileType, successful, DownloadError.fromCode(reason), completionDate,
|
||||
reasonDetailed);
|
||||
private DownloadStatus(long id, String title, long feedfileId, int feedfileType, boolean successful,
|
||||
boolean cancelled, boolean done, DownloadError reason, Date completionDate,
|
||||
String reasonDetailed, boolean initiatedByUser) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.feedfileId = feedfileId;
|
||||
this.reason = reason;
|
||||
this.successful = successful;
|
||||
this.cancelled = cancelled;
|
||||
this.done = done;
|
||||
this.completionDate = (Date) completionDate.clone();
|
||||
this.reasonDetailed = reasonDetailed;
|
||||
this.feedfileType = feedfileType;
|
||||
this.initiatedByUser = initiatedByUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -165,6 +135,8 @@ public class DownloadStatus {
|
|||
return feedfileType;
|
||||
}
|
||||
|
||||
public boolean isInitiatedByUser() { return initiatedByUser; }
|
||||
|
||||
public boolean isDone() {
|
||||
return done;
|
||||
}
|
||||
|
|
|
@ -78,12 +78,12 @@ public class FeedParserTask implements Callable<FeedHandlerResult> {
|
|||
}
|
||||
|
||||
if (successful) {
|
||||
downloadStatus = new DownloadStatus(feed, feed.getHumanReadableIdentifier(),
|
||||
DownloadError.SUCCESS, successful, reasonDetailed);
|
||||
downloadStatus = new DownloadStatus(feed, feed.getHumanReadableIdentifier(), DownloadError.SUCCESS,
|
||||
successful, reasonDetailed, request.isInitiatedByUser());
|
||||
return result;
|
||||
} else {
|
||||
downloadStatus = new DownloadStatus(feed, request.getTitle(),
|
||||
reason, successful, reasonDetailed);
|
||||
downloadStatus = new DownloadStatus(feed, feed.getTitle(), reason, successful,
|
||||
reasonDetailed, request.isInitiatedByUser());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ public class MediaDownloadedHandler implements Runnable {
|
|||
} catch (ExecutionException e) {
|
||||
Log.e(TAG, "ExecutionException in MediaHandlerThread: " + e.getMessage());
|
||||
updatedStatus = new DownloadStatus(media, media.getEpisodeTitle(),
|
||||
DownloadError.ERROR_DB_ACCESS_ERROR, false, e.getMessage());
|
||||
DownloadError.ERROR_DB_ACCESS_ERROR, false, e.getMessage(), request.isInitiatedByUser());
|
||||
}
|
||||
|
||||
if (GpodnetPreferences.loggedIn() && item != null) {
|
||||
|
|
|
@ -92,7 +92,7 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
|
|||
Log.d(TAG, "Enqueueing " + itemsToDownload.length + " items for download");
|
||||
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadMedia(false, context, itemsToDownload);
|
||||
DownloadRequester.getInstance().downloadMedia(false, context, false, itemsToDownload);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
|
@ -105,8 +105,9 @@ public final class DBTasks {
|
|||
* enqueuing Feeds for download from a previous call
|
||||
*
|
||||
* @param context Might be used for accessing the database
|
||||
* @param initiatedByUser a boolean indicating if the refresh was triggered by user action.
|
||||
*/
|
||||
public static void refreshAllFeeds(final Context context) {
|
||||
public static void refreshAllFeeds(final Context context, boolean initiatedByUser) {
|
||||
if (!isRefreshing.compareAndSet(false, true)) {
|
||||
Log.d(TAG, "Ignoring request to refresh all feeds: Refresh lock is locked");
|
||||
return;
|
||||
|
@ -116,7 +117,7 @@ public final class DBTasks {
|
|||
throw new IllegalStateException("DBTasks.refreshAllFeeds() must not be called from the main thread.");
|
||||
}
|
||||
|
||||
refreshFeeds(context, DBReader.getFeedList());
|
||||
refreshFeeds(context, DBReader.getFeedList(), initiatedByUser);
|
||||
isRefreshing.set(false);
|
||||
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
|
||||
|
@ -134,9 +135,11 @@ public final class DBTasks {
|
|||
/**
|
||||
* @param context
|
||||
* @param feedList the list of feeds to refresh
|
||||
* @param initiatedByUser a boolean indicating if the refresh was triggered by user action.
|
||||
*/
|
||||
private static void refreshFeeds(final Context context,
|
||||
final List<Feed> feedList) {
|
||||
final List<Feed> feedList,
|
||||
boolean initiatedByUser) {
|
||||
|
||||
for (Feed feed : feedList) {
|
||||
FeedPreferences prefs = feed.getPreferences();
|
||||
|
@ -148,11 +151,12 @@ public final class DBTasks {
|
|||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DBWriter.addDownloadStatus(
|
||||
new DownloadStatus(feed, feed
|
||||
.getHumanReadableIdentifier(),
|
||||
DownloadError.ERROR_REQUEST_ERROR, false, e
|
||||
.getMessage()
|
||||
)
|
||||
new DownloadStatus(feed,
|
||||
feed.getHumanReadableIdentifier(),
|
||||
DownloadError.ERROR_REQUEST_ERROR,
|
||||
false,
|
||||
e.getMessage(),
|
||||
initiatedByUser)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -168,15 +172,16 @@ public final class DBTasks {
|
|||
*/
|
||||
public static void forceRefreshCompleteFeed(final Context context, final Feed feed) {
|
||||
try {
|
||||
refreshFeed(context, feed, true, true);
|
||||
refreshFeed(context, feed, true, true, false);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DBWriter.addDownloadStatus(
|
||||
new DownloadStatus(feed, feed
|
||||
.getHumanReadableIdentifier(),
|
||||
DownloadError.ERROR_REQUEST_ERROR, false, e
|
||||
.getMessage()
|
||||
)
|
||||
new DownloadStatus(feed,
|
||||
feed.getHumanReadableIdentifier(),
|
||||
DownloadError.ERROR_REQUEST_ERROR,
|
||||
false,
|
||||
e.getMessage(),
|
||||
false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -196,7 +201,7 @@ public final class DBTasks {
|
|||
nextFeed.setPageNr(pageNr);
|
||||
nextFeed.setPaged(true);
|
||||
nextFeed.setId(feed.getId());
|
||||
DownloadRequester.getInstance().downloadFeed(context, nextFeed, loadAllPages, false);
|
||||
DownloadRequester.getInstance().downloadFeed(context, nextFeed, loadAllPages, false, true);
|
||||
} else {
|
||||
Log.e(TAG, "loadNextPageOfFeed: Feed was either not paged or contained no nextPageLink");
|
||||
}
|
||||
|
@ -212,7 +217,7 @@ public final class DBTasks {
|
|||
private static void refreshFeed(Context context, Feed feed)
|
||||
throws DownloadRequestException {
|
||||
Log.d(TAG, "refreshFeed(feed.id: " + feed.getId() +")");
|
||||
refreshFeed(context, feed, false, false);
|
||||
refreshFeed(context, feed, false, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -221,13 +226,13 @@ public final class DBTasks {
|
|||
* @param context Used for requesting the download.
|
||||
* @param feed The Feed object.
|
||||
*/
|
||||
public static void forceRefreshFeed(Context context, Feed feed)
|
||||
public static void forceRefreshFeed(Context context, Feed feed, boolean initiatedByUser)
|
||||
throws DownloadRequestException {
|
||||
Log.d(TAG, "refreshFeed(feed.id: " + feed.getId() +")");
|
||||
refreshFeed(context, feed, false, true);
|
||||
refreshFeed(context, feed, false, true, initiatedByUser);
|
||||
}
|
||||
|
||||
private static void refreshFeed(Context context, Feed feed, boolean loadAllPages, boolean force)
|
||||
private static void refreshFeed(Context context, Feed feed, boolean loadAllPages, boolean force, boolean initiatedByUser)
|
||||
throws DownloadRequestException {
|
||||
Feed f;
|
||||
String lastUpdate = feed.hasLastUpdateFailed() ? null : feed.getLastUpdate();
|
||||
|
@ -238,7 +243,7 @@ public final class DBTasks {
|
|||
feed.getPreferences().getUsername(), feed.getPreferences().getPassword());
|
||||
}
|
||||
f.setId(feed.getId());
|
||||
DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages, force);
|
||||
DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages, force, initiatedByUser);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -113,9 +113,9 @@ public class DownloadRequester implements DownloadStateProvider {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private DownloadRequest createRequest(FeedFile item, FeedFile container, File dest,
|
||||
boolean overwriteIfExists, String username, String password,
|
||||
String lastModified, boolean deleteOnFailure, Bundle arguments) {
|
||||
private DownloadRequest createRequest(FeedFile item, FeedFile container, File dest, boolean overwriteIfExists,
|
||||
String username, String password, String lastModified,
|
||||
boolean deleteOnFailure, Bundle arguments, boolean initiatedByUser) {
|
||||
final boolean partiallyDownloadedFileExists = item.getFile_url() != null && new File(item.getFile_url()).exists();
|
||||
|
||||
Log.d(TAG, "partiallyDownloadedFileExists: " + partiallyDownloadedFileExists);
|
||||
|
@ -156,7 +156,7 @@ public class DownloadRequester implements DownloadStateProvider {
|
|||
String baseUrl = (container != null) ? container.getDownload_url() : null;
|
||||
item.setDownload_url(URLChecker.prepareURL(item.getDownload_url(), baseUrl));
|
||||
|
||||
DownloadRequest.Builder builder = new DownloadRequest.Builder(dest.toString(), item)
|
||||
DownloadRequest.Builder builder = new DownloadRequest.Builder(dest.toString(), item, initiatedByUser)
|
||||
.withAuthentication(username, password)
|
||||
.lastModified(lastModified)
|
||||
.deleteOnFailure(deleteOnFailure)
|
||||
|
@ -191,7 +191,7 @@ public class DownloadRequester implements DownloadStateProvider {
|
|||
* @param loadAllPages Set to true to download all pages
|
||||
*/
|
||||
public synchronized void downloadFeed(Context context, Feed feed, boolean loadAllPages,
|
||||
boolean force)
|
||||
boolean force, boolean initiatedByUser)
|
||||
throws DownloadRequestException {
|
||||
if (feedFileValid(feed)) {
|
||||
String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null;
|
||||
|
@ -203,7 +203,8 @@ public class DownloadRequester implements DownloadStateProvider {
|
|||
args.putBoolean(REQUEST_ARG_LOAD_ALL_PAGES, loadAllPages);
|
||||
|
||||
DownloadRequest request = createRequest(feed, null, new File(getFeedfilePath(), getFeedfileName(feed)),
|
||||
true, username, password, lastModified, true, args);
|
||||
true, username, password, lastModified, true, args, initiatedByUser
|
||||
);
|
||||
if (request != null) {
|
||||
download(context, request);
|
||||
}
|
||||
|
@ -211,18 +212,18 @@ public class DownloadRequester implements DownloadStateProvider {
|
|||
}
|
||||
|
||||
public synchronized void downloadFeed(Context context, Feed feed) throws DownloadRequestException {
|
||||
downloadFeed(context, feed, false, false);
|
||||
downloadFeed(context, feed, false, false, true);
|
||||
}
|
||||
|
||||
public synchronized void downloadMedia(@NonNull Context context, FeedItem... feedItems)
|
||||
public synchronized void downloadMedia(@NonNull Context context, boolean initiatedByUser, FeedItem... feedItems)
|
||||
throws DownloadRequestException {
|
||||
downloadMedia(true, context, feedItems);
|
||||
downloadMedia(true, context, initiatedByUser, feedItems);
|
||||
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||
public synchronized void downloadMedia(boolean performAutoCleanup, @NonNull Context context,
|
||||
FeedItem... items)
|
||||
boolean initiatedByUser, FeedItem... items)
|
||||
throws DownloadRequestException {
|
||||
Log.d(TAG, "downloadMedia() called with: performAutoCleanup = [" + performAutoCleanup
|
||||
+ "], #items = [" + items.length + "]");
|
||||
|
@ -230,7 +231,7 @@ public class DownloadRequester implements DownloadStateProvider {
|
|||
List<DownloadRequest> requests = new ArrayList<>(items.length);
|
||||
for (FeedItem item : items) {
|
||||
try {
|
||||
DownloadRequest request = createRequest(item.getMedia());
|
||||
DownloadRequest request = createRequest(item.getMedia(), initiatedByUser);
|
||||
if (request != null) {
|
||||
requests.add(request);
|
||||
}
|
||||
|
@ -246,7 +247,7 @@ public class DownloadRequester implements DownloadStateProvider {
|
|||
.getMedia()
|
||||
.getHumanReadableIdentifier(),
|
||||
DownloadError.ERROR_REQUEST_ERROR,
|
||||
false, e.getMessage()
|
||||
false, e.getMessage(), initiatedByUser
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -256,7 +257,7 @@ public class DownloadRequester implements DownloadStateProvider {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private DownloadRequest createRequest(@Nullable FeedMedia feedmedia)
|
||||
private DownloadRequest createRequest(@Nullable FeedMedia feedmedia, boolean initiatedByUser)
|
||||
throws DownloadRequestException {
|
||||
if (!feedFileValid(feedmedia)) {
|
||||
return null;
|
||||
|
@ -278,8 +279,7 @@ public class DownloadRequester implements DownloadStateProvider {
|
|||
} else {
|
||||
dest = new File(getMediafilePath(feedmedia), getMediafilename(feedmedia));
|
||||
}
|
||||
return createRequest(feedmedia, feed,
|
||||
dest, false, username, password, null, false, null);
|
||||
return createRequest(feedmedia, feed, dest, false, username, password, null, false, null, initiatedByUser);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -115,7 +115,7 @@ public class AutoUpdateManager {
|
|||
public static void runImmediate(@NonNull Context context) {
|
||||
Log.d(TAG, "Run auto update immediately in background.");
|
||||
new Thread(() -> {
|
||||
DBTasks.refreshAllFeeds(context.getApplicationContext());
|
||||
DBTasks.refreshAllFeeds(context.getApplicationContext(), true);
|
||||
}, "ManualRefreshAllFeeds").start();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ public class NotificationUtils {
|
|||
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_AUTO_DOWNLOAD = "auto_download";
|
||||
|
||||
public static void createChannels(Context context) {
|
||||
if (android.os.Build.VERSION.SDK_INT < 26) {
|
||||
|
@ -26,6 +27,7 @@ public class NotificationUtils {
|
|||
mNotificationManager.createNotificationChannel(createChannelDownloading(context));
|
||||
mNotificationManager.createNotificationChannel(createChannelPlaying(context));
|
||||
mNotificationManager.createNotificationChannel(createChannelError(context));
|
||||
mNotificationManager.createNotificationChannel(createChannelAutoDownload(context));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,4 +64,12 @@ public class NotificationUtils {
|
|||
mChannel.setDescription(c.getString(R.string.notification_channel_error_description));
|
||||
return mChannel;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM16.59,7.58L10,14.17l-2.59,-2.58L6,13l4,4 8,-8z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
|
@ -237,6 +237,7 @@
|
|||
<string name="download_canceled_msg">Download canceled</string>
|
||||
<string name="download_canceled_autodownload_enabled_msg">Download canceled\nDisabled <i>Auto Download</i> for this item</string>
|
||||
<string name="download_report_title">Downloads completed with error(s)</string>
|
||||
<string name="auto_download_report_title">Auto-downloads completed</string>
|
||||
<string name="download_report_content_title">Download Report</string>
|
||||
<string name="download_error_malformed_url">Malformed URL</string>
|
||||
<string name="download_error_io_error">IO Error</string>
|
||||
|
@ -470,6 +471,8 @@
|
|||
<string name="pref_lockscreen_background_sum">Set the lockscreen background to the current episode\'s image. As a side effect, this will also show the image in third party apps.</string>
|
||||
<string name="pref_showDownloadReport_title">Show Download Report</string>
|
||||
<string name="pref_showDownloadReport_sum">If downloads fail, generate a report that shows the details of the failure.</string>
|
||||
<string name="pref_showAutoDownloadReport_title">Show Auto Download Report</string>
|
||||
<string name="pref_showAutoDownloadReport_sum">Show a notification for automatically downloaded episodes.</string>
|
||||
<string name="pref_expand_notify_unsupport_toast">Android versions before 4.1 do not support expanded notifications.</string>
|
||||
<string name="pref_enqueue_location_title">Enqueue Location</string>
|
||||
<string name="pref_enqueue_location_sum">Add episodes to: %1$s</string>
|
||||
|
@ -781,6 +784,8 @@
|
|||
<string name="notification_channel_playing_description">Allows to control playback. This is the main notification you see while playing a podcast.</string>
|
||||
<string name="notification_channel_error">Errors</string>
|
||||
<string name="notification_channel_error_description">Shown if something went wrong, for example if download or gpodder sync fails.</string>
|
||||
<string name="notification_channel_auto_download">Auto Downloads</string>
|
||||
<string name="notification_channel_episode_auto_download">Shown when episodes have been automatically downloaded.</string>
|
||||
|
||||
<!-- Widget settings -->
|
||||
<string name="widget_settings">Widget settings</string>
|
||||
|
|
Loading…
Reference in New Issue