summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/danoeh/antennapod')
-rw-r--r--src/de/danoeh/antennapod/activity/AudioplayerActivity.java21
-rw-r--r--src/de/danoeh/antennapod/adapter/ChapterListAdapter.java (renamed from src/de/danoeh/antennapod/adapter/SCListAdapter.java)13
-rw-r--r--src/de/danoeh/antennapod/feed/Chapter.java58
-rw-r--r--src/de/danoeh/antennapod/feed/FeedItem.java23
-rw-r--r--src/de/danoeh/antennapod/feed/FeedManager.java39
-rw-r--r--src/de/danoeh/antennapod/feed/FeedSearcher.java4
-rw-r--r--src/de/danoeh/antennapod/feed/ID3Chapter.java36
-rw-r--r--src/de/danoeh/antennapod/feed/SimpleChapter.java23
-rw-r--r--src/de/danoeh/antennapod/service/PlaybackService.java3
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadService.java20
-rw-r--r--src/de/danoeh/antennapod/storage/PodDBAdapter.java34
-rw-r--r--src/de/danoeh/antennapod/syndication/namespace/simplechapters/NSSimpleChapters.java14
-rw-r--r--src/de/danoeh/antennapod/util/ChapterUtils.java97
-rw-r--r--src/de/danoeh/antennapod/util/PlaybackController.java3
-rw-r--r--src/de/danoeh/antennapod/util/comparator/ChapterStartTimeComparator.java20
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/ChapterReader.java95
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/ID3Reader.java182
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/ID3ReaderException.java20
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java18
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/model/Header.java29
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/model/TagHeader.java26
21 files changed, 697 insertions, 81 deletions
diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
index 3587775a5..c469e70f9 100644
--- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -16,7 +16,8 @@ import com.viewpagerindicator.TabPageIndicator;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.adapter.SCListAdapter;
+import de.danoeh.antennapod.adapter.ChapterListAdapter;
+import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.feed.SimpleChapter;
import de.danoeh.antennapod.fragment.CoverFragment;
@@ -62,7 +63,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
FeedMedia media = controller.getMedia();
int tabcount = 2;
- if (media != null && media.getItem().getSimpleChapters() != null) {
+ if (media != null && media.getItem().getChapters() != null) {
tabcount = 3;
}
pagerAdapter = new MediaPlayerPagerAdapter(getSupportFragmentManager(),
@@ -85,8 +86,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
}
}
- public class MediaPlayerPagerAdapter extends
- FragmentStatePagerAdapter {
+ public class MediaPlayerPagerAdapter extends FragmentStatePagerAdapter {
private int numItems;
private AudioplayerActivity activity;
@@ -109,8 +109,8 @@ public class AudioplayerActivity extends MediaplayerActivity {
if (media != null) {
switch (position) {
case POS_COVER:
- activity.coverFragment = CoverFragment
- .newInstance(media.getItem());
+ activity.coverFragment = CoverFragment.newInstance(media
+ .getItem());
return activity.coverFragment;
case POS_DESCR:
activity.descriptionFragment = ItemDescriptionFragment
@@ -123,16 +123,15 @@ public class AudioplayerActivity extends MediaplayerActivity {
public void onListItemClick(ListView l, View v,
int position, long id) {
super.onListItemClick(l, v, position, id);
- SimpleChapter chapter = (SimpleChapter) this
- .getListAdapter().getItem(position);
+ Chapter chapter = (Chapter) this.getListAdapter().getItem(
+ position);
controller.seekToChapter(chapter);
}
};
- sCChapterFragment.setListAdapter(new SCListAdapter(
- activity, 0, media.getItem()
- .getSimpleChapters()));
+ sCChapterFragment.setListAdapter(new ChapterListAdapter(
+ activity, 0, media.getItem().getChapters()));
return sCChapterFragment;
default:
diff --git a/src/de/danoeh/antennapod/adapter/SCListAdapter.java b/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java
index 0174b4848..e13b930eb 100644
--- a/src/de/danoeh/antennapod/adapter/SCListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java
@@ -20,15 +20,16 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.SimpleChapter;
import de.danoeh.antennapod.util.Converter;
-public class SCListAdapter extends ArrayAdapter<SimpleChapter> {
+public class ChapterListAdapter extends ArrayAdapter<Chapter> {
- private static final String TAG = "SCListAdapter";
+ private static final String TAG = "ChapterListAdapter";
- public SCListAdapter(Context context, int textViewResourceId,
- List<SimpleChapter> objects) {
+ public ChapterListAdapter(Context context, int textViewResourceId,
+ List<Chapter> objects) {
super(context, textViewResourceId, objects);
}
@@ -36,7 +37,7 @@ public class SCListAdapter extends ArrayAdapter<SimpleChapter> {
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
- SimpleChapter sc = getItem(position);
+ Chapter sc = getItem(position);
// Inflate Layout
if (convertView == null) {
@@ -114,7 +115,7 @@ public class SCListAdapter extends ArrayAdapter<SimpleChapter> {
}
});
- SimpleChapter current = sc.getItem().getCurrentChapter();
+ Chapter current = sc.getItem().getCurrentChapter();
if (current != null) {
if (current == sc) {
holder.title.setTextColor(convertView.getResources().getColor(
diff --git a/src/de/danoeh/antennapod/feed/Chapter.java b/src/de/danoeh/antennapod/feed/Chapter.java
new file mode 100644
index 000000000..f50053f93
--- /dev/null
+++ b/src/de/danoeh/antennapod/feed/Chapter.java
@@ -0,0 +1,58 @@
+package de.danoeh.antennapod.feed;
+
+public abstract class Chapter extends FeedComponent {
+
+ /** Defines starting point in milliseconds. */
+ protected long start;
+ protected String title;
+ protected FeedItem item;
+ protected String link;
+
+ public Chapter(long start) {
+ super();
+ this.start = start;
+ }
+
+ public Chapter(long start, String title, FeedItem item, String link) {
+ super();
+ this.start = start;
+ this.title = title;
+ this.item = item;
+ this.link = link;
+ }
+
+ public abstract int getChapterType();
+
+ public long getStart() {
+ return start;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public FeedItem getItem() {
+ return item;
+ }
+
+ public String getLink() {
+ return link;
+ }
+
+ public void setStart(long start) {
+ this.start = start;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setItem(FeedItem item) {
+ this.item = item;
+ }
+
+ public void setLink(String link) {
+ this.link = link;
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/feed/FeedItem.java b/src/de/danoeh/antennapod/feed/FeedItem.java
index 209426b5e..3fa9a66b8 100644
--- a/src/de/danoeh/antennapod/feed/FeedItem.java
+++ b/src/de/danoeh/antennapod/feed/FeedItem.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.feed;
import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
/**
* Data Object for a XML message
@@ -22,7 +23,7 @@ public class FeedItem extends FeedComponent {
private Feed feed;
protected boolean read;
private String paymentLink;
- private ArrayList<SimpleChapter> simpleChapters;
+ private List<Chapter> chapters;
public FeedItem() {
this.read = true;
@@ -41,11 +42,11 @@ public class FeedItem extends FeedComponent {
}
/** Get the chapter that fits the position. */
- public SimpleChapter getCurrentChapter(int position) {
- SimpleChapter current = null;
- if (simpleChapters != null) {
- current = simpleChapters.get(0);
- for (SimpleChapter sc : simpleChapters) {
+ public Chapter getCurrentChapter(int position) {
+ Chapter current = null;
+ if (chapters != null) {
+ current = chapters.get(0);
+ for (Chapter sc : chapters) {
if (sc.getStart() > position) {
break;
} else {
@@ -57,7 +58,7 @@ public class FeedItem extends FeedComponent {
}
/** Calls getCurrentChapter with current position. */
- public SimpleChapter getCurrentChapter() {
+ public Chapter getCurrentChapter() {
return getCurrentChapter(media.getPosition());
}
@@ -144,12 +145,12 @@ public class FeedItem extends FeedComponent {
this.paymentLink = paymentLink;
}
- public ArrayList<SimpleChapter> getSimpleChapters() {
- return simpleChapters;
+ public List<Chapter> getChapters() {
+ return chapters;
}
- public void setSimpleChapters(ArrayList<SimpleChapter> simpleChapters) {
- this.simpleChapters = simpleChapters;
+ public void setChapters(List<Chapter> chapters) {
+ this.chapters = chapters;
}
public String getItemIdentifier() {
diff --git a/src/de/danoeh/antennapod/feed/FeedManager.java b/src/de/danoeh/antennapod/feed/FeedManager.java
index a3ab901f9..1c6d7036f 100644
--- a/src/de/danoeh/antennapod/feed/FeedManager.java
+++ b/src/de/danoeh/antennapod/feed/FeedManager.java
@@ -374,13 +374,13 @@ public class FeedManager {
downloadFeedItem(context, queue.toArray(new FeedItem[queue.size()]));
}
}
-
+
public void downloadFeedItem(final Context context, FeedItem... items) {
boolean autoQueue = PreferenceManager.getDefaultSharedPreferences(
context.getApplicationContext()).getBoolean(
PodcastApp.PREF_AUTO_QUEUE, true);
List<FeedItem> addToQueue = new ArrayList<FeedItem>();
-
+
for (FeedItem item : items) {
if (item.getMedia() != null
&& !requester.isDownloadingFile(item.getMedia())
@@ -390,7 +390,8 @@ public class FeedManager {
}
}
if (autoQueue) {
- addQueueItem(context, addToQueue.toArray(new FeedItem[addToQueue.size()]));
+ addQueueItem(context,
+ addToQueue.toArray(new FeedItem[addToQueue.size()]));
}
}
@@ -919,19 +920,31 @@ public class FeedManager {
Cursor chapterCursor = adapter
.getSimpleChaptersOfFeedItemCursor(item);
if (chapterCursor.moveToFirst()) {
- item.setSimpleChapters(new ArrayList<SimpleChapter>());
+ item.setChapters(new ArrayList<Chapter>());
do {
- SimpleChapter chapter = new SimpleChapter(
- item,
- chapterCursor
- .getLong(PodDBAdapter.KEY_SC_START_INDEX),
- chapterCursor
- .getString(PodDBAdapter.KEY_TITLE_INDEX),
- chapterCursor
- .getString(PodDBAdapter.KEY_SC_LINK_INDEX));
+ int chapterType = chapterCursor
+ .getInt(PodDBAdapter.KEY_CHAPTER_TYPE_INDEX);
+ Chapter chapter = null;
+ long start = chapterCursor
+ .getLong(PodDBAdapter.KEY_CHAPTER_START_INDEX);
+ String title = chapterCursor
+ .getString(PodDBAdapter.KEY_TITLE_INDEX);
+ String link = chapterCursor
+ .getString(PodDBAdapter.KEY_CHAPTER_LINK_INDEX);
+
+ 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;
+ }
chapter.setId(chapterCursor
.getLong(PodDBAdapter.KEY_ID_INDEX));
- item.getSimpleChapters().add(chapter);
+ item.getChapters().add(chapter);
} while (chapterCursor.moveToNext());
}
chapterCursor.close();
diff --git a/src/de/danoeh/antennapod/feed/FeedSearcher.java b/src/de/danoeh/antennapod/feed/FeedSearcher.java
index cb98e3bee..0948af71b 100644
--- a/src/de/danoeh/antennapod/feed/FeedSearcher.java
+++ b/src/de/danoeh/antennapod/feed/FeedSearcher.java
@@ -114,8 +114,8 @@ public class FeedSearcher {
private static void searchFeedItemChaptersSingleFeed(String query,
ArrayList<SearchResult> destination, Feed feed) {
for (FeedItem item : feed.getItems()) {
- if (item.getSimpleChapters() != null) {
- for (SimpleChapter sc : item.getSimpleChapters()) {
+ if (item.getChapters() != null) {
+ for (Chapter sc : item.getChapters()) {
SearchResult result = createSearchResult(item, query, sc
.getTitle().toLowerCase(), VALUE_ITEM_CHAPTER);
if (result != null) {
diff --git a/src/de/danoeh/antennapod/feed/ID3Chapter.java b/src/de/danoeh/antennapod/feed/ID3Chapter.java
new file mode 100644
index 000000000..6dde7854e
--- /dev/null
+++ b/src/de/danoeh/antennapod/feed/ID3Chapter.java
@@ -0,0 +1,36 @@
+package de.danoeh.antennapod.feed;
+
+public class ID3Chapter extends Chapter {
+ public static final int CHAPTERTYPE_ID3CHAPTER = 2;
+
+ /**
+ * Identifies the chapter in its ID3 tag. This attribute does not have to be
+ * store in the DB and is only used for parsing.
+ */
+ private String id3ID;
+
+ public ID3Chapter(String id3ID, long start) {
+ super(start);
+ this.id3ID = id3ID;
+ }
+
+ public ID3Chapter(long start, String title, FeedItem item, String link) {
+ super(start, title, item, link);
+ }
+
+ @Override
+ public String toString() {
+ return "ID3Chapter [id3ID=" + id3ID + ", title=" + title + ", start="
+ + start + ", url=" + link + "]";
+ }
+
+ @Override
+ public int getChapterType() {
+ return CHAPTERTYPE_ID3CHAPTER;
+ }
+
+ public String getId3ID() {
+ return id3ID;
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/feed/SimpleChapter.java b/src/de/danoeh/antennapod/feed/SimpleChapter.java
index 96731d6e0..2462497ef 100644
--- a/src/de/danoeh/antennapod/feed/SimpleChapter.java
+++ b/src/de/danoeh/antennapod/feed/SimpleChapter.java
@@ -1,18 +1,10 @@
package de.danoeh.antennapod.feed;
-public class SimpleChapter extends FeedComponent {
- /** Defines starting point in milliseconds. */
- private long start;
- private String title;
- private FeedItem item;
- private String link;
-
- public SimpleChapter(FeedItem item, long start, String title, String link) {
- super();
- this.item = item;
- this.start = start;
- this.title = title;
- this.link = link;
+public class SimpleChapter extends Chapter {
+ public static final int CHAPTERTYPE_SIMPLECHAPTER = 0;
+
+ public SimpleChapter(long start, String title, FeedItem item, String link) {
+ super(start, title, item, link);
}
public String getTitle() {
@@ -35,4 +27,9 @@ public class SimpleChapter extends FeedComponent {
return link;
}
+ @Override
+ public int getChapterType() {
+ return CHAPTERTYPE_SIMPLECHAPTER;
+ }
+
}
diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java
index e59f237c9..2a425e9fe 100644
--- a/src/de/danoeh/antennapod/service/PlaybackService.java
+++ b/src/de/danoeh/antennapod/service/PlaybackService.java
@@ -38,6 +38,7 @@ import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AudioplayerActivity;
import de.danoeh.antennapod.activity.VideoplayerActivity;
+import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedManager;
@@ -761,7 +762,7 @@ public class PlaybackService extends Service {
saveCurrentPosition();
}
- public void seekToChapter(SimpleChapter chapter) {
+ public void seekToChapter(Chapter chapter) {
seek((int) chapter.getStart());
}
diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java
index 25cc1ee5a..5341c54aa 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadService.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadService.java
@@ -56,6 +56,7 @@ import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.syndication.handler.FeedHandler;
import de.danoeh.antennapod.syndication.handler.UnsupportedFeedtypeException;
+import de.danoeh.antennapod.util.ChapterUtils;
import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.InvalidFeedException;
@@ -427,7 +428,7 @@ public class DownloadService extends Service {
createReport = true;
}
successfulDownloads++;
- } else if (!status.isCancelled()){
+ } else if (!status.isCancelled()) {
if (status.getFeedFile().getClass() != FeedImage.class) {
createReport = true;
}
@@ -675,6 +676,8 @@ public class DownloadService extends Service {
@Override
public void run() {
+ boolean chaptersRead = false;
+
media.setDownloaded(true);
// Get duration
MediaPlayer mediaplayer = new MediaPlayer();
@@ -691,10 +694,21 @@ public class DownloadService extends Service {
mediaplayer.release();
}
+ if (media.getItem().getChapters() == null) {
+ ChapterUtils.readID3ChaptersFromFeedItem(media.getItem());
+ if (media.getItem().getChapters() != null) {
+ chaptersRead = true;
+ }
+ }
+
saveDownloadStatus(status);
sendDownloadHandledIntent(DOWNLOAD_TYPE_MEDIA);
- manager.setFeedMedia(DownloadService.this, media);
-
+ if (chaptersRead) {
+ manager.setFeedItem(DownloadService.this, media.getItem());
+ } else {
+ manager.setFeedMedia(DownloadService.this, media);
+ }
+
downloadsBeingHandled -= 1;
queryDownloads();
}
diff --git a/src/de/danoeh/antennapod/storage/PodDBAdapter.java b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
index 3ce4a94aa..6cb8e0940 100644
--- a/src/de/danoeh/antennapod/storage/PodDBAdapter.java
+++ b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
@@ -14,6 +14,7 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.asynctask.DownloadStatus;
+import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedImage;
import de.danoeh.antennapod.feed.FeedItem;
@@ -25,7 +26,7 @@ import de.danoeh.antennapod.feed.SimpleChapter;
* */
public class PodDBAdapter {
private static final String TAG = "PodDBAdapter";
- private static final int DATABASE_VERSION = 6;
+ private static final int DATABASE_VERSION = 7;
private static final String DATABASE_NAME = "Antennapod.db";
/** Maximum number of arguments for IN-operator. */
@@ -72,10 +73,11 @@ public class PodDBAdapter {
// --------- Queue indices
public static final int KEY_FEEDITEM_INDEX = 1;
public static final int KEY_QUEUE_FEED_INDEX = 2;
- // --------- Simplechapters indices
- public static final int KEY_SC_START_INDEX = 2;
- public static final int KEY_SC_FEEDITEM_INDEX = 3;
- public static final int KEY_SC_LINK_INDEX = 4;
+ // --------- Chapters indices
+ public static final int KEY_CHAPTER_START_INDEX = 2;
+ public static final int KEY_CHAPTER_FEEDITEM_INDEX = 3;
+ public static final int KEY_CHAPTER_LINK_INDEX = 4;
+ public static final int KEY_CHAPTER_TYPE_INDEX = 5;
// Key-constants
public static final String KEY_ID = "id";
@@ -107,12 +109,13 @@ public class PodDBAdapter {
public static final String KEY_START = "start";
public static final String KEY_LANGUAGE = "language";
public static final String KEY_AUTHOR = "author";
- public static final String KEY_HAS_SIMPLECHAPTERS = "has_simple_chapters";
+ public static final String KEY_HAS_CHAPTERS = "has_simple_chapters";
public static final String KEY_TYPE = "type";
public static final String KEY_ITEM_IDENTIFIER = "item_identifier";
public static final String KEY_FEED_IDENTIFIER = "feed_identifier";
public static final String KEY_REASON_DETAILED = "reason_detailed";
public static final String KEY_DOWNLOADSTATUS_TITLE = "title";
+ public static final String KEY_CHAPTER_TYPE = "type";
// Table names
public static final String TABLE_NAME_FEEDS = "Feeds";
@@ -142,7 +145,7 @@ public class PodDBAdapter {
+ " INTEGER," + KEY_READ + " INTEGER," + KEY_LINK + " TEXT,"
+ KEY_DESCRIPTION + " TEXT," + KEY_PAYMENT_LINK + " TEXT,"
+ KEY_MEDIA + " INTEGER," + KEY_FEED + " INTEGER,"
- + KEY_HAS_SIMPLECHAPTERS + " INTEGER," + KEY_ITEM_IDENTIFIER
+ + KEY_HAS_CHAPTERS + " INTEGER," + KEY_ITEM_IDENTIFIER
+ " TEXT)";
private static final String CREATE_TABLE_FEED_IMAGES = "CREATE TABLE "
@@ -170,7 +173,7 @@ public class PodDBAdapter {
private static final String CREATE_TABLE_SIMPLECHAPTERS = "CREATE TABLE "
+ TABLE_NAME_SIMPLECHAPTERS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
+ " TEXT," + KEY_START + " INTEGER," + KEY_FEEDITEM + " INTEGER,"
- + KEY_LINK + " TEXT)";
+ + KEY_LINK + " TEXT," + KEY_CHAPTER_TYPE + " INTEGER)";
private SQLiteDatabase db;
private final Context context;
@@ -330,7 +333,7 @@ public class PodDBAdapter {
}
values.put(KEY_FEED, item.getFeed().getId());
values.put(KEY_READ, item.isRead());
- values.put(KEY_HAS_SIMPLECHAPTERS, item.getSimpleChapters() != null);
+ values.put(KEY_HAS_CHAPTERS, item.getChapters() != null);
values.put(KEY_ITEM_IDENTIFIER, item.getItemIdentifier());
if (item.getId() == 0) {
item.setId(db.insert(TABLE_NAME_FEED_ITEMS, null, values));
@@ -338,19 +341,20 @@ public class PodDBAdapter {
db.update(TABLE_NAME_FEED_ITEMS, values, KEY_ID + "=?",
new String[] { String.valueOf(item.getId()) });
}
- if (item.getSimpleChapters() != null) {
- setSimpleChapters(item);
+ if (item.getChapters() != null) {
+ setChapters(item);
}
return item.getId();
}
- public void setSimpleChapters(FeedItem item) {
+ public void setChapters(FeedItem item) {
ContentValues values = new ContentValues();
- for (SimpleChapter chapter : item.getSimpleChapters()) {
+ for (Chapter chapter : item.getChapters()) {
values.put(KEY_TITLE, chapter.getTitle());
values.put(KEY_START, chapter.getStart());
values.put(KEY_FEEDITEM, item.getId());
values.put(KEY_LINK, chapter.getLink());
+ values.put(KEY_CHAPTER_TYPE, chapter.getChapterType());
if (chapter.getId() == 0) {
chapter.setId(db
.insert(TABLE_NAME_SIMPLECHAPTERS, null, values));
@@ -670,6 +674,10 @@ public class PodDBAdapter {
db.execSQL("ALTER TABLE " + TABLE_NAME_DOWNLOAD_LOG
+ " ADD COLUMN " + KEY_DOWNLOADSTATUS_TITLE + " TEXT");
}
+ if (oldVersion <= 6) {
+ db.execSQL("ALTER TABLE " + TABLE_NAME_SIMPLECHAPTERS
+ + " ADD COLUMN " + KEY_CHAPTER_TYPE + " INTEGER");
+ }
}
}
diff --git a/src/de/danoeh/antennapod/syndication/namespace/simplechapters/NSSimpleChapters.java b/src/de/danoeh/antennapod/syndication/namespace/simplechapters/NSSimpleChapters.java
index f1fecf513..f6918231c 100644
--- a/src/de/danoeh/antennapod/syndication/namespace/simplechapters/NSSimpleChapters.java
+++ b/src/de/danoeh/antennapod/syndication/namespace/simplechapters/NSSimpleChapters.java
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import org.xml.sax.Attributes;
+import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.SimpleChapter;
import de.danoeh.antennapod.syndication.handler.HandlerState;
import de.danoeh.antennapod.syndication.namespace.Namespace;
@@ -24,15 +25,14 @@ public class NSSimpleChapters extends Namespace {
public SyndElement handleElementStart(String localName, HandlerState state,
Attributes attributes) {
if (localName.equals(CHAPTERS)) {
- state.getCurrentItem().setSimpleChapters(
- new ArrayList<SimpleChapter>());
+ state.getCurrentItem().setChapters(new ArrayList<Chapter>());
} else if (localName.equals(CHAPTER)) {
state.getCurrentItem()
- .getSimpleChapters()
- .add(new SimpleChapter(state.getCurrentItem(),
- SyndDateUtils.parseTimeString(attributes
- .getValue(START)), attributes
- .getValue(TITLE), attributes.getValue(HREF)));
+ .getChapters()
+ .add(new SimpleChapter(SyndDateUtils
+ .parseTimeString(attributes.getValue(START)),
+ attributes.getValue(TITLE), state.getCurrentItem(),
+ attributes.getValue(HREF)));
}
return new SyndElement(localName, this);
diff --git a/src/de/danoeh/antennapod/util/ChapterUtils.java b/src/de/danoeh/antennapod/util/ChapterUtils.java
new file mode 100644
index 000000000..dd9dbce5a
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/ChapterUtils.java
@@ -0,0 +1,97 @@
+package de.danoeh.antennapod.util;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import android.util.Log;
+import de.danoeh.antennapod.AppConfig;
+import de.danoeh.antennapod.feed.Chapter;
+import de.danoeh.antennapod.feed.FeedItem;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.util.comparator.ChapterStartTimeComparator;
+import de.danoeh.antennapod.util.id3reader.ChapterReader;
+import de.danoeh.antennapod.util.id3reader.ID3ReaderException;
+
+/** Utility class for getting chapter data from media files. */
+public class ChapterUtils {
+ private static final String TAG = "ChapterUtils";
+
+ private ChapterUtils() {
+ }
+
+ public static void readID3ChaptersFromFeedItem(FeedItem item) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Reading id3 chapters from item " + item.getTitle());
+ final FeedMedia media = item.getMedia();
+ if (media != null && media.isDownloaded()
+ && media.getFile_url() != null) {
+ File source = new File(media.getFile_url());
+ if (source.exists()) {
+ ChapterReader reader = new ChapterReader();
+ InputStream in = null;
+
+ try {
+ in = new BufferedInputStream(new FileInputStream(source));
+ reader.readInputStream(in);
+ List<Chapter> chapters = reader.getChapters();
+
+ if (chapters != null) {
+ Collections.sort(chapters, new ChapterStartTimeComparator());
+ processChapters(chapters, item);
+ if (chaptersValid(chapters)) {
+ item.setChapters(chapters);
+ } else {
+ Log.e(TAG, "Chapter data was invalid");
+ }
+ } else {
+ Log.i(TAG, "ChapterReader could not find any ID3 chapters");
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (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");
+ }
+ }
+ }
+
+ /** Makes sure that chapter does a title and an item attribute. */
+ private static void processChapters(List<Chapter> chapters, FeedItem item) {
+ for (int i = 0; i < chapters.size(); i++) {
+ Chapter c = chapters.get(i);
+ if (c.getTitle() == null) {
+ c.setTitle(Integer.toString(i));
+ }
+ c.setItem(item);
+ }
+ }
+
+ private static boolean chaptersValid(List<Chapter> chapters) {
+ for (Chapter c : chapters) {
+ if (c.getTitle() == null) {
+ return false;
+ }
+ if (c.getStart() < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/PlaybackController.java b/src/de/danoeh/antennapod/util/PlaybackController.java
index 38b5201e7..3e0ffb2ad 100644
--- a/src/de/danoeh/antennapod/util/PlaybackController.java
+++ b/src/de/danoeh/antennapod/util/PlaybackController.java
@@ -26,6 +26,7 @@ import android.widget.TextView;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.feed.SimpleChapter;
import de.danoeh.antennapod.service.PlaybackService;
@@ -552,7 +553,7 @@ public abstract class PlaybackController {
}
}
- public void seekToChapter(SimpleChapter chapter) {
+ public void seekToChapter(Chapter chapter) {
if (playbackService != null) {
playbackService.seekToChapter(chapter);
}
diff --git a/src/de/danoeh/antennapod/util/comparator/ChapterStartTimeComparator.java b/src/de/danoeh/antennapod/util/comparator/ChapterStartTimeComparator.java
new file mode 100644
index 000000000..7cc6fa458
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/comparator/ChapterStartTimeComparator.java
@@ -0,0 +1,20 @@
+package de.danoeh.antennapod.util.comparator;
+
+import java.util.Comparator;
+
+import de.danoeh.antennapod.feed.Chapter;
+
+public class ChapterStartTimeComparator implements Comparator<Chapter> {
+
+ @Override
+ public int compare(Chapter lhs, Chapter rhs) {
+ if (lhs.getStart() == rhs.getStart()) {
+ return 0;
+ } else if (lhs.getStart() < rhs.getStart()) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/ChapterReader.java b/src/de/danoeh/antennapod/util/id3reader/ChapterReader.java
new file mode 100644
index 000000000..9a86de15d
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/ChapterReader.java
@@ -0,0 +1,95 @@
+package de.danoeh.antennapod.util.id3reader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import de.danoeh.antennapod.feed.Chapter;
+import de.danoeh.antennapod.feed.ID3Chapter;
+import de.danoeh.antennapod.util.id3reader.model.FrameHeader;
+import de.danoeh.antennapod.util.id3reader.model.TagHeader;
+
+public class ChapterReader extends ID3Reader {
+
+ private static final String FRAME_ID_CHAPTER = "CHAP";
+ private static final String FRAME_ID_TITLE = "TIT2";
+
+ private List<Chapter> chapters;
+ private ID3Chapter currentChapter;
+
+ @Override
+ public int onStartTagHeader(TagHeader header) {
+ chapters = new ArrayList<Chapter>();
+ System.out.println(header.toString());
+ return ID3Reader.ACTION_DONT_SKIP;
+ }
+
+ @Override
+ public int onStartFrameHeader(FrameHeader header, InputStream input)
+ throws IOException, ID3ReaderException {
+ System.out.println(header.toString());
+ if (header.getId().equals(FRAME_ID_CHAPTER)) {
+ if (currentChapter != null) {
+ if (!hasId3Chapter(currentChapter)) {
+ chapters.add(currentChapter);
+ currentChapter = null;
+ }
+ }
+ System.out.println("Found chapter");
+ String elementId = readString(input, Integer.MAX_VALUE);
+ char[] startTimeSource = readBytes(input, 4);
+ long startTime = ((int) startTimeSource[0] << 24)
+ | ((int) startTimeSource[1] << 16)
+ | ((int) startTimeSource[2] << 8) | startTimeSource[3];
+ currentChapter = new ID3Chapter(elementId, startTime);
+ skipBytes(input, 12);
+ return ID3Reader.ACTION_DONT_SKIP;
+ } else if (header.getId().equals(FRAME_ID_TITLE)) {
+ if (currentChapter != null && currentChapter.getTitle() == null) {
+ System.out.println("Found title");
+ skipBytes(input, 1);
+ currentChapter
+ .setTitle(readString(input, header.getSize() - 1));
+ return ID3Reader.ACTION_DONT_SKIP;
+ }
+ }
+
+ return super.onStartFrameHeader(header, input);
+ }
+
+ private boolean hasId3Chapter(ID3Chapter chapter) {
+ for (Chapter c : chapters) {
+ if (((ID3Chapter) c).getId3ID().equals(chapter.getId3ID())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onEndTag() {
+ if (currentChapter != null) {
+ if (!hasId3Chapter(currentChapter)) {
+ chapters.add(currentChapter);
+ }
+ }
+ System.out.println("Reached end of tag");
+ if (chapters != null) {
+ for (Chapter c : chapters) {
+ System.out.println(c.toString());
+ }
+ }
+ }
+
+ @Override
+ public void onNoTagHeaderFound() {
+ System.out.println("No tag header found");
+ super.onNoTagHeaderFound();
+ }
+
+ public List<Chapter> getChapters() {
+ return chapters;
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java b/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java
new file mode 100644
index 000000000..8164170bf
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java
@@ -0,0 +1,182 @@
+package de.danoeh.antennapod.util.id3reader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+import de.danoeh.antennapod.util.id3reader.model.FrameHeader;
+import de.danoeh.antennapod.util.id3reader.model.TagHeader;
+
+/**
+ * Reads the ID3 Tag of a given file. In order to use this class, you should
+ * create a subclass of it and overwrite the onStart* - or onEnd* - methods.
+ */
+public class ID3Reader {
+ private static final int HEADER_LENGTH = 10;
+ private static final int ID3_LENGTH = 3;
+ private static final int FRAME_ID_LENGTH = 4;
+
+ protected static final int ACTION_SKIP = 1;
+ protected static final int ACTION_DONT_SKIP = 2;
+
+ protected int readerPosition;
+
+ private static final char[] LITTLE_ENDIAN_BOM = { 0xff, 0xfe };
+ private static final char[] BIG_ENDIAN_BOM = { 0xfe, 0xff };
+
+ public ID3Reader() {
+ }
+
+ public final void readInputStream(InputStream input) throws IOException,
+ ID3ReaderException {
+ int rc;
+ readerPosition = 0;
+ char[] tagHeaderSource = readBytes(input, HEADER_LENGTH);
+ TagHeader tagHeader = createTagHeader(tagHeaderSource);
+ if (tagHeader == null) {
+ onNoTagHeaderFound();
+ } else {
+ rc = onStartTagHeader(tagHeader);
+ if (rc == ACTION_SKIP) {
+ onEndTag();
+ } else {
+ while (readerPosition < tagHeader.getSize()) {
+ FrameHeader frameHeader = createFrameHeader(readBytes(
+ input, HEADER_LENGTH));
+ rc = onStartFrameHeader(frameHeader, input);
+ if (rc == ACTION_SKIP) {
+ skipBytes(input, frameHeader.getSize());
+ }
+ }
+ onEndTag();
+ }
+ }
+ }
+
+ /**
+ * Read a certain number of bytes from the given input stream. This method
+ * changes the readerPosition-attribute.
+ */
+ protected char[] readBytes(InputStream input, int number)
+ throws IOException, ID3ReaderException {
+ char[] header = new char[number];
+ for (int i = 0; i < number; i++) {
+ int b = input.read();
+ readerPosition++;
+ if (b != -1) {
+ header[i] = (char) b;
+ } else {
+ throw new ID3ReaderException("Unexpected end of stream");
+ }
+ }
+ return header;
+ }
+
+ /**
+ * Skip a certain number of bytes on the given input stream. This method
+ * changes the readerPosition-attribute.
+ */
+ protected void skipBytes(InputStream input, int number) throws IOException {
+ int skipped = 0;
+ while (skipped < number) {
+ skipped += input.skip(number - skipped);
+ System.out.println("Skipped = " + skipped);
+ }
+
+ readerPosition += number;
+ }
+
+ private TagHeader createTagHeader(char[] source) throws ID3ReaderException {
+ boolean hasTag = (source[0] == 0x49) && (source[1] == 0x44)
+ && (source[2] == 0x33);
+ if (source.length != HEADER_LENGTH) {
+ throw new ID3ReaderException("Length of header must be "
+ + HEADER_LENGTH);
+ }
+ if (hasTag) {
+ String id = null;
+ id = new String(source, 0, ID3_LENGTH);
+ char version = (char) ((source[3] << 8) | source[4]);
+ byte flags = (byte) source[5];
+ int size = (source[6] << 24) | (source[7] << 16) | (source[8] << 8)
+ | source[9];
+ return new TagHeader(id, size, version, flags);
+ } else {
+ return null;
+ }
+ }
+
+ private FrameHeader createFrameHeader(char[] source)
+ throws ID3ReaderException {
+ if (source.length != HEADER_LENGTH) {
+ throw new ID3ReaderException("Length of header must be "
+ + HEADER_LENGTH);
+ }
+ String id = null;
+ id = new String(source, 0, FRAME_ID_LENGTH);
+ int size = (((int) source[4]) << 24) | (((int) source[5]) << 16)
+ | (((int) source[6]) << 8) | source[7];
+ char flags = (char) ((source[8] << 8) | source[9]);
+ return new FrameHeader(id, size, flags);
+ }
+
+ protected String readString(InputStream input, int max) throws IOException,
+ ID3ReaderException {
+ char[] bom = readBytes(input, 2);
+ if (bom == LITTLE_ENDIAN_BOM || bom == BIG_ENDIAN_BOM) {
+ return readUnicodeString(input, bom, max);
+ } else {
+ PushbackInputStream pi = new PushbackInputStream(input, 2);
+ pi.unread(bom[1]);
+ pi.unread(bom[0]);
+ return readISOString(pi, max);
+ }
+ }
+
+ private String readISOString(InputStream input, int max)
+ throws IOException, ID3ReaderException {
+ int bytesRead = 0;
+ StringBuilder builder = new StringBuilder();
+ char c;
+ while (++bytesRead <= max && (c = (char) input.read()) > 0) {
+ builder.append(c);
+ }
+ return builder.toString();
+ }
+
+ private String readUnicodeString(InputStream input, char[] bom, int max)
+ throws IOException, ID3ReaderException {
+ StringBuffer builder = new StringBuffer();
+ char c1 = (char) input.read();
+ char c2 = (char) input.read();
+ int bytesRead = 2;
+ while ((c1 > 0 && c2 > 0) && ++bytesRead <= max) {
+
+ builder.append(c1);
+ c1 = c2;
+ c2 = (char) input.read();
+ }
+ if (bom == LITTLE_ENDIAN_BOM) {
+ builder.reverse();
+ }
+ return builder.toString();
+ }
+
+ public int onStartTagHeader(TagHeader header) {
+ return ACTION_SKIP;
+ }
+
+ public int onStartFrameHeader(FrameHeader header, InputStream input)
+ throws IOException, ID3ReaderException {
+ return ACTION_SKIP;
+ }
+
+ public void onEndTag() {
+
+ }
+
+ public void onNoTagHeaderFound() {
+
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/ID3ReaderException.java b/src/de/danoeh/antennapod/util/id3reader/ID3ReaderException.java
new file mode 100644
index 000000000..c458540ee
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/ID3ReaderException.java
@@ -0,0 +1,20 @@
+package de.danoeh.antennapod.util.id3reader;
+
+public class ID3ReaderException extends Exception {
+
+ public ID3ReaderException() {
+ }
+
+ public ID3ReaderException(String arg0) {
+ super(arg0);
+ }
+
+ public ID3ReaderException(Throwable arg0) {
+ super(arg0);
+ }
+
+ public ID3ReaderException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java b/src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java
new file mode 100644
index 000000000..2c0d8e5ba
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java
@@ -0,0 +1,18 @@
+package de.danoeh.antennapod.util.id3reader.model;
+
+public class FrameHeader extends Header {
+
+ protected char flags;
+
+ public FrameHeader(String id, int size, char flags) {
+ super(id, size);
+ this.flags = flags;
+ }
+
+ @Override
+ public String toString() {
+ return "FrameHeader [flags=" + Integer.toString(flags) + ", id=" + id + ", size=" + size
+ + "]";
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/model/Header.java b/src/de/danoeh/antennapod/util/id3reader/model/Header.java
new file mode 100644
index 000000000..22d5b6376
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/model/Header.java
@@ -0,0 +1,29 @@
+package de.danoeh.antennapod.util.id3reader.model;
+
+public abstract class Header {
+
+ protected String id;
+ protected int size;
+
+ public Header(String id, int size) {
+ super();
+ this.id = id;
+ this.size = size;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ @Override
+ public String toString() {
+ return "Header [id=" + id + ", size=" + size + "]";
+ }
+
+
+
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/model/TagHeader.java b/src/de/danoeh/antennapod/util/id3reader/model/TagHeader.java
new file mode 100644
index 000000000..ec99ef14e
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/model/TagHeader.java
@@ -0,0 +1,26 @@
+package de.danoeh.antennapod.util.id3reader.model;
+
+public class TagHeader extends Header {
+
+ protected char version;
+ protected byte flags;
+
+ public TagHeader(String id, int size, char version, byte flags) {
+ super(id, size);
+ this.version = version;
+ this.flags = flags;
+ }
+
+ @Override
+ public String toString() {
+ return "TagHeader [version=" + version + ", flags=" + flags + ", id="
+ + id + ", size=" + size + "]";
+ }
+
+ public char getVersion() {
+ return version;
+ }
+
+
+
+}