summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod/util
diff options
context:
space:
mode:
authordaniel oeh <daniel.oeh@gmail.com>2013-06-25 19:27:26 +0200
committerdaniel oeh <daniel.oeh@gmail.com>2013-06-25 19:27:26 +0200
commit62961d6594687eb34e36adaeba55a90c99e2dd4e (patch)
tree12ab392df2803ec3d0a5526ac132f7926252f2aa /src/de/danoeh/antennapod/util
parent4707139def5500f1039de669e53822d47855008c (diff)
parentddf77913bc7b0c4549ab1ea4b4aa462bf80ac406 (diff)
downloadAntennaPod-62961d6594687eb34e36adaeba55a90c99e2dd4e.zip
Merge branch 'develop'
Diffstat (limited to 'src/de/danoeh/antennapod/util')
-rw-r--r--src/de/danoeh/antennapod/util/Converter.java1
-rw-r--r--src/de/danoeh/antennapod/util/UndoBarController.java135
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/ChapterReader.java36
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/ID3Reader.java89
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java5
-rw-r--r--src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java2
6 files changed, 236 insertions, 32 deletions
diff --git a/src/de/danoeh/antennapod/util/Converter.java b/src/de/danoeh/antennapod/util/Converter.java
index f02e8ea69..6ef47af31 100644
--- a/src/de/danoeh/antennapod/util/Converter.java
+++ b/src/de/danoeh/antennapod/util/Converter.java
@@ -78,4 +78,5 @@ public final class Converter {
return String.format("%02d:%02d", h, m);
}
+
}
diff --git a/src/de/danoeh/antennapod/util/UndoBarController.java b/src/de/danoeh/antennapod/util/UndoBarController.java
new file mode 100644
index 000000000..e726717a1
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/UndoBarController.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012 Roman Nurik
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.danoeh.antennapod.util;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.widget.TextView;
+
+import de.danoeh.antennapod.R;
+
+public class UndoBarController {
+ private View mBarView;
+ private TextView mMessageView;
+ private ViewPropertyAnimator mBarAnimator;
+ private Handler mHideHandler = new Handler();
+
+ private UndoListener mUndoListener;
+
+ // State objects
+ private Parcelable mUndoToken;
+ private CharSequence mUndoMessage;
+
+ public interface UndoListener {
+ void onUndo(Parcelable token);
+ }
+
+ public UndoBarController(View undoBarView, UndoListener undoListener) {
+ mBarView = undoBarView;
+ mBarAnimator = mBarView.animate();
+ mUndoListener = undoListener;
+
+ mMessageView = (TextView) mBarView.findViewById(R.id.undobar_message);
+ mBarView.findViewById(R.id.undobar_button)
+ .setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ hideUndoBar(false);
+ mUndoListener.onUndo(mUndoToken);
+ }
+ });
+
+ hideUndoBar(true);
+ }
+
+ public void showUndoBar(boolean immediate, CharSequence message, Parcelable undoToken) {
+ mUndoToken = undoToken;
+ mUndoMessage = message;
+ mMessageView.setText(mUndoMessage);
+
+ mHideHandler.removeCallbacks(mHideRunnable);
+ mHideHandler.postDelayed(mHideRunnable,
+ mBarView.getResources().getInteger(R.integer.undobar_hide_delay));
+
+ mBarView.setVisibility(View.VISIBLE);
+ if (immediate) {
+ mBarView.setAlpha(1);
+ } else {
+ mBarAnimator.cancel();
+ mBarAnimator
+ .alpha(1)
+ .setDuration(
+ mBarView.getResources()
+ .getInteger(android.R.integer.config_shortAnimTime))
+ .setListener(null);
+ }
+ }
+
+ public void hideUndoBar(boolean immediate) {
+ mHideHandler.removeCallbacks(mHideRunnable);
+ if (immediate) {
+ mBarView.setVisibility(View.GONE);
+ mBarView.setAlpha(0);
+ mUndoMessage = null;
+ mUndoToken = null;
+
+ } else {
+ mBarAnimator.cancel();
+ mBarAnimator
+ .alpha(0)
+ .setDuration(mBarView.getResources()
+ .getInteger(android.R.integer.config_shortAnimTime))
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBarView.setVisibility(View.GONE);
+ mUndoMessage = null;
+ mUndoToken = null;
+ }
+ });
+ }
+ }
+
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putCharSequence("undo_message", mUndoMessage);
+ outState.putParcelable("undo_token", mUndoToken);
+ }
+
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ if (savedInstanceState != null) {
+ mUndoMessage = savedInstanceState.getCharSequence("undo_message");
+ mUndoToken = savedInstanceState.getParcelable("undo_token");
+
+ if (mUndoToken != null || !TextUtils.isEmpty(mUndoMessage)) {
+ showUndoBar(true, mUndoMessage, mUndoToken);
+ }
+ }
+ }
+
+ private Runnable mHideRunnable = new Runnable() {
+ @Override
+ public void run() {
+ hideUndoBar(false);
+ }
+ };
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/ChapterReader.java b/src/de/danoeh/antennapod/util/id3reader/ChapterReader.java
index e1cafe85d..f897f886c 100644
--- a/src/de/danoeh/antennapod/util/id3reader/ChapterReader.java
+++ b/src/de/danoeh/antennapod/util/id3reader/ChapterReader.java
@@ -2,18 +2,24 @@ package de.danoeh.antennapod.util.id3reader;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
+import android.util.Log;
+
+import de.danoeh.antennapod.AppConfig;
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 TAG = "ID3ChapterReader";
private static final String FRAME_ID_CHAPTER = "CHAP";
private static final String FRAME_ID_TITLE = "TIT2";
+ private static final String FRAME_ID_LINK = "WXXX";
private List<Chapter> chapters;
private ID3Chapter currentChapter;
@@ -33,27 +39,45 @@ public class ChapterReader extends ID3Reader {
if (currentChapter != null) {
if (!hasId3Chapter(currentChapter)) {
chapters.add(currentChapter);
- System.out.println("Found chapter: " + currentChapter);
+ if (AppConfig.DEBUG) Log.d(TAG, "Found chapter: " + currentChapter);
currentChapter = null;
}
}
- String elementId = readISOString(input, Integer.MAX_VALUE);
+ StringBuffer elementId = new StringBuffer();
+ readISOString(elementId, 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);
+ currentChapter = new ID3Chapter(elementId.toString(), startTime);
skipBytes(input, 12);
return ID3Reader.ACTION_DONT_SKIP;
} else if (header.getId().equals(FRAME_ID_TITLE)) {
if (currentChapter != null && currentChapter.getTitle() == null) {
+ StringBuffer title = new StringBuffer();
+ readString(title, input, header.getSize());
currentChapter
- .setTitle(readString(input, header.getSize()));
- System.out.println("Found title: " + currentChapter.getTitle());
+ .setTitle(title.toString());
+ if (AppConfig.DEBUG) Log.d(TAG, "Found title: " + currentChapter.getTitle());
return ID3Reader.ACTION_DONT_SKIP;
}
- }
+ } else if (header.getId().equals(FRAME_ID_LINK)) {
+ if (currentChapter != null) {
+ // skip description
+ int descriptionLength = readString(null, input, header.getSize());
+ StringBuffer link = new StringBuffer();
+ readISOString(link, input, header.getSize() - descriptionLength);
+ String decodedLink = URLDecoder.decode(link.toString(), "UTF-8");
+
+ currentChapter.setLink(decodedLink);
+
+ if (AppConfig.DEBUG) Log.d(TAG, "Found link: " + currentChapter.getLink());
+ return ID3Reader.ACTION_DONT_SKIP;
+ }
+ } else if (header.getId().equals("APIC")) {
+ Log.d(TAG, header.toString());
+ }
return super.onStartFrameHeader(header, input);
}
diff --git a/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java b/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java
index dff6d77e8..92f817363 100644
--- a/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java
+++ b/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java
@@ -24,7 +24,11 @@ public class ID3Reader {
protected int readerPosition;
- private static final byte ENCODING_UNICODE = 1;
+ private static final byte ENCODING_UTF16_WITH_BOM = 1;
+ private static final byte ENCODING_UTF16_WITHOUT_BOM = 2;
+ private static final byte ENCODING_UTF8 = 3;
+
+ private TagHeader tagHeader;
public ID3Reader() {
}
@@ -34,7 +38,7 @@ public class ID3Reader {
int rc;
readerPosition = 0;
char[] tagHeaderSource = readBytes(input, HEADER_LENGTH);
- TagHeader tagHeader = createTagHeader(tagHeaderSource);
+ tagHeader = createTagHeader(tagHeaderSource);
if (tagHeader == null) {
onNoTagHeaderFound();
} else {
@@ -124,12 +128,12 @@ public class ID3Reader {
+ HEADER_LENGTH);
}
if (hasTag) {
- String id = null;
- id = new String(source, 0, ID3_LENGTH);
+ String 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];
+ size = unsynchsafe(size);
return new TagHeader(id, size, version, flags);
} else {
return null;
@@ -142,48 +146,89 @@ public class ID3Reader {
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];
+ String id = new String(source, 0, FRAME_ID_LENGTH);
+
+ int size = (((int) source[4]) << 24) | (((int) source[5]) << 16)
+ | (((int) source[6]) << 8) | source[7];
+ if (tagHeader != null && tagHeader.getVersion() >= 0x0400) {
+ size = unsynchsafe(size);
+ }
char flags = (char) ((source[8] << 8) | source[9]);
return new FrameHeader(id, size, flags);
}
- protected String readString(InputStream input, int max) throws IOException,
+ private int unsynchsafe(int in) {
+ int out = 0;
+ int mask = 0x7F000000;
+
+ while (mask != 0) {
+ out >>= 1;
+ out |= in & mask;
+ mask >>= 8;
+ }
+
+ return out;
+ }
+
+ protected int readString(StringBuffer buffer, InputStream input, int max) throws IOException,
ID3ReaderException {
if (max > 0) {
char[] encoding = readBytes(input, 1);
max--;
- if (encoding[0] == ENCODING_UNICODE) {
- return readUnicodeString(input, max);
- } else {
- return readISOString(input, max);
+ if (encoding[0] == ENCODING_UTF16_WITH_BOM || encoding[0] == ENCODING_UTF16_WITHOUT_BOM) {
+ return readUnicodeString(buffer, input, max, Charset.forName("UTF-16")) + 1; // take encoding byte into account
+ } else if (encoding[0] == ENCODING_UTF8) {
+ return readUnicodeString(buffer, input, max, Charset.forName("UTF-8")) + 1; // take encoding byte into account
+ } else {
+ return readISOString(buffer, input, max) + 1; // take encoding byte into account
}
} else {
- return "";
+ if (buffer != null) {
+ buffer.append("");
+ }
+ return 0;
}
}
- protected String readISOString(InputStream input, int max)
+ protected int readISOString(StringBuffer buffer, 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);
+ if (buffer != null) {
+ buffer.append(c);
+ }
}
- return builder.toString();
+ return bytesRead;
}
- private String readUnicodeString(InputStream input, int max)
+ private int readUnicodeString(StringBuffer strBuffer, InputStream input, int max, Charset charset)
throws IOException, ID3ReaderException {
byte[] buffer = new byte[max];
- IOUtils.readFully(input, buffer);
- Charset charset = Charset.forName("UTF-16");
- return charset.newDecoder().decode(ByteBuffer.wrap(buffer)).toString();
+ int c, cZero = -1;
+ int i = 0;
+ for (; i < max; i++) {
+ c = input.read();
+ if (c == -1) {
+ break;
+ } else if (c == 0) {
+ if (cZero == 0) {
+ // termination character found
+ break;
+ } else {
+ cZero = 0;
+ }
+ } else {
+ buffer[i] = (byte) c;
+ cZero = -1;
+ }
+ }
+ if (strBuffer != null) {
+ strBuffer.append(charset.newDecoder().decode(ByteBuffer.wrap(buffer)).toString());
+ }
+ return i;
}
public int onStartTagHeader(TagHeader header) {
diff --git a/src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java b/src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java
index 2c0d8e5ba..df73393a5 100644
--- a/src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java
+++ b/src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java
@@ -11,8 +11,7 @@ public class FrameHeader extends Header {
@Override
public String toString() {
- return "FrameHeader [flags=" + Integer.toString(flags) + ", id=" + id + ", size=" + size
- + "]";
- }
+ return String.format("FrameHeader [flags=%s, id=%s, size=%s]", Integer.toBinaryString(flags), id, Integer.toBinaryString(size));
+ }
}
diff --git a/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java b/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
index 64bcb1cf2..472124bf7 100644
--- a/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
+++ b/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
@@ -140,7 +140,7 @@ public class FeedItemMenuHandler {
manager.addQueueItem(context, selectedItem);
break;
case R.id.remove_from_queue_item:
- manager.removeQueueItem(context, selectedItem);
+ manager.removeQueueItem(context, selectedItem, true);
break;
case R.id.stream_item:
manager.playMedia(context, selectedItem.getMedia(), true, true,