diff --git a/app/src/main/java/de/danoeh/antennapod/view/CircularProgressBar.java b/app/src/main/java/de/danoeh/antennapod/view/CircularProgressBar.java new file mode 100644 index 000000000..4b3c51cfc --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/CircularProgressBar.java @@ -0,0 +1,87 @@ +package de.danoeh.antennapod.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; +import androidx.annotation.Nullable; + +public class CircularProgressBar extends View { + private static final float EPSILON = 0.005f; + + private final Paint paintBackground = new Paint(); + private final Paint paintProgress = new Paint(); + private float percentage = 0; + private float targetPercentage = 0; + private Object tag = null; + + public CircularProgressBar(Context context) { + super(context); + setup(); + } + + public CircularProgressBar(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + setup(); + } + + public CircularProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setup(); + } + + private void setup() { + paintBackground.setAntiAlias(true); + paintBackground.setStyle(Paint.Style.STROKE); + + paintProgress.setAntiAlias(true); + paintProgress.setStyle(Paint.Style.STROKE); + paintProgress.setStrokeCap(Paint.Cap.ROUND); + + int[] colorAttrs = new int[] { android.R.attr.textColorPrimary, android.R.attr.textColorSecondary }; + TypedArray a = getContext().obtainStyledAttributes(colorAttrs); + paintProgress.setColor(a.getColor(0, 0xffffffff)); + paintBackground.setColor(a.getColor(1, 0xffffffff)); + a.recycle(); + } + + /** + * Sets the percentage to be displayed. + * @param percentage Number from 0 to 1 + * @param tag When the tag is the same as last time calling setPercentage, the update is animated + */ + public void setPercentage(float percentage, Object tag) { + targetPercentage = percentage; + + if (tag == null || !tag.equals(this.tag)) { + // Do not animate + this.percentage = percentage; + this.tag = tag; + } + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + float padding = getHeight() * 0.06f; + paintBackground.setStrokeWidth(getHeight() * 0.02f); + paintProgress.setStrokeWidth(padding); + RectF bounds = new RectF(padding, padding, getWidth() - padding, getHeight() - padding); + canvas.drawArc(bounds, 0, 360, false, paintBackground); + + if (percentage > EPSILON && 1 - percentage > EPSILON) { + canvas.drawArc(bounds, -90, percentage * 360, false, paintProgress); + } + + if (Math.abs(percentage - targetPercentage) > EPSILON) { + float delta = Math.min(0.02f, Math.abs(targetPercentage - percentage)); + percentage += delta * ((targetPercentage - percentage) > 0 ? 1f : -1f); + invalidate(); + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemViewHolder.java index 6fb3c9d86..c3a007d5d 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemViewHolder.java +++ b/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemViewHolder.java @@ -3,17 +3,14 @@ package de.danoeh.antennapod.view; import android.graphics.Color; import android.os.Build; import android.text.Layout; -import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; -import androidx.core.view.LayoutInflaterCompat; import androidx.recyclerview.widget.RecyclerView; import com.joanzapata.iconify.Iconify; import de.danoeh.antennapod.R; @@ -55,6 +52,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder public final ImageView isFavorite; private final ProgressBar progressBar; public final ImageButton butSecondary; + private final CircularProgressBar secondaryActionProgress; private final MainActivity activity; private final TextView separatorIcons; @@ -82,6 +80,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder isFavorite = itemView.findViewById(R.id.isFavorite); size = itemView.findViewById(R.id.size); separatorIcons = itemView.findViewById(R.id.separatorIcons); + secondaryActionProgress = itemView.findViewById(R.id.secondaryActionProgress); itemView.setTag(this); } @@ -106,15 +105,17 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder isInQueue.setVisibility(item.isTagged(FeedItem.TAG_QUEUE) ? View.VISIBLE : View.GONE); itemView.setAlpha(item.isPlayed() /*&& makePlayedItemsTransparent*/ ? 0.5f : 1.0f); - if (item.getMedia() != null) { - bind(item.getMedia()); - } - ItemActionButton actionButton = ItemActionButton.forItem(item, true); actionButton.configure(butSecondary, activity); butSecondary.setFocusable(false); butSecondary.setTag(item); + if (item.getMedia() != null) { + bind(item.getMedia()); + } else { + secondaryActionProgress.setPercentage(0, item); + } + new CoverLoader(activity) .withUri(ImageResourceUtils.getImageLocation(item)) .withFallbackUri(item.getFeed().getImageLocation()) @@ -133,28 +134,27 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder container.setBackgroundColor(Color.TRANSPARENT); } - final DownloadRequest downloadRequest = DownloadRequester.getInstance().getRequestFor(media); - progressBar.setVisibility(View.GONE); - position.setVisibility(View.GONE); - if (downloadRequest != null) { - position.setText(Converter.byteToString(downloadRequest.getSoFar())); - if (downloadRequest.getSize() > 0) { - duration.setText(Converter.byteToString(downloadRequest.getSize())); - } else { - duration.setText(Converter.byteToString(media.getSize())); - } - progressBar.setProgress(downloadRequest.getProgressPercent()); + if (DownloadRequester.getInstance().isDownloadingFile(media)) { + final DownloadRequest downloadRequest = DownloadRequester.getInstance().getRequestFor(media); + float percent = 0.01f * downloadRequest.getProgressPercent(); + secondaryActionProgress.setPercentage(Math.max(percent, 0.01f), item); + } else if (media.isDownloaded()) { + secondaryActionProgress.setPercentage(1, item); // Do not animate 100% -> 0% + } else { + secondaryActionProgress.setPercentage(0, item); // Animate X% -> 0% + } + + if (media.getDuration() > 0 + && (item.getState() == FeedItem.State.PLAYING || item.getState() == FeedItem.State.IN_PROGRESS)) { + int progress = (int) (100.0 * media.getPosition() / media.getDuration()); + progressBar.setProgress(progress); + position.setText(Converter.getDurationStringLong(media.getPosition())); + duration.setText(Converter.getDurationStringLong(media.getDuration())); progressBar.setVisibility(View.VISIBLE); position.setVisibility(View.VISIBLE); - } else if (item.getState() == FeedItem.State.PLAYING || item.getState() == FeedItem.State.IN_PROGRESS) { - if (media.getDuration() > 0) { - int progress = (int) (100.0 * media.getPosition() / media.getDuration()); - progressBar.setProgress(progress); - progressBar.setVisibility(View.VISIBLE); - position.setVisibility(View.VISIBLE); - position.setText(Converter.getDurationStringLong(media.getPosition())); - duration.setText(Converter.getDurationStringLong(media.getDuration())); - } + } else { + progressBar.setVisibility(View.GONE); + position.setVisibility(View.GONE); } if (media.getSize() > 0) { diff --git a/app/src/main/res/layout/feeditemlist_item.xml b/app/src/main/res/layout/feeditemlist_item.xml index 09dbc8908..cd983ffca 100644 --- a/app/src/main/res/layout/feeditemlist_item.xml +++ b/app/src/main/res/layout/feeditemlist_item.xml @@ -190,8 +190,6 @@ - - diff --git a/app/src/main/res/layout/secondary_action.xml b/app/src/main/res/layout/secondary_action.xml index 1f4d9e4e6..d083d5181 100644 --- a/app/src/main/res/layout/secondary_action.xml +++ b/app/src/main/res/layout/secondary_action.xml @@ -1,12 +1,26 @@ - + + + + + +