summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorByteHamster <info@bytehamster.com>2020-02-10 12:27:09 +0100
committerByteHamster <info@bytehamster.com>2020-02-12 17:12:21 +0100
commit312cb845981466b2c1464e0b6227dfafe9848b2c (patch)
tree6189127e1d48231a516ff9477f720d825f9f8ed4
parent9497a97289d7d798a09d3a155b402fc9495693a8 (diff)
downloadAntennaPod-312cb845981466b2c1464e0b6227dfafe9848b2c.zip
Added ChapterImageModelLoader
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java60
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java98
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java58
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java5
7 files changed, 220 insertions, 38 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
index 386b760b1..9940ccbdd 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -6,6 +6,7 @@ import android.util.Log;
import android.view.View;
import android.widget.ListView;
+import de.danoeh.antennapod.core.util.ChapterUtils;
import java.util.List;
import java.util.ListIterator;
@@ -104,18 +105,10 @@ public class ChaptersFragment extends ListFragment {
}
private int getCurrentChapter(Playable media) {
- if (media == null || media.getChapters() == null || media.getChapters().size() == 0 || controller == null) {
+ if (controller == null) {
return -1;
}
- int currentPosition = controller.getPosition();
-
- List<Chapter> chapters = media.getChapters();
- for (int i = 0; i < chapters.size(); i++) {
- if (chapters.get(i).getStart() > currentPosition) {
- return i - 1;
- }
- }
- return chapters.size() - 1;
+ return ChapterUtils.getCurrentChapterIndex(media, controller.getPosition());
}
private void loadMediaInfo() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index 5467d71a8..b9263f21e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
+import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import android.util.Log;
@@ -14,14 +15,20 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
+import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
+import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Displays the cover and the title of a FeedItem.
@@ -36,6 +43,8 @@ public class CoverFragment extends Fragment {
private ImageView imgvCover;
private PlaybackController controller;
private Disposable disposable;
+ private int displayedChapterIndex = -1;
+ private Playable media;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -53,18 +62,20 @@ public class CoverFragment extends Fragment {
if (disposable != null) {
disposable.dispose();
}
- disposable = Maybe.create(emitter -> {
- Playable media = controller.getMedia();
- if (media != null) {
- emitter.onSuccess(media);
- } else {
- emitter.onComplete();
- }
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(media -> displayMediaInfo((Playable) media),
- error -> Log.e(TAG, Log.getStackTraceString(error)));
+ disposable = Maybe.<Playable>create(emitter -> {
+ Playable media = controller.getMedia();
+ if (media != null) {
+ emitter.onSuccess(media);
+ } else {
+ emitter.onComplete();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(media -> {
+ this.media = media;
+ displayMediaInfo(media);
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
private void displayMediaInfo(@NonNull Playable media) {
@@ -99,6 +110,7 @@ public class CoverFragment extends Fragment {
};
controller.init();
loadMediaInfo();
+ EventBus.getDefault().register(this);
}
@Override
@@ -106,6 +118,30 @@ public class CoverFragment extends Fragment {
super.onStop();
controller.release();
controller = null;
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ if (controller == null) {
+ return;
+ }
+ int chapter = ChapterUtils.getCurrentChapterIndex(media, event.getPosition());
+ if (chapter != displayedChapterIndex) {
+ displayedChapterIndex = chapter;
+
+ if (chapter == -1 || TextUtils.isEmpty(media.getChapters().get(chapter).getImageUrl())) {
+ displayMediaInfo(media);
+ } else {
+ Glide.with(this)
+ .load(EmbeddedChapterImage.getModelFor(media, chapter))
+ .apply(new RequestOptions()
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .dontAnimate()
+ .fitCenter())
+ .into(imgvCover);
+ }
+ }
}
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
index b2c809e90..50511526f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
@@ -11,10 +11,12 @@ import com.bumptech.glide.load.DecodeFormat;
import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory;
import com.bumptech.glide.module.AppGlideModule;
+import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
import java.io.InputStream;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import java.nio.ByteBuffer;
/**
* {@see com.bumptech.glide.integration.okhttp.OkHttpGlideModule}
@@ -32,5 +34,6 @@ public class ApGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
registry.replace(String.class, InputStream.class, new ApOkHttpUrlLoader.Factory());
+ registry.append(EmbeddedChapterImage.class, ByteBuffer.class, new ChapterImageModelLoader.Factory());
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
new file mode 100644
index 000000000..6548e9c5e
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
@@ -0,0 +1,98 @@
+package de.danoeh.antennapod.core.glide;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.bumptech.glide.Priority;
+import com.bumptech.glide.load.DataSource;
+import com.bumptech.glide.load.Options;
+import com.bumptech.glide.load.data.DataFetcher;
+import com.bumptech.glide.load.model.ModelLoader;
+import com.bumptech.glide.load.model.ModelLoaderFactory;
+import com.bumptech.glide.load.model.MultiModelLoaderFactory;
+import com.bumptech.glide.signature.ObjectKey;
+import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import org.apache.commons.io.IOUtils;
+
+public final class ChapterImageModelLoader implements ModelLoader<EmbeddedChapterImage, ByteBuffer> {
+
+ public static class Factory implements ModelLoaderFactory<EmbeddedChapterImage, ByteBuffer> {
+ @Override
+ public ModelLoader<EmbeddedChapterImage, ByteBuffer> build(MultiModelLoaderFactory unused) {
+ return new ChapterImageModelLoader();
+ }
+
+ @Override
+ public void teardown() {
+ // Do nothing.
+ }
+ }
+
+ @Nullable
+ @Override
+ public LoadData<ByteBuffer> buildLoadData(EmbeddedChapterImage model, int width, int height, Options options) {
+ return new LoadData<>(new ObjectKey(model), new EmbeddedImageFetcher(model));
+ }
+
+ @Override
+ public boolean handles(EmbeddedChapterImage model) {
+ return true;
+ }
+
+ class EmbeddedImageFetcher implements DataFetcher<ByteBuffer> {
+ private final EmbeddedChapterImage image;
+
+ public EmbeddedImageFetcher(EmbeddedChapterImage image) {
+ this.image = image;
+ }
+
+ @Override
+ public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
+
+ BufferedInputStream stream = null;
+ try {
+ if (image.getMedia().localFileAvailable()) {
+ File localFile = new File(image.getMedia().getLocalMediaUrl());
+ stream = new BufferedInputStream(new FileInputStream(localFile));
+ } else {
+ URL url = new URL(image.getMedia().getStreamUrl());
+ stream = new BufferedInputStream(url.openStream());
+ }
+ byte[] imageContent = new byte[image.getLength()];
+ stream.skip(image.getPosition());
+ stream.read(imageContent, 0, image.getLength());
+ callback.onDataReady(ByteBuffer.wrap(imageContent));
+ } catch (IOException e) {
+ callback.onLoadFailed(new IOException("Loading embedded cover did not work"));
+ e.printStackTrace();
+ } finally {
+ IOUtils.closeQuietly(stream);
+ }
+ }
+
+ @Override public void cleanup() {
+ // nothing to clean up
+ }
+ @Override public void cancel() {
+ // cannot cancel
+ }
+
+ @NonNull
+ @Override
+ public Class<ByteBuffer> getDataClass() {
+ return ByteBuffer.class;
+ }
+
+ @NonNull
+ @Override
+ public DataSource getDataSource() {
+ return DataSource.LOCAL;
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
index e69bb2863..b75887154 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
@@ -36,24 +36,17 @@ public class ChapterUtils {
private ChapterUtils() {
}
- @Nullable
- public static Chapter getCurrentChapter(Playable media) {
- if (media.getChapters() == null) {
- return null;
+ public static int getCurrentChapterIndex(Playable media, int position) {
+ if (media == null || media.getChapters() == null || media.getChapters().size() == 0) {
+ return -1;
}
List<Chapter> chapters = media.getChapters();
- if (chapters == null) {
- return null;
- }
- Chapter current = chapters.get(0);
- for (Chapter sc : chapters) {
- if (sc.getStart() > media.getPosition()) {
- break;
- } else {
- current = sc;
+ for (int i = 0; i < chapters.size(); i++) {
+ if (chapters.get(i).getStart() > position) {
+ return i - 1;
}
}
- return current;
+ return chapters.size() - 1;
}
public static void loadChaptersFromStreamUrl(Playable media) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java b/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java
new file mode 100644
index 000000000..5cb62e6c2
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java
@@ -0,0 +1,58 @@
+package de.danoeh.antennapod.core.util;
+
+import de.danoeh.antennapod.core.util.playback.Playable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class EmbeddedChapterImage {
+ private static final Pattern EMBEDDED_IMAGE_MATCHER = Pattern.compile("embedded-image://(.*)@(\\d+):(\\d+)");
+ final String mime;
+ final int position;
+ final int length;
+ final Playable media;
+
+ public EmbeddedChapterImage(Playable media, String imageUrl) {
+ this.media = media;
+ Matcher m = EMBEDDED_IMAGE_MATCHER.matcher(imageUrl);
+ if (m.find()) {
+ this.mime = m.group(1);
+ this.position = Integer.parseInt(m.group(2));
+ this.length = Integer.parseInt(m.group(3));
+ } else {
+ throw new IllegalArgumentException("Not an embedded chapter");
+ }
+ }
+
+ public static String makeUrl(String mime, int position, int length) {
+ return "embedded-image://" + mime + "@" + position + ":" + length;
+ }
+
+ public String getMime() {
+ return mime;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public Playable getMedia() {
+ return media;
+ }
+
+ private static boolean isEmbeddedChapterImage(String imageUrl) {
+ return EMBEDDED_IMAGE_MATCHER.matcher(imageUrl).matches();
+ }
+
+ public static Object getModelFor(Playable media, int chapter) {
+ String imageUrl = media.getChapters().get(chapter).getImageUrl();
+ if (isEmbeddedChapterImage(imageUrl)) {
+ return new EmbeddedChapterImage(media, imageUrl);
+ } else {
+ return imageUrl;
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
index b007967c1..934e0b00c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
@@ -4,6 +4,7 @@ import android.text.TextUtils;
import android.util.Log;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.ID3Chapter;
+import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
import de.danoeh.antennapod.core.util.id3reader.model.TagHeader;
@@ -104,8 +105,8 @@ public class ChapterReader extends ID3Reader {
// Data contains the picture
int length = header.getSize() - read;
if (TextUtils.isEmpty(currentChapter.getImageUrl()) || type == IMAGE_TYPE_COVER) {
- currentChapter.setImageUrl("embedded-image://" + mime.toString()
- + "@" + input.getByteCount() + ":" + length);
+ currentChapter.setImageUrl(
+ EmbeddedChapterImage.makeUrl(mime.toString(), input.getCount(), length));
}
skipBytes(input, length);
}