Merge pull request #777 from mfietz/feature/download-log-retry
Retry failed downloads in the download log
This commit is contained in:
commit
252f80de78
|
@ -2,20 +2,34 @@ package de.danoeh.antennapod.adapter;
|
|||
|
||||
import android.content.Context;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.joanzapata.android.iconify.Iconify;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedImage;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
|
||||
/** Displays a list of DownloadStatus entries. */
|
||||
public class DownloadLogAdapter extends BaseAdapter {
|
||||
|
||||
private final String TAG = "DownloadLogAdapter";
|
||||
|
||||
private Context context;
|
||||
|
||||
private ItemAccess itemAccess;
|
||||
|
@ -35,11 +49,11 @@ public class DownloadLogAdapter extends BaseAdapter {
|
|||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
convertView = inflater.inflate(R.layout.downloadlog_item, parent, false);
|
||||
holder.icon = (TextView) convertView.findViewById(R.id.txtvIcon);
|
||||
holder.retry = (Button) convertView.findViewById(R.id.btnRetry);
|
||||
holder.date = (TextView) convertView.findViewById(R.id.txtvDate);
|
||||
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
|
||||
holder.type = (TextView) convertView.findViewById(R.id.txtvType);
|
||||
holder.date = (TextView) convertView.findViewById(R.id.txtvDate);
|
||||
holder.successful = (TextView) convertView
|
||||
.findViewById(R.id.txtvStatus);
|
||||
holder.reason = (TextView) convertView
|
||||
.findViewById(R.id.txtvReason);
|
||||
convertView.setTag(holder);
|
||||
|
@ -62,33 +76,99 @@ public class DownloadLogAdapter extends BaseAdapter {
|
|||
status.getCompletionDate().getTime(),
|
||||
System.currentTimeMillis(), 0, 0));
|
||||
if (status.isSuccessful()) {
|
||||
holder.successful.setTextColor(convertView.getResources().getColor(
|
||||
holder.icon.setTextColor(convertView.getResources().getColor(
|
||||
R.color.download_success_green));
|
||||
holder.successful.setText(R.string.download_successful);
|
||||
holder.icon.setText("{fa-check-circle}");
|
||||
Iconify.addIcons(holder.icon);
|
||||
holder.retry.setVisibility(View.GONE);
|
||||
holder.reason.setVisibility(View.GONE);
|
||||
} else {
|
||||
holder.successful.setTextColor(convertView.getResources().getColor(
|
||||
holder.icon.setTextColor(convertView.getResources().getColor(
|
||||
R.color.download_failed_red));
|
||||
holder.successful.setText(R.string.download_failed);
|
||||
holder.icon.setText("{fa-times-circle}");
|
||||
Iconify.addIcons(holder.icon);
|
||||
String reasonText = status.getReason().getErrorString(context);
|
||||
if (status.getReasonDetailed() != null) {
|
||||
reasonText += ": " + status.getReasonDetailed();
|
||||
}
|
||||
holder.reason.setText(reasonText);
|
||||
holder.reason.setVisibility(View.VISIBLE);
|
||||
if(status.getFeedfileType() != FeedImage.FEEDFILETYPE_FEEDIMAGE &&
|
||||
!newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
|
||||
holder.retry.setVisibility(View.VISIBLE);
|
||||
holder.retry.setText("{fa-repeat}");
|
||||
Iconify.addIcons(holder.retry);
|
||||
holder.retry.setOnClickListener(clickListener);
|
||||
ButtonHolder btnHolder;
|
||||
if(holder.retry.getTag() != null) {
|
||||
btnHolder = (ButtonHolder) holder.retry.getTag();
|
||||
} else {
|
||||
btnHolder = new ButtonHolder();
|
||||
}
|
||||
btnHolder.typeId = status.getFeedfileType();
|
||||
btnHolder.id = status.getFeedfileId();
|
||||
holder.retry.setTag(btnHolder);
|
||||
} else {
|
||||
holder.retry.setVisibility(View.GONE);
|
||||
holder.retry.setOnClickListener(null);
|
||||
holder.retry.setTag(null);
|
||||
}
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
private final View.OnClickListener clickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ButtonHolder holder = (ButtonHolder) v.getTag();
|
||||
if(holder.typeId == Feed.FEEDFILETYPE_FEED) {
|
||||
Feed feed = DBReader.getFeed(context, holder.id);
|
||||
feed.setLastUpdate(new Date(0)); // force refresh
|
||||
try {
|
||||
DBTasks.refreshFeed(context, feed);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else if(holder.typeId == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||
FeedMedia media = DBReader.getFeedMedia(context, holder.id);
|
||||
try {
|
||||
DBTasks.downloadFeedItems(context, media.getItem());
|
||||
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
Log.wtf(TAG, "Unexpected type id: " + holder.typeId);
|
||||
}
|
||||
v.setVisibility(View.GONE);
|
||||
}
|
||||
};
|
||||
|
||||
private boolean newerWasSuccessful(int position, int feedTypeId, long id) {
|
||||
for (int i = 0; i < position; i++) {
|
||||
DownloadStatus status = getItem(i);
|
||||
if (status.getFeedfileType() == feedTypeId && status.getFeedfileId() == id &&
|
||||
status.isSuccessful()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static class Holder {
|
||||
TextView icon;
|
||||
Button retry;
|
||||
TextView title;
|
||||
TextView type;
|
||||
TextView date;
|
||||
TextView successful;
|
||||
TextView reason;
|
||||
}
|
||||
|
||||
static class ButtonHolder {
|
||||
int typeId;
|
||||
long id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return itemAccess.getCount();
|
||||
|
@ -104,9 +184,9 @@ public class DownloadLogAdapter extends BaseAdapter {
|
|||
return position;
|
||||
}
|
||||
|
||||
public static interface ItemAccess {
|
||||
public int getCount();
|
||||
public DownloadStatus getItem(int position);
|
||||
public interface ItemAccess {
|
||||
int getCount();
|
||||
DownloadStatus getItem(int position);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,78 +1,84 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
tools:background="@android:color/darker_gray">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
<TextView
|
||||
android:id="@+id/txtvIcon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:textSize="48sp"
|
||||
tools:text="[Icon]"
|
||||
android:gravity="center" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnRetry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
|
||||
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
|
||||
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding">
|
||||
android:layout_below="@id/txtvIcon"
|
||||
android:layout_alignLeft="@id/txtvIcon"
|
||||
android:layout_alignRight="@id/txtvIcon"
|
||||
android:layout_marginTop="8dp"
|
||||
tools:text="↻" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvType"
|
||||
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
|
||||
tools:text="Media file"
|
||||
tools:background="@android:color/holo_green_dark" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvTitle"
|
||||
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toLeftOf="@id/txtvType"
|
||||
tools:text="Download item title"
|
||||
tools:background="@android:color/holo_blue_light" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
<TextView
|
||||
android:id="@+id/txtvType"
|
||||
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
|
||||
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
|
||||
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding">
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
tools:text="Media file"
|
||||
tools:background="@android:color/holo_green_dark" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvDate"
|
||||
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginRight="8dp"
|
||||
tools:text="January 23"
|
||||
tools:background="@android:color/holo_green_dark" />
|
||||
<TextView
|
||||
android:id="@+id/txtvTitle"
|
||||
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@id/txtvIcon"
|
||||
android:layout_toLeftOf="@id/txtvType"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:minLines="1"
|
||||
android:maxLines="2"
|
||||
tools:text="Download item title"
|
||||
tools:background="@android:color/holo_blue_light" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvStatus"
|
||||
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
tools:text="successful"
|
||||
tools:background="@android:color/holo_green_dark" />
|
||||
|
||||
</RelativeLayout>
|
||||
<TextView
|
||||
android:id="@+id/txtvDate"
|
||||
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@id/txtvIcon"
|
||||
android:layout_below="@id/txtvTitle"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
tools:text="January 23"
|
||||
tools:background="@android:color/holo_green_dark" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvReason"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
|
||||
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
|
||||
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
|
||||
android:layout_below="@id/txtvDate"
|
||||
android:layout_toRightOf="@id/txtvIcon"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:textColor="?android:attr/textColorTertiary"
|
||||
android:textSize="@dimen/text_size_micro"
|
||||
tools:text="@string/design_time_downloaded_log_failure_reason"
|
||||
tools:background="@android:color/holo_green_dark" />
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
|
@ -939,6 +939,13 @@ public class DownloadService extends Service {
|
|||
|
||||
|
||||
if (successful) {
|
||||
// we create a 'successful' download log if the feed's last refresh failed
|
||||
List<DownloadStatus> log = DBReader.getFeedDownloadLog(DownloadService.this, feed);
|
||||
if(log.size() > 0 && log.get(0).isSuccessful() == false) {
|
||||
saveDownloadStatus(new DownloadStatus(feed,
|
||||
feed.getHumanReadableIdentifier(), DownloadError.SUCCESS, successful,
|
||||
reasonDetailed));
|
||||
}
|
||||
return Pair.create(request, result);
|
||||
} else {
|
||||
numberOfDownloads.decrementAndGet();
|
||||
|
|
|
@ -327,6 +327,21 @@ public final class DBReader {
|
|||
return feed;
|
||||
}
|
||||
|
||||
private static DownloadStatus extractDownloadStatusFromCursorRow(final Cursor cursor) {
|
||||
long id = cursor.getLong(PodDBAdapter.KEY_ID_INDEX);
|
||||
long feedfileId = cursor.getLong(PodDBAdapter.KEY_FEEDFILE_INDEX);
|
||||
int feedfileType = cursor.getInt(PodDBAdapter.KEY_FEEDFILETYPE_INDEX);
|
||||
boolean successful = cursor.getInt(PodDBAdapter.KEY_SUCCESSFUL_INDEX) > 0;
|
||||
int reason = cursor.getInt(PodDBAdapter.KEY_REASON_INDEX);
|
||||
String reasonDetailed = cursor.getString(PodDBAdapter.KEY_REASON_DETAILED_INDEX);
|
||||
String title = cursor.getString(PodDBAdapter.KEY_DOWNLOADSTATUS_TITLE_INDEX);
|
||||
Date completionDate = new Date(cursor.getLong(PodDBAdapter.KEY_COMPLETION_DATE_INDEX));
|
||||
|
||||
return new DownloadStatus(id, title, feedfileId,
|
||||
feedfileType, successful, DownloadError.fromCode(reason), completionDate,
|
||||
reasonDetailed);
|
||||
}
|
||||
|
||||
|
||||
private static FeedItem getMatchingItemForMedia(long itemId,
|
||||
List<FeedItem> items) {
|
||||
|
@ -565,27 +580,7 @@ public final class DBReader {
|
|||
|
||||
if (logCursor.moveToFirst()) {
|
||||
do {
|
||||
long id = logCursor.getLong(PodDBAdapter.KEY_ID_INDEX);
|
||||
|
||||
long feedfileId = logCursor
|
||||
.getLong(PodDBAdapter.KEY_FEEDFILE_INDEX);
|
||||
int feedfileType = logCursor
|
||||
.getInt(PodDBAdapter.KEY_FEEDFILETYPE_INDEX);
|
||||
boolean successful = logCursor
|
||||
.getInt(PodDBAdapter.KEY_SUCCESSFUL_INDEX) > 0;
|
||||
int reason = logCursor.getInt(PodDBAdapter.KEY_REASON_INDEX);
|
||||
String reasonDetailed = logCursor
|
||||
.getString(PodDBAdapter.KEY_REASON_DETAILED_INDEX);
|
||||
String title = logCursor
|
||||
.getString(PodDBAdapter.KEY_DOWNLOADSTATUS_TITLE_INDEX);
|
||||
Date completionDate = new Date(
|
||||
logCursor
|
||||
.getLong(PodDBAdapter.KEY_COMPLETION_DATE_INDEX)
|
||||
);
|
||||
downloadLog.add(new DownloadStatus(id, title, feedfileId,
|
||||
feedfileType, successful, DownloadError.fromCode(reason), completionDate,
|
||||
reasonDetailed));
|
||||
|
||||
downloadLog.add(extractDownloadStatusFromCursorRow(logCursor));
|
||||
} while (logCursor.moveToNext());
|
||||
}
|
||||
logCursor.close();
|
||||
|
@ -593,6 +588,60 @@ public final class DBReader {
|
|||
return downloadLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the download log for a particular feed from the database.
|
||||
*
|
||||
* @param context A context that is used for opening a database connection.
|
||||
* @param feed Feed for which the download log is loaded
|
||||
* @return A list with DownloadStatus objects that represent the feed's download log,
|
||||
* newest events first.
|
||||
*/
|
||||
public static List<DownloadStatus> getFeedDownloadLog(Context context, Feed feed) {
|
||||
Log.d(TAG, "getFeedDownloadLog(CONTEXT, " + feed.toString() + ")");
|
||||
|
||||
PodDBAdapter adapter = new PodDBAdapter(context);
|
||||
adapter.open();
|
||||
Cursor cursor = adapter.getDownloadLog(Feed.FEEDFILETYPE_FEED, feed.getId());
|
||||
List<DownloadStatus> downloadLog = new ArrayList<DownloadStatus>(
|
||||
cursor.getCount());
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
downloadLog.add(extractDownloadStatusFromCursorRow(cursor));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
cursor.close();
|
||||
Collections.sort(downloadLog, new DownloadStatusComparator());
|
||||
return downloadLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the download log for a particular feed media from the database.
|
||||
*
|
||||
* @param context A context that is used for opening a database connection.
|
||||
* @param media Feed media for which the download log is loaded
|
||||
* @return A list with DownloadStatus objects that represent the feed media's download log,
|
||||
* newest events first.
|
||||
*/
|
||||
public static List<DownloadStatus> getFeedMediaDownloadLog(Context context, FeedMedia media) {
|
||||
Log.d(TAG, "getFeedDownloadLog(CONTEXT, " + media.toString() + ")");
|
||||
|
||||
PodDBAdapter adapter = new PodDBAdapter(context);
|
||||
adapter.open();
|
||||
Cursor cursor = adapter.getDownloadLog(FeedMedia.FEEDFILETYPE_FEEDMEDIA, media.getId());
|
||||
List<DownloadStatus> downloadLog = new ArrayList<DownloadStatus>(
|
||||
cursor.getCount());
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
downloadLog.add(extractDownloadStatusFromCursorRow(cursor));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
cursor.close();
|
||||
Collections.sort(downloadLog, new DownloadStatusComparator());
|
||||
return downloadLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the FeedItemStatistics objects of all Feeds in the database. This method should be preferred over
|
||||
* {@link #getFeedItemList(android.content.Context, de.danoeh.antennapod.core.feed.Feed)} if only metadata about
|
||||
|
|
|
@ -977,6 +977,14 @@ public class PodDBAdapter {
|
|||
return c;
|
||||
}
|
||||
|
||||
public final Cursor getDownloadLog(final int feedFileType, final long feedFileId) {
|
||||
final String query = "SELECT * FROM " + TABLE_NAME_DOWNLOAD_LOG +
|
||||
" WHERE " + KEY_FEEDFILE + "=" + feedFileId + " AND " + KEY_FEEDFILETYPE + "=" + feedFileType
|
||||
+ " ORDER BY " + KEY_ID + " DESC";
|
||||
Cursor c = db.rawQuery(query, null);
|
||||
return c;
|
||||
}
|
||||
|
||||
public final Cursor getDownloadLogCursor(final int limit) {
|
||||
Cursor c = db.query(TABLE_NAME_DOWNLOAD_LOG, null, null, null, null,
|
||||
null, KEY_COMPLETION_DATE + " DESC LIMIT " + limit);
|
||||
|
|
Loading…
Reference in New Issue