Merge pull request #3839 from shortspider/NotificationForAutoDownloads

Notification for Auto Downloads
This commit is contained in:
H. Lehmann 2020-03-26 19:22:04 +01:00 committed by GitHub
commit 38c1b559e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 237 additions and 181 deletions

View File

@ -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)

View File

@ -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() {

View File

@ -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();

View File

@ -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();

View File

@ -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());

View File

@ -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();

View File

@ -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;

View File

@ -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());

View File

@ -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);

View File

@ -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"

View File

@ -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();

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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");
}

View File

@ -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) {

View File

@ -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();
}

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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();
}

View File

@ -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);
}
/**

View File

@ -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);
}
/**

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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>