Chapter dividers for the progress bar (#4915)

This commit is contained in:
Jonas Burian 2021-03-01 20:59:28 +01:00 committed by GitHub
parent d444e8d23e
commit a476ce2f47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 169 additions and 10 deletions

View File

@ -29,6 +29,7 @@ import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.event.FavoritesEvent; import de.danoeh.antennapod.core.event.FavoritesEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent; 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.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; 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.SleepTimerDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog; import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.view.ChapterSeekBar;
import de.danoeh.antennapod.ui.common.PlaybackSpeedIndicatorView; import de.danoeh.antennapod.ui.common.PlaybackSpeedIndicatorView;
import io.reactivex.Maybe; import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
@ -63,7 +65,7 @@ import java.util.List;
* Shows the audio player. * Shows the audio player.
*/ */
public class AudioPlayerFragment extends Fragment implements public class AudioPlayerFragment extends Fragment implements
SeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener { ChapterSeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
public static final String TAG = "AudioPlayerFragment"; public static final String TAG = "AudioPlayerFragment";
private static final int POS_COVER = 0; private static final int POS_COVER = 0;
private static final int POS_DESCR = 1; private static final int POS_DESCR = 1;
@ -77,7 +79,7 @@ public class AudioPlayerFragment extends Fragment implements
private ViewPager2 pager; private ViewPager2 pager;
private TextView txtvPosition; private TextView txtvPosition;
private TextView txtvLength; private TextView txtvLength;
private SeekBar sbPosition; private ChapterSeekBar sbPosition;
private ImageButton butRev; private ImageButton butRev;
private TextView txtvRev; private TextView txtvRev;
private ImageButton butPlay; private ImageButton butPlay;
@ -172,12 +174,33 @@ public class AudioPlayerFragment extends Fragment implements
return root; return root;
} }
public void setHasChapters(boolean hasChapters) { private void setHasChapters(boolean hasChapters) {
this.hasChapters = hasChapters; this.hasChapters = hasChapters;
tabLayoutMediator.detach(); tabLayoutMediator.detach();
tabLayoutMediator.attach(); 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() { public View getExternalPlayerHolder() {
return getView().findViewById(R.id.playerFragment); return getView().findViewById(R.id.playerFragment);
} }
@ -298,6 +321,7 @@ public class AudioPlayerFragment extends Fragment implements
disposable = Maybe.create(emitter -> { disposable = Maybe.create(emitter -> {
Playable media = controller.getMedia(); Playable media = controller.getMedia();
if (media != null) { if (media != null) {
media.loadChapterMarks(getContext());
emitter.onSuccess(media); emitter.onSuccess(media);
} else { } else {
emitter.onComplete(); emitter.onComplete();
@ -389,8 +413,15 @@ public class AudioPlayerFragment extends Fragment implements
if (controller == null) { if (controller == null) {
return; return;
} }
if (media != null && media.getChapters() != null) {
setHasChapters(media.getChapters().size() > 0);
} else {
setHasChapters(false);
}
updatePosition(new PlaybackPositionEvent(controller.getPosition(), controller.getDuration())); updatePosition(new PlaybackPositionEvent(controller.getPosition(), controller.getDuration()));
updatePlaybackSpeedButton(media); updatePlaybackSpeedButton(media);
setChapterDividers(media);
setupOptionsMenu(media); setupOptionsMenu(media);
} }

View File

@ -137,7 +137,6 @@ public class ChaptersFragment extends Fragment {
return; return;
} }
adapter.setMedia(media); adapter.setMedia(media);
((AudioPlayerFragment) getParentFragment()).setHasChapters(adapter.getItemCount() > 0);
int positionOfCurrentChapter = getCurrentChapter(media); int positionOfCurrentChapter = getCurrentChapter(media);
updateChapterSelection(positionOfCurrentChapter); updateChapterSelection(positionOfCurrentChapter);
} }

View File

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

View File

@ -88,7 +88,7 @@
android:layoutDirection="ltr" android:layoutDirection="ltr"
android:orientation="vertical"> android:orientation="vertical">
<SeekBar <de.danoeh.antennapod.view.ChapterSeekBar
android:id="@+id/sbPosition" android:id="@+id/sbPosition"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"