This commit is contained in:
Martin Fietz 2017-10-21 18:01:49 +02:00
parent eb920d7363
commit ee50bca545
7 changed files with 173 additions and 224 deletions

View File

@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter; package de.danoeh.antennapod.adapter;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.text.Layout; import android.text.Layout;
import android.text.Selection; import android.text.Selection;
@ -42,8 +43,9 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
this.media = media; this.media = media;
} }
@NonNull
@Override @Override
public View getView(final int position, View convertView, ViewGroup parent) { public View getView(final int position, View convertView, @NonNull ViewGroup parent) {
Holder holder; Holder holder;
Chapter sc = getItem(position); Chapter sc = getItem(position);
@ -120,7 +122,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
if (link.length != 0) { if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) { if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget); link[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN){ } else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer, Selection.setSelection(buffer,
buffer.getSpanStart(link[0]), buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0])); buffer.getSpanEnd(link[0]));
@ -139,23 +141,17 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
callback.onPlayChapterButtonClicked(position); callback.onPlayChapterButtonClicked(position);
} }
}); });
Chapter current = ChapterUtils.getCurrentChapter(media); Chapter current = ChapterUtils.getCurrentChapter(media);
if (current != null) { if (current == sc) {
if (current == sc) { boolean darkTheme = UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark;
int playingBackGroundColor; int highlight = darkTheme ? R.color.highlight_dark : R.color.highlight_light;
if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { int playingBackGroundColor = ContextCompat.getColor(getContext(), highlight);
playingBackGroundColor = ContextCompat.getColor(getContext(), R.color.highlight_dark); holder.view.setBackgroundColor(playingBackGroundColor);
} else {
playingBackGroundColor = ContextCompat.getColor(getContext(), R.color.highlight_light);
}
holder.view.setBackgroundColor(playingBackGroundColor);
} else {
holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
holder.title.setTextColor(defaultTextColor);
holder.start.setTextColor(defaultTextColor);
}
} else { } else {
Log.w(TAG, "Could not find out what the current chapter is."); holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
holder.title.setTextColor(defaultTextColor);
holder.start.setTextColor(defaultTextColor);
} }
return convertView; return convertView;
@ -172,7 +168,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
@Override @Override
public int getCount() { public int getCount() {
if(media == null || media.getChapters() == null) { if (media == null || media.getChapters() == null) {
return 0; return 0;
} }
// ignore invalid chapters // ignore invalid chapters

View File

@ -27,11 +27,13 @@ public abstract class Chapter extends FeedComponent {
} }
public static Chapter fromCursor(Cursor cursor, FeedItem item) { public static Chapter fromCursor(Cursor cursor, FeedItem item) {
int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE); int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
int indexStart = cursor.getColumnIndex(PodDBAdapter.KEY_START); int indexStart = cursor.getColumnIndex(PodDBAdapter.KEY_START);
int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK); int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK);
int indexChapterType = cursor.getColumnIndex(PodDBAdapter.KEY_CHAPTER_TYPE); int indexChapterType = cursor.getColumnIndex(PodDBAdapter.KEY_CHAPTER_TYPE);
long id = cursor.getLong(indexId);
String title = cursor.getString(indexTitle); String title = cursor.getString(indexTitle);
long start = cursor.getLong(indexStart); long start = cursor.getLong(indexStart);
String link = cursor.getString(indexLink); String link = cursor.getString(indexLink);
@ -49,6 +51,7 @@ public abstract class Chapter extends FeedComponent {
chapter = new VorbisCommentChapter(start, title, item, link); chapter = new VorbisCommentChapter(start, title, item, link);
break; break;
} }
chapter.setId(id);
return chapter; return chapter;
} }

View File

@ -1,6 +1,7 @@
package de.danoeh.antennapod.core.feed; package de.danoeh.antennapod.core.feed;
import android.database.Cursor; import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;

View File

@ -389,16 +389,19 @@ public class FeedMedia extends FeedFile implements Playable {
if (item == null && itemID != 0) { if (item == null && itemID != 0) {
item = DBReader.getFeedItem(itemID); item = DBReader.getFeedItem(itemID);
} }
if (item == null || item.getChapters() != null) {
return;
}
// check if chapters are stored in db and not loaded yet. // check if chapters are stored in db and not loaded yet.
if (item != null && item.hasChapters() && item.getChapters() == null) { if (item.hasChapters()) {
DBReader.loadChaptersOfFeedItem(item); DBReader.loadChaptersOfFeedItem(item);
} else if (item != null && item.getChapters() == null) { } else {
if(localFileAvailable()) { if(localFileAvailable()) {
ChapterUtils.loadChaptersFromFileUrl(this); ChapterUtils.loadChaptersFromFileUrl(this);
} else { } else {
ChapterUtils.loadChaptersFromStreamUrl(this); ChapterUtils.loadChaptersFromStreamUrl(this);
} }
if (getChapters() != null && item != null) { if (item.getChapters() != null) {
DBWriter.setFeedItem(item); DBWriter.setFeedItem(item);
} }
} }

View File

@ -811,31 +811,8 @@ public final class DBReader {
} }
item.setChapters(new ArrayList<>(chaptersCount)); item.setChapters(new ArrayList<>(chaptersCount));
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
int indexType = cursor.getColumnIndex(PodDBAdapter.KEY_CHAPTER_TYPE); Chapter chapter = Chapter.fromCursor(cursor, item);
int indexStart = cursor.getColumnIndex(PodDBAdapter.KEY_START);
int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK);
int chapterType = cursor.getInt(indexType);
Chapter chapter = null;
long start = cursor.getLong(indexStart);
String title = cursor.getString(indexTitle);
String link = cursor.getString(indexLink);
switch (chapterType) {
case SimpleChapter.CHAPTERTYPE_SIMPLECHAPTER:
chapter = new SimpleChapter(start, title, item, link);
break;
case ID3Chapter.CHAPTERTYPE_ID3CHAPTER:
chapter = new ID3Chapter(start, title, item, link);
break;
case VorbisCommentChapter.CHAPTERTYPE_VORBISCOMMENT_CHAPTER:
chapter = new VorbisCommentChapter(start, title, item, link);
break;
}
if (chapter != null) { if (chapter != null) {
int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
chapter.setId(cursor.getLong(indexId));
item.getChapters().add(chapter); item.getChapters().add(chapter);
} }
} }

View File

@ -1,5 +1,7 @@
package de.danoeh.antennapod.core.util; package de.danoeh.antennapod.core.util;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@ -14,7 +16,6 @@ import java.net.URL;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.util.comparator.ChapterStartTimeComparator; import de.danoeh.antennapod.core.util.comparator.ChapterStartTimeComparator;
import de.danoeh.antennapod.core.util.id3reader.ChapterReader; import de.danoeh.antennapod.core.util.id3reader.ChapterReader;
@ -27,55 +28,74 @@ import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderExc
* Utility class for getting chapter data from media files. * Utility class for getting chapter data from media files.
*/ */
public class ChapterUtils { public class ChapterUtils {
private static final String TAG = "ChapterUtils"; private static final String TAG = "ChapterUtils";
private ChapterUtils() { private ChapterUtils() {
} }
@Nullable
public static Chapter getCurrentChapter(Playable media) {
if (media.getChapters() == null) {
return null;
}
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;
}
}
return current;
}
public static void loadChaptersFromStreamUrl(Playable media) {
ChapterUtils.readID3ChaptersFromPlayableStreamUrl(media);
if (media.getChapters() == null) {
ChapterUtils.readOggChaptersFromPlayableStreamUrl(media);
}
}
public static void loadChaptersFromFileUrl(Playable media) {
if (!media.localFileAvailable()) {
Log.e(TAG, "Could not load chapters from file url: local file not available");
return;
}
ChapterUtils.readID3ChaptersFromPlayableFileUrl(media);
if (media.getChapters() == null) {
ChapterUtils.readOggChaptersFromPlayableFileUrl(media);
}
}
/** /**
* Uses the download URL of a media object of a feeditem to read its ID3 * Uses the download URL of a media object of a feeditem to read its ID3
* chapters. * chapters.
*/ */
public static void readID3ChaptersFromPlayableStreamUrl(Playable p) { private static void readID3ChaptersFromPlayableStreamUrl(Playable p) {
if (p != null && p.getStreamUrl() != null) { if (p == null || p.getStreamUrl() == null) {
if (BuildConfig.DEBUG) Log.e(TAG, "Unable to read ID3 chapters: media or download URL was null");
Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle()); return;
InputStream in = null; }
try { Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
URL url = new URL(p.getStreamUrl()); InputStream in = null;
ChapterReader reader = new ChapterReader(); try {
URL url = new URL(p.getStreamUrl());
in = url.openStream(); in = url.openStream();
reader.readInputStream(in); List<Chapter> chapters = readChaptersFrom(in);
List<Chapter> chapters = reader.getChapters(); if(!chapters.isEmpty()) {
p.setChapters(chapters);
if (chapters != null) {
Collections
.sort(chapters, new ChapterStartTimeComparator());
processChapters(chapters, p);
if (chaptersValid(chapters)) {
p.setChapters(chapters);
Log.i(TAG, "Chapters loaded");
} else {
Log.e(TAG, "Chapter data was invalid");
}
} else {
Log.i(TAG, "ChapterReader could not find any ID3 chapters");
}
} catch (IOException | ID3ReaderException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} }
} else { Log.i(TAG, "Chapters loaded");
Log.e(TAG, } catch (IOException | ID3ReaderException e) {
"Unable to read ID3 chapters: media or download URL was null"); Log.e(TAG, Log.getStackTraceString(e));
} finally {
IOUtils.closeQuietly(in);
} }
} }
@ -83,107 +103,104 @@ public class ChapterUtils {
* Uses the file URL of a media object of a feeditem to read its ID3 * Uses the file URL of a media object of a feeditem to read its ID3
* chapters. * chapters.
*/ */
public static void readID3ChaptersFromPlayableFileUrl(Playable p) { private static void readID3ChaptersFromPlayableFileUrl(Playable p) {
if (p != null && p.localFileAvailable() && p.getLocalMediaUrl() != null) { if (p == null || !p.localFileAvailable() || p.getLocalMediaUrl() == null) {
if (BuildConfig.DEBUG) return;
Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle()); }
File source = new File(p.getLocalMediaUrl()); Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
if (source.exists()) { File source = new File(p.getLocalMediaUrl());
ChapterReader reader = new ChapterReader(); if (!source.exists()) {
InputStream in = null; Log.e(TAG, "Unable to read id3 chapters: Source doesn't exist");
return;
}
try { InputStream in = null;
in = new BufferedInputStream(new FileInputStream(source)); try {
reader.readInputStream(in); in = new BufferedInputStream(new FileInputStream(source));
List<Chapter> chapters = reader.getChapters(); List<Chapter> chapters = readChaptersFrom(in);
if (!chapters.isEmpty()) {
if (chapters != null) { p.setChapters(chapters);
Collections.sort(chapters,
new ChapterStartTimeComparator());
processChapters(chapters, p);
if (chaptersValid(chapters)) {
p.setChapters(chapters);
Log.i(TAG, "Chapters loaded");
} else {
Log.e(TAG, "Chapter data was invalid");
}
} else {
Log.i(TAG,
"ChapterReader could not find any ID3 chapters");
}
} catch (IOException | ID3ReaderException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} else {
Log.e(TAG, "Unable to read id3 chapters: Source doesn't exist");
} }
Log.i(TAG, "Chapters loaded");
} catch (IOException | ID3ReaderException e) {
Log.e(TAG, Log.getStackTraceString(e));
} finally {
IOUtils.closeQuietly(in);
} }
} }
public static void readOggChaptersFromPlayableStreamUrl(Playable media) { @NonNull
if (media != null && media.streamAvailable()) { private static List<Chapter> readChaptersFrom(InputStream in) throws IOException, ID3ReaderException {
ChapterReader reader = new ChapterReader();
reader.readInputStream(in);
List<Chapter> chapters = reader.getChapters();
if (chapters == null) {
Log.i(TAG, "ChapterReader could not find any ID3 chapters");
return Collections.emptyList();
}
Collections.sort(chapters, new ChapterStartTimeComparator());
enumerateEmptyChapterTitles(chapters);
if (!chaptersValid(chapters)) {
Log.e(TAG, "Chapter data was invalid");
return Collections.emptyList();
}
return chapters;
}
private static void readOggChaptersFromPlayableStreamUrl(Playable media) {
if (media == null || !media.streamAvailable()) {
return;
}
InputStream input = null;
try {
URL url = new URL(media.getStreamUrl());
input = url.openStream();
if (input != null) {
readOggChaptersFromInputStream(media, input);
}
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
} finally {
IOUtils.closeQuietly(input);
}
}
private static void readOggChaptersFromPlayableFileUrl(Playable media) {
if (media == null || media.getLocalMediaUrl() == null) {
return;
}
File source = new File(media.getLocalMediaUrl());
if (source.exists()) {
InputStream input = null; InputStream input = null;
try { try {
URL url = new URL(media.getStreamUrl()); input = new BufferedInputStream(new FileInputStream(source));
input = url.openStream(); readOggChaptersFromInputStream(media, input);
if (input != null) { } catch (FileNotFoundException e) {
readOggChaptersFromInputStream(media, input); Log.e(TAG, Log.getStackTraceString(e));
}
} catch (IOException e) {
e.printStackTrace();
} finally { } finally {
IOUtils.closeQuietly(input); IOUtils.closeQuietly(input);
} }
} }
} }
public static void readOggChaptersFromPlayableFileUrl(Playable media) { private static void readOggChaptersFromInputStream(Playable p, InputStream input) {
if (media != null && media.getLocalMediaUrl() != null) { Log.d(TAG, "Trying to read chapters from item with title " + p.getEpisodeTitle());
File source = new File(media.getLocalMediaUrl());
if (source.exists()) {
InputStream input = null;
try {
input = new BufferedInputStream(new FileInputStream(source));
readOggChaptersFromInputStream(media, input);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(input);
}
}
}
}
private static void readOggChaptersFromInputStream(Playable p,
InputStream input) {
if (BuildConfig.DEBUG)
Log.d(TAG,
"Trying to read chapters from item with title "
+ p.getEpisodeTitle());
try { try {
VorbisCommentChapterReader reader = new VorbisCommentChapterReader(); VorbisCommentChapterReader reader = new VorbisCommentChapterReader();
reader.readInputStream(input); reader.readInputStream(input);
List<Chapter> chapters = reader.getChapters(); List<Chapter> chapters = reader.getChapters();
if (chapters != null) { if (chapters == null) {
Collections.sort(chapters, new ChapterStartTimeComparator()); Log.i(TAG, "ChapterReader could not find any Ogg vorbis chapters");
processChapters(chapters, p); return;
if (chaptersValid(chapters)) { }
p.setChapters(chapters); Collections.sort(chapters, new ChapterStartTimeComparator());
Log.i(TAG, "Chapters loaded"); enumerateEmptyChapterTitles(chapters);
} else { if (chaptersValid(chapters)) {
Log.e(TAG, "Chapter data was invalid"); p.setChapters(chapters);
} Log.i(TAG, "Chapters loaded");
} else { } else {
Log.i(TAG, Log.e(TAG, "Chapter data was invalid");
"ChapterReader could not find any Ogg vorbis chapters");
} }
} catch (VorbisCommentReaderException e) { } catch (VorbisCommentReaderException e) {
e.printStackTrace(); e.printStackTrace();
@ -193,7 +210,7 @@ public class ChapterUtils {
/** /**
* Makes sure that chapter does a title and an item attribute. * Makes sure that chapter does a title and an item attribute.
*/ */
private static void processChapters(List<Chapter> chapters, Playable p) { private static void enumerateEmptyChapterTitles(List<Chapter> chapters) {
for (int i = 0; i < chapters.size(); i++) { for (int i = 0; i < chapters.size(); i++) {
Chapter c = chapters.get(i); Chapter c = chapters.get(i);
if (c.getTitle() == null) { if (c.getTitle() == null) {
@ -207,9 +224,6 @@ public class ChapterUtils {
return false; return false;
} }
for (Chapter c : chapters) { for (Chapter c : chapters) {
if (c.getTitle() == null) {
return false;
}
if (c.getStart() < 0) { if (c.getStart() < 0) {
return false; return false;
} }
@ -217,49 +231,4 @@ public class ChapterUtils {
return true; return true;
} }
/**
* Calls getCurrentChapter with current position.
*/
public static Chapter getCurrentChapter(Playable media) {
if (media.getChapters() != null) {
List<Chapter> chapters = media.getChapters();
Chapter current = null;
if (chapters != null) {
current = chapters.get(0);
for (Chapter sc : chapters) {
if (sc.getStart() > media.getPosition()) {
break;
} else {
current = sc;
}
}
}
return current;
} else {
return null;
}
}
public static void loadChaptersFromStreamUrl(Playable media) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Starting chapterLoader thread");
ChapterUtils.readID3ChaptersFromPlayableStreamUrl(media);
if (media.getChapters() == null) {
ChapterUtils.readOggChaptersFromPlayableStreamUrl(media);
}
if (BuildConfig.DEBUG)
Log.d(TAG, "ChapterLoaderThread has finished");
}
public static void loadChaptersFromFileUrl(Playable media) {
if (media.localFileAvailable()) {
ChapterUtils.readID3ChaptersFromPlayableFileUrl(media);
if (media.getChapters() == null) {
ChapterUtils.readOggChaptersFromPlayableFileUrl(media);
}
} else {
Log.e(TAG, "Could not load chapters from file url: local file not available");
}
}
} }

View File

@ -27,20 +27,20 @@ public class ChapterReader extends ID3Reader {
@Override @Override
public int onStartTagHeader(TagHeader header) { public int onStartTagHeader(TagHeader header) {
chapters = new ArrayList<>(); chapters = new ArrayList<>();
System.out.println(header.toString()); Log.d(TAG, "header: " + header);
return ID3Reader.ACTION_DONT_SKIP; return ID3Reader.ACTION_DONT_SKIP;
} }
@Override @Override
public int onStartFrameHeader(FrameHeader header, InputStream input) public int onStartFrameHeader(FrameHeader header, InputStream input)
throws IOException, ID3ReaderException { throws IOException, ID3ReaderException {
System.out.println(header.toString()); Log.d(TAG, "header: " + header);
switch (header.getId()) { switch (header.getId()) {
case FRAME_ID_CHAPTER: case FRAME_ID_CHAPTER:
if (currentChapter != null) { if (currentChapter != null) {
if (!hasId3Chapter(currentChapter)) { if (!hasId3Chapter(currentChapter)) {
chapters.add(currentChapter); chapters.add(currentChapter);
if (BuildConfig.DEBUG) Log.d(TAG, "Found chapter: " + currentChapter); Log.d(TAG, "Found chapter: " + currentChapter);
currentChapter = null; currentChapter = null;
} }
} }
@ -59,7 +59,7 @@ public class ChapterReader extends ID3Reader {
readString(title, input, header.getSize()); readString(title, input, header.getSize());
currentChapter currentChapter
.setTitle(title.toString()); .setTitle(title.toString());
if (BuildConfig.DEBUG) Log.d(TAG, "Found title: " + currentChapter.getTitle()); Log.d(TAG, "Found title: " + currentChapter.getTitle());
return ID3Reader.ACTION_DONT_SKIP; return ID3Reader.ACTION_DONT_SKIP;
} }
@ -74,7 +74,7 @@ public class ChapterReader extends ID3Reader {
currentChapter.setLink(decodedLink); currentChapter.setLink(decodedLink);
if (BuildConfig.DEBUG) Log.d(TAG, "Found link: " + currentChapter.getLink()); Log.d(TAG, "Found link: " + currentChapter.getLink());
return ID3Reader.ACTION_DONT_SKIP; return ID3Reader.ACTION_DONT_SKIP;
} }
break; break;
@ -102,17 +102,17 @@ public class ChapterReader extends ID3Reader {
chapters.add(currentChapter); chapters.add(currentChapter);
} }
} }
System.out.println("Reached end of tag"); Log.d(TAG, "Reached end of tag");
if (chapters != null) { if (chapters != null) {
for (Chapter c : chapters) { for (Chapter c : chapters) {
System.out.println(c.toString()); Log.d(TAG, "chapter: " + c);
} }
} }
} }
@Override @Override
public void onNoTagHeaderFound() { public void onNoTagHeaderFound() {
System.out.println("No tag header found"); Log.d(TAG, "No tag header found");
super.onNoTagHeaderFound(); super.onNoTagHeaderFound();
} }