Added circular action buttons
This commit is contained in:
parent
8b6a53fb88
commit
29010b857e
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,17 +3,14 @@ package de.danoeh.antennapod.view;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.Layout;
|
import android.text.Layout;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import androidx.core.view.LayoutInflaterCompat;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.joanzapata.iconify.Iconify;
|
import com.joanzapata.iconify.Iconify;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
|
@ -55,6 +52,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder
|
||||||
public final ImageView isFavorite;
|
public final ImageView isFavorite;
|
||||||
private final ProgressBar progressBar;
|
private final ProgressBar progressBar;
|
||||||
public final ImageButton butSecondary;
|
public final ImageButton butSecondary;
|
||||||
|
private final CircularProgressBar secondaryActionProgress;
|
||||||
private final MainActivity activity;
|
private final MainActivity activity;
|
||||||
private final TextView separatorIcons;
|
private final TextView separatorIcons;
|
||||||
|
|
||||||
|
@ -82,6 +80,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder
|
||||||
isFavorite = itemView.findViewById(R.id.isFavorite);
|
isFavorite = itemView.findViewById(R.id.isFavorite);
|
||||||
size = itemView.findViewById(R.id.size);
|
size = itemView.findViewById(R.id.size);
|
||||||
separatorIcons = itemView.findViewById(R.id.separatorIcons);
|
separatorIcons = itemView.findViewById(R.id.separatorIcons);
|
||||||
|
secondaryActionProgress = itemView.findViewById(R.id.secondaryActionProgress);
|
||||||
itemView.setTag(this);
|
itemView.setTag(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,15 +105,17 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder
|
||||||
isInQueue.setVisibility(item.isTagged(FeedItem.TAG_QUEUE) ? View.VISIBLE : View.GONE);
|
isInQueue.setVisibility(item.isTagged(FeedItem.TAG_QUEUE) ? View.VISIBLE : View.GONE);
|
||||||
itemView.setAlpha(item.isPlayed() /*&& makePlayedItemsTransparent*/ ? 0.5f : 1.0f);
|
itemView.setAlpha(item.isPlayed() /*&& makePlayedItemsTransparent*/ ? 0.5f : 1.0f);
|
||||||
|
|
||||||
if (item.getMedia() != null) {
|
|
||||||
bind(item.getMedia());
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemActionButton actionButton = ItemActionButton.forItem(item, true);
|
ItemActionButton actionButton = ItemActionButton.forItem(item, true);
|
||||||
actionButton.configure(butSecondary, activity);
|
actionButton.configure(butSecondary, activity);
|
||||||
butSecondary.setFocusable(false);
|
butSecondary.setFocusable(false);
|
||||||
butSecondary.setTag(item);
|
butSecondary.setTag(item);
|
||||||
|
|
||||||
|
if (item.getMedia() != null) {
|
||||||
|
bind(item.getMedia());
|
||||||
|
} else {
|
||||||
|
secondaryActionProgress.setPercentage(0, item);
|
||||||
|
}
|
||||||
|
|
||||||
new CoverLoader(activity)
|
new CoverLoader(activity)
|
||||||
.withUri(ImageResourceUtils.getImageLocation(item))
|
.withUri(ImageResourceUtils.getImageLocation(item))
|
||||||
.withFallbackUri(item.getFeed().getImageLocation())
|
.withFallbackUri(item.getFeed().getImageLocation())
|
||||||
|
@ -133,28 +134,27 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder
|
||||||
container.setBackgroundColor(Color.TRANSPARENT);
|
container.setBackgroundColor(Color.TRANSPARENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
final DownloadRequest downloadRequest = DownloadRequester.getInstance().getRequestFor(media);
|
if (DownloadRequester.getInstance().isDownloadingFile(media)) {
|
||||||
progressBar.setVisibility(View.GONE);
|
final DownloadRequest downloadRequest = DownloadRequester.getInstance().getRequestFor(media);
|
||||||
position.setVisibility(View.GONE);
|
float percent = 0.01f * downloadRequest.getProgressPercent();
|
||||||
if (downloadRequest != null) {
|
secondaryActionProgress.setPercentage(Math.max(percent, 0.01f), item);
|
||||||
position.setText(Converter.byteToString(downloadRequest.getSoFar()));
|
} else if (media.isDownloaded()) {
|
||||||
if (downloadRequest.getSize() > 0) {
|
secondaryActionProgress.setPercentage(1, item); // Do not animate 100% -> 0%
|
||||||
duration.setText(Converter.byteToString(downloadRequest.getSize()));
|
} else {
|
||||||
} else {
|
secondaryActionProgress.setPercentage(0, item); // Animate X% -> 0%
|
||||||
duration.setText(Converter.byteToString(media.getSize()));
|
}
|
||||||
}
|
|
||||||
progressBar.setProgress(downloadRequest.getProgressPercent());
|
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);
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
position.setVisibility(View.VISIBLE);
|
position.setVisibility(View.VISIBLE);
|
||||||
} else if (item.getState() == FeedItem.State.PLAYING || item.getState() == FeedItem.State.IN_PROGRESS) {
|
} else {
|
||||||
if (media.getDuration() > 0) {
|
progressBar.setVisibility(View.GONE);
|
||||||
int progress = (int) (100.0 * media.getPosition() / media.getDuration());
|
position.setVisibility(View.GONE);
|
||||||
progressBar.setProgress(progress);
|
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
|
||||||
position.setVisibility(View.VISIBLE);
|
|
||||||
position.setText(Converter.getDurationStringLong(media.getPosition()));
|
|
||||||
duration.setText(Converter.getDurationStringLong(media.getDuration()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (media.getSize() > 0) {
|
if (media.getSize() > 0) {
|
||||||
|
|
|
@ -190,8 +190,6 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include layout="@layout/vertical_list_divider"/>
|
|
||||||
|
|
||||||
<include layout="@layout/secondary_action"/>
|
<include layout="@layout/secondary_action"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -1,12 +1,26 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/butSecondaryAction"
|
android:layout_width="48dp"
|
||||||
android:layout_width="@dimen/listview_secondary_button_width"
|
android:layout_height="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_marginRight="16dp"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:layout_marginEnd="16dp">
|
||||||
android:clickable="false"
|
|
||||||
android:focusable="false"
|
<ImageButton
|
||||||
android:focusableInTouchMode="false"
|
android:id="@+id/butSecondaryAction"
|
||||||
tools:ignore="ContentDescription"
|
android:layout_width="32dp"
|
||||||
tools:src="@sample/secondaryaction" />
|
android:layout_height="32dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
tools:src="@sample/secondaryaction"/>
|
||||||
|
|
||||||
|
<de.danoeh.antennapod.view.CircularProgressBar
|
||||||
|
android:id="@+id/secondaryActionProgress"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_height="40dp"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
Loading…
Reference in New Issue