Chapter dividers for the progress bar (#4915)
This commit is contained in:
parent
d444e8d23e
commit
a476ce2f47
|
@ -29,6 +29,7 @@ import de.danoeh.antennapod.activity.CastEnabledActivity;
|
|||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.core.event.FavoritesEvent;
|
||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||
import de.danoeh.antennapod.core.feed.Chapter;
|
||||
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
|
@ -46,6 +47,7 @@ import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
|
|||
import de.danoeh.antennapod.dialog.SleepTimerDialog;
|
||||
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
|
||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||
import de.danoeh.antennapod.view.ChapterSeekBar;
|
||||
import de.danoeh.antennapod.ui.common.PlaybackSpeedIndicatorView;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
|
@ -63,7 +65,7 @@ import java.util.List;
|
|||
* Shows the audio player.
|
||||
*/
|
||||
public class AudioPlayerFragment extends Fragment implements
|
||||
SeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
|
||||
ChapterSeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
|
||||
public static final String TAG = "AudioPlayerFragment";
|
||||
private static final int POS_COVER = 0;
|
||||
private static final int POS_DESCR = 1;
|
||||
|
@ -77,7 +79,7 @@ public class AudioPlayerFragment extends Fragment implements
|
|||
private ViewPager2 pager;
|
||||
private TextView txtvPosition;
|
||||
private TextView txtvLength;
|
||||
private SeekBar sbPosition;
|
||||
private ChapterSeekBar sbPosition;
|
||||
private ImageButton butRev;
|
||||
private TextView txtvRev;
|
||||
private ImageButton butPlay;
|
||||
|
@ -172,12 +174,33 @@ public class AudioPlayerFragment extends Fragment implements
|
|||
return root;
|
||||
}
|
||||
|
||||
public void setHasChapters(boolean hasChapters) {
|
||||
private void setHasChapters(boolean hasChapters) {
|
||||
this.hasChapters = hasChapters;
|
||||
tabLayoutMediator.detach();
|
||||
tabLayoutMediator.attach();
|
||||
}
|
||||
|
||||
private void setChapterDividers(Playable media) {
|
||||
|
||||
if (media == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float[] dividerPos = null;
|
||||
|
||||
if (hasChapters) {
|
||||
List<Chapter> chapters = media.getChapters();
|
||||
dividerPos = new float[chapters.size()];
|
||||
float duration = media.getDuration();
|
||||
|
||||
for (int i = 0; i < chapters.size(); i++) {
|
||||
dividerPos[i] = chapters.get(i).getStart() / duration;
|
||||
}
|
||||
}
|
||||
|
||||
sbPosition.setDividerPos(dividerPos);
|
||||
}
|
||||
|
||||
public View getExternalPlayerHolder() {
|
||||
return getView().findViewById(R.id.playerFragment);
|
||||
}
|
||||
|
@ -298,16 +321,17 @@ public class AudioPlayerFragment extends Fragment implements
|
|||
disposable = Maybe.create(emitter -> {
|
||||
Playable media = controller.getMedia();
|
||||
if (media != null) {
|
||||
media.loadChapterMarks(getContext());
|
||||
emitter.onSuccess(media);
|
||||
} else {
|
||||
emitter.onComplete();
|
||||
}
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(media -> updateUi((Playable) media),
|
||||
error -> Log.e(TAG, Log.getStackTraceString(error)),
|
||||
() -> updateUi(null));
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(media -> updateUi((Playable) media),
|
||||
error -> Log.e(TAG, Log.getStackTraceString(error)),
|
||||
() -> updateUi(null));
|
||||
}
|
||||
|
||||
private PlaybackController newPlaybackController() {
|
||||
|
@ -389,8 +413,15 @@ public class AudioPlayerFragment extends Fragment implements
|
|||
if (controller == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (media != null && media.getChapters() != null) {
|
||||
setHasChapters(media.getChapters().size() > 0);
|
||||
} else {
|
||||
setHasChapters(false);
|
||||
}
|
||||
updatePosition(new PlaybackPositionEvent(controller.getPosition(), controller.getDuration()));
|
||||
updatePlaybackSpeedButton(media);
|
||||
setChapterDividers(media);
|
||||
setupOptionsMenu(media);
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,6 @@ public class ChaptersFragment extends Fragment {
|
|||
return;
|
||||
}
|
||||
adapter.setMedia(media);
|
||||
((AudioPlayerFragment) getParentFragment()).setHasChapters(adapter.getItemCount() > 0);
|
||||
int positionOfCurrentChapter = getCurrentChapter(media);
|
||||
updateChapterSelection(positionOfCurrentChapter);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
package de.danoeh.antennapod.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||
|
||||
public class ChapterSeekBar extends androidx.appcompat.widget.AppCompatSeekBar {
|
||||
|
||||
private float top;
|
||||
private float width;
|
||||
private float bottom;
|
||||
private float density;
|
||||
private float progressPrimary;
|
||||
private float progressSecondary;
|
||||
private float[] dividerPos;
|
||||
private final Paint paintBackground = new Paint();
|
||||
private final Paint paintProgressPrimary = new Paint();
|
||||
private final Paint paintProgressSecondary = new Paint();
|
||||
|
||||
public ChapterSeekBar(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public ChapterSeekBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public ChapterSeekBar(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
setBackground(null); // Removes the thumb shadow
|
||||
dividerPos = null;
|
||||
density = context.getResources().getDisplayMetrics().density;
|
||||
paintBackground.setColor(ThemeUtils.getColorFromAttr(getContext(),
|
||||
de.danoeh.antennapod.core.R.attr.currently_playing_background));
|
||||
paintBackground.setAlpha(128);
|
||||
paintProgressPrimary.setColor(ThemeUtils.getColorFromAttr(getContext(),
|
||||
de.danoeh.antennapod.core.R.attr.colorPrimary));
|
||||
paintProgressSecondary.setColor(ThemeUtils.getColorFromAttr(getContext(),
|
||||
de.danoeh.antennapod.core.R.attr.seek_background));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the relative positions of the chapter dividers.
|
||||
* @param dividerPos of the chapter dividers relative to the duration of the media.
|
||||
*/
|
||||
public void setDividerPos(final float[] dividerPos) {
|
||||
if (dividerPos != null) {
|
||||
this.dividerPos = new float[dividerPos.length + 2];
|
||||
this.dividerPos[0] = 0;
|
||||
System.arraycopy(dividerPos, 0, this.dividerPos, 1, dividerPos.length);
|
||||
this.dividerPos[this.dividerPos.length - 1] = 1;
|
||||
} else {
|
||||
this.dividerPos = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void onDraw(Canvas canvas) {
|
||||
top = getTop() + density * 7.5f;
|
||||
bottom = getBottom() - density * 7.5f;
|
||||
width = (float) (getRight() - getPaddingRight() - getLeft() - getPaddingLeft());
|
||||
progressSecondary = getSecondaryProgress() / (float) getMax() * width;
|
||||
progressPrimary = getProgress() / (float) getMax() * width;
|
||||
|
||||
if (dividerPos == null) {
|
||||
drawProgress(canvas);
|
||||
} else {
|
||||
drawProgressChapters(canvas);
|
||||
}
|
||||
drawThumb(canvas);
|
||||
}
|
||||
|
||||
private void drawProgress(Canvas canvas) {
|
||||
final int saveCount = canvas.save();
|
||||
canvas.translate(getPaddingLeft(), getPaddingTop());
|
||||
canvas.drawRect(0, top, width, bottom, paintBackground);
|
||||
canvas.drawRect(0, top, progressSecondary, bottom, paintProgressSecondary);
|
||||
canvas.drawRect(0, top, progressPrimary, bottom, paintProgressPrimary);
|
||||
canvas.restoreToCount(saveCount);
|
||||
}
|
||||
|
||||
private void drawProgressChapters(Canvas canvas) {
|
||||
final int saveCount = canvas.save();
|
||||
int currChapter = 1;
|
||||
float chapterMargin = density * 0.6f;
|
||||
float topExpanded = getTop() + density * 7;
|
||||
float bottomExpanded = getBottom() - density * 7;
|
||||
|
||||
canvas.translate(getPaddingLeft(), getPaddingTop());
|
||||
|
||||
for (int i = 1; i < dividerPos.length; i++) {
|
||||
float right = dividerPos[i] * width - chapterMargin;
|
||||
float left = dividerPos[i - 1] * width + chapterMargin;
|
||||
float rightCurr = dividerPos[currChapter] * width - chapterMargin;
|
||||
float leftCurr = dividerPos[currChapter - 1] * width + chapterMargin;
|
||||
|
||||
canvas.drawRect(left, top, right, bottom, paintBackground);
|
||||
|
||||
if (right < progressPrimary) {
|
||||
currChapter = i + 1;
|
||||
canvas.drawRect(left, top, right, bottom, paintProgressPrimary);
|
||||
} else if (isPressed()) {
|
||||
canvas.drawRect(leftCurr, topExpanded, rightCurr, bottomExpanded, paintBackground);
|
||||
canvas.drawRect(leftCurr, topExpanded, progressPrimary, bottomExpanded, paintProgressPrimary);
|
||||
} else {
|
||||
if (progressSecondary > leftCurr) {
|
||||
canvas.drawRect(leftCurr, top, progressSecondary, bottom, paintProgressSecondary);
|
||||
}
|
||||
canvas.drawRect(leftCurr, top, progressPrimary, bottom, paintProgressPrimary);
|
||||
}
|
||||
}
|
||||
canvas.restoreToCount(saveCount);
|
||||
}
|
||||
|
||||
private void drawThumb(Canvas canvas) {
|
||||
final int saveCount = canvas.save();
|
||||
canvas.translate(getPaddingLeft() - getThumbOffset(), getPaddingTop());
|
||||
getThumb().draw(canvas);
|
||||
canvas.restoreToCount(saveCount);
|
||||
}
|
||||
}
|
|
@ -88,7 +88,7 @@
|
|||
android:layoutDirection="ltr"
|
||||
android:orientation="vertical">
|
||||
|
||||
<SeekBar
|
||||
<de.danoeh.antennapod.view.ChapterSeekBar
|
||||
android:id="@+id/sbPosition"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
Loading…
Reference in New Issue