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/AddFeedActivity.java17
-rw-r--r--src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java11
-rw-r--r--src/de/danoeh/antennapod/activity/PreferenceActivity.java48
-rw-r--r--src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java3
-rw-r--r--src/de/danoeh/antennapod/miroguide/conn/MiroGuideConnector.java2
-rw-r--r--src/de/danoeh/antennapod/service/PlaybackService.java1796
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadService.java4
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadStatus.java121
-rw-r--r--src/de/danoeh/antennapod/service/download/Downloader.java1
-rw-r--r--src/de/danoeh/antennapod/service/download/HttpDownloader.java10
-rw-r--r--src/de/danoeh/antennapod/storage/DBReader.java3
-rw-r--r--src/de/danoeh/antennapod/storage/PodDBAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/syndication/namespace/NSRSS20.java15
-rw-r--r--src/de/danoeh/antennapod/util/ConnectionTester.java6
-rw-r--r--src/de/danoeh/antennapod/util/DownloadError.java86
-rw-r--r--src/de/danoeh/antennapod/util/UndoBarController.java14
-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/playback/PlaybackController.java4
20 files changed, 1190 insertions, 1083 deletions
diff --git a/src/de/danoeh/antennapod/activity/AddFeedActivity.java b/src/de/danoeh/antennapod/activity/AddFeedActivity.java
index 39434fa87..545b53494 100644
--- a/src/de/danoeh/antennapod/activity/AddFeedActivity.java
+++ b/src/de/danoeh/antennapod/activity/AddFeedActivity.java
@@ -2,6 +2,8 @@ package de.danoeh.antennapod.activity;
import java.util.Date;
+import org.apache.commons.lang3.StringUtils;
+
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
@@ -44,6 +46,9 @@ public class AddFeedActivity extends SherlockActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Was started with Intent " + getIntent().getAction()
+ + " and Data " + getIntent().getDataString());
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@@ -54,6 +59,10 @@ public class AddFeedActivity extends SherlockActivity {
progDialog = new ProgressDialog(this);
etxtFeedurl = (EditText) findViewById(R.id.etxtFeedurl);
+ if (StringUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
+ etxtFeedurl.setText(getIntent().getDataString());
+ }
+
butBrowseMiroGuide = (Button) findViewById(R.id.butBrowseMiroguide);
butOpmlImport = (Button) findViewById(R.id.butOpmlImport);
butConfirm = (Button) findViewById(R.id.butConfirm);
@@ -101,7 +110,7 @@ public class AddFeedActivity extends SherlockActivity {
if (intent.getAction() != null
&& intent.getAction().equals(Intent.ACTION_SEND)) {
if (AppConfig.DEBUG)
- Log.d(TAG, "Was started with ACTION_SEND intent");
+ Log.d(TAG, "Resuming with ACTION_SEND intent");
String text = intent.getStringExtra(Intent.EXTRA_TEXT);
if (text != null) {
etxtFeedurl.setText(text);
@@ -152,7 +161,7 @@ public class AddFeedActivity extends SherlockActivity {
}
@Override
- public void onConnectionFailure(int reason) {
+ public void onConnectionFailure(DownloadError reason) {
handleDownloadError(reason);
}
});
@@ -168,11 +177,11 @@ public class AddFeedActivity extends SherlockActivity {
progDialog.setMessage(getString(R.string.loading_label));
}
- private void handleDownloadError(int reason) {
+ private void handleDownloadError(DownloadError reason) {
final AlertDialog errorDialog = new AlertDialog.Builder(this).create();
errorDialog.setTitle(R.string.error_label);
errorDialog.setMessage(getString(R.string.error_msg_prefix) + " "
- + DownloadError.getErrorString(this, reason));
+ + reason.getErrorString(this));
errorDialog.setButton(getString(android.R.string.ok),
new DialogInterface.OnClickListener() {
@Override
diff --git a/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index 50ff38e22..ce58babc0 100644
--- a/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -89,9 +89,8 @@ public abstract class OnlineFeedViewActivity extends SherlockFragmentActivity {
if (status.isSuccessful()) {
parseFeed();
} else {
- String errorMsg = DownloadError.getErrorString(
- OnlineFeedViewActivity.this,
- status.getReason());
+ String errorMsg = status.getReason().getErrorString(
+ OnlineFeedViewActivity.this);
if (errorMsg != null
&& status.getReasonDetailed() != null) {
errorMsg += " ("
@@ -191,9 +190,9 @@ public abstract class OnlineFeedViewActivity extends SherlockFragmentActivity {
}
});
} else {
- final String errorMsg = DownloadError.getErrorString(
- OnlineFeedViewActivity.this,
- DownloadError.ERROR_PARSER_EXCEPTION)
+ final String errorMsg =
+ DownloadError.ERROR_PARSER_EXCEPTION.getErrorString(
+ OnlineFeedViewActivity.this)
+ " (" + reasonDetailed + ")";
runOnUiThread(new Runnable() {
diff --git a/src/de/danoeh/antennapod/activity/PreferenceActivity.java b/src/de/danoeh/antennapod/activity/PreferenceActivity.java
index 14d9c29cc..be5fc2c26 100644
--- a/src/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/src/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -12,6 +12,7 @@ import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
@@ -147,32 +148,47 @@ public class PreferenceActivity extends SherlockPreferenceActivity {
.setOnPreferenceChangeListener(
new OnPreferenceChangeListener() {
+
@Override
- public boolean onPreferenceChange(
- Preference preference, Object newValue) {
- if (newValue instanceof String) {
- setEpisodeCacheSizeText(Integer
- .valueOf((String) newValue));
- }
+ public boolean onPreferenceChange(Preference preference, Object o) {
+ checkItemVisibility();
return true;
}
});
- findPreference(UserPreferences.PREF_ENABLE_AUTODL)
- .setOnPreferenceClickListener(new OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- checkItemVisibility();
- return true;
- }
- });
-
+ buildUpdateIntervalPreference();
buildAutodownloadSelectedNetworsPreference();
setSelectedNetworksEnabled(UserPreferences
.isEnableAutodownloadWifiFilter());
}
+ private void buildUpdateIntervalPreference() {
+ ListPreference pref = (ListPreference) findPreference(UserPreferences.PREF_UPDATE_INTERVAL);
+ String[] values = getResources().getStringArray(
+ R.array.update_intervall_values);
+ String[] entries = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ Integer v = Integer.parseInt(values[x]);
+ switch (v) {
+ case 0:
+ entries[x] = getString(R.string.pref_update_interval_hours_manual);
+ break;
+ case 1:
+ entries[x] = v
+ + " "
+ + getString(R.string.pref_update_interval_hours_singular);
+ break;
+ default:
+ entries[x] = v + " "
+ + getString(R.string.pref_update_interval_hours_plural);
+ break;
+
+ }
+ }
+ pref.setEntries(entries);
+
+ }
+
private void setSelectedNetworksEnabled(boolean b) {
if (selectedNetworks != null) {
for (Preference p : selectedNetworks) {
diff --git a/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
index 5f6a0ea77..c067ac5d2 100644
--- a/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -71,8 +71,7 @@ public class DownloadLogAdapter extends BaseAdapter {
holder.successful.setTextColor(convertView.getResources().getColor(
R.color.download_failed_red));
holder.successful.setText(R.string.download_failed);
- String reasonText = DownloadError.getErrorString(context,
- status.getReason());
+ String reasonText = status.getReason().getErrorString(context);
if (status.getReasonDetailed() != null) {
reasonText += ": " + status.getReasonDetailed();
}
diff --git a/src/de/danoeh/antennapod/miroguide/conn/MiroGuideConnector.java b/src/de/danoeh/antennapod/miroguide/conn/MiroGuideConnector.java
index 4637c7725..99bef4bd8 100644
--- a/src/de/danoeh/antennapod/miroguide/conn/MiroGuideConnector.java
+++ b/src/de/danoeh/antennapod/miroguide/conn/MiroGuideConnector.java
@@ -20,7 +20,7 @@ import android.net.Uri;
public class MiroGuideConnector {
private HttpClient httpClient;
- private static final String HOST_URL = "https://www.miroguide.com/api/";
+ private static final String HOST_URL = "http://www.miroguide.com/api/";
private static final String PATH_GET_CHANNELS = "get_channels";
private static final String PATH_LIST_CATEGORIES = "list_categories";
private static final String PATH_GET_CHANNEL = "get_channel";
diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java
index edcc4157e..a173dfdfb 100644
--- a/src/de/danoeh/antennapod/service/PlaybackService.java
+++ b/src/de/danoeh/antennapod/service/PlaybackService.java
@@ -48,6 +48,7 @@ import de.danoeh.antennapod.util.QueueAccess;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
import de.danoeh.antennapod.util.playback.Playable;
import de.danoeh.antennapod.util.playback.Playable.PlayableException;
+import de.danoeh.antennapod.util.playback.PlaybackController;
/**
* Controls the MediaPlayer that plays a FeedMedia-file
@@ -435,41 +436,47 @@ public class PlaybackService extends Service {
return Service.START_NOT_STICKY;
}
- /**
- * Handles media button events
- */
- private void handleKeycode(int keycode) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Handling keycode: " + keycode);
- switch (keycode) {
- case KeyEvent.KEYCODE_HEADSETHOOK:
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- if (status == PlayerStatus.PLAYING) {
- pause(true, true);
- } else if (status == PlayerStatus.PAUSED) {
- play();
- } else if (status == PlayerStatus.PREPARING) {
- setStartWhenPrepared(!startWhenPrepared);
- } else if (status == PlayerStatus.INITIALIZED) {
- startWhenPrepared = true;
- prepare();
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- if (status == PlayerStatus.PAUSED) {
- play();
- } else if (status == PlayerStatus.INITIALIZED) {
- startWhenPrepared = true;
- prepare();
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- if (status == PlayerStatus.PLAYING) {
- pause(true, true);
- }
- break;
- }
- }
+ /** Handles media button events */
+ private void handleKeycode(int keycode) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Handling keycode: " + keycode);
+ switch (keycode) {
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ if (status == PlayerStatus.PLAYING) {
+ pause(true, true);
+ } else if (status == PlayerStatus.PAUSED) {
+ play();
+ } else if (status == PlayerStatus.PREPARING) {
+ setStartWhenPrepared(!startWhenPrepared);
+ } else if (status == PlayerStatus.INITIALIZED) {
+ startWhenPrepared = true;
+ prepare();
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ if (status == PlayerStatus.PAUSED) {
+ play();
+ } else if (status == PlayerStatus.INITIALIZED) {
+ startWhenPrepared = true;
+ prepare();
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ if (status == PlayerStatus.PLAYING) {
+ pause(true, true);
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+ seekDelta(PlaybackController.DEFAULT_SEEK_DELTA);
+ break;
+ }
+ case KeyEvent.KEYCODE_MEDIA_REWIND: {
+ seekDelta(-PlaybackController.DEFAULT_SEEK_DELTA);
+ break;
+ }
+ }
+ }
/**
* Called by a mediaplayer Activity as soon as it has prepared its
@@ -544,12 +551,12 @@ public class PlaybackService extends Service {
status = PlayerStatus.STOPPED;
}
- public void notifyVideoSurfaceAbandoned() {
- resetVideoSurface();
+ public void notifyVideoSurfaceAbandoned() {
+ resetVideoSurface();
if (media != null) {
initMediaplayer(true);
}
- }
+ }
/**
* Called after service has extracted the media it is supposed to play.
@@ -635,125 +642,125 @@ public class PlaybackService extends Service {
}
}
- private void setupPositionSaver() {
- if (positionSaverFuture == null
- || (positionSaverFuture.isCancelled() || positionSaverFuture
- .isDone())) {
-
- positionSaver = new PositionSaver();
- positionSaverFuture = schedExecutor.scheduleAtFixedRate(
- positionSaver, PositionSaver.WAITING_INTERVALL,
- PositionSaver.WAITING_INTERVALL, TimeUnit.MILLISECONDS);
- }
- }
-
- private void cancelPositionSaver() {
- if (positionSaverFuture != null) {
- boolean result = positionSaverFuture.cancel(true);
- if (AppConfig.DEBUG)
- Log.d(TAG, "PositionSaver cancelled. Result: " + result);
- }
- }
-
- private MediaPlayer.OnPreparedListener preparedListener = new MediaPlayer.OnPreparedListener() {
- @Override
- public void onPrepared(MediaPlayer mp) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Resource prepared");
- mp.seekTo(media.getPosition());
- if (media.getDuration() == 0) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Setting duration of media");
- media.setDuration(mp.getDuration());
- }
- setStatus(PlayerStatus.PREPARED);
- if (chapterLoader != null) {
- chapterLoader.interrupt();
- }
- chapterLoader = new Thread() {
- @Override
- public void run() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Chapter loader started");
- if (media != null && media.getChapters() == null) {
- media.loadChapterMarks();
- if (!isInterrupted() && media.getChapters() != null) {
- sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
- 0);
- }
- }
- if (AppConfig.DEBUG)
- Log.d(TAG, "Chapter loader stopped");
- }
- };
- chapterLoader.start();
-
- if (startWhenPrepared) {
- play();
- }
- }
- };
-
- private MediaPlayer.OnSeekCompleteListener onSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() {
-
- @Override
- public void onSeekComplete(MediaPlayer mp) {
- if (status == PlayerStatus.SEEKING) {
- setStatus(statusBeforeSeek);
- }
-
- }
- };
-
- private MediaPlayer.OnInfoListener onInfoListener = new MediaPlayer.OnInfoListener() {
-
- @Override
- public boolean onInfo(MediaPlayer mp, int what, int extra) {
- switch (what) {
- case MediaPlayer.MEDIA_INFO_BUFFERING_START:
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
- return true;
- case MediaPlayer.MEDIA_INFO_BUFFERING_END:
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
- return true;
- default:
- return false;
- }
- }
- };
-
- private MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() {
- private static final String TAG = "PlaybackService.onErrorListener";
-
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
- Log.w(TAG, "An error has occured: " + what);
- if (mp.isPlaying()) {
- pause(true, true);
- }
- sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
- setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
- stopSelf();
- return true;
- }
- };
-
- private MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() {
-
- @Override
- public void onCompletion(MediaPlayer mp) {
- endPlayback(true);
- }
- };
-
- private MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {
-
- @Override
- public void onBufferingUpdate(MediaPlayer mp, int percent) {
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
-
- }
- };
+ private void setupPositionSaver() {
+ if (positionSaverFuture == null
+ || (positionSaverFuture.isCancelled() || positionSaverFuture
+ .isDone())) {
+
+ positionSaver = new PositionSaver();
+ positionSaverFuture = schedExecutor.scheduleAtFixedRate(
+ positionSaver, PositionSaver.WAITING_INTERVALL,
+ PositionSaver.WAITING_INTERVALL, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private void cancelPositionSaver() {
+ if (positionSaverFuture != null) {
+ boolean result = positionSaverFuture.cancel(true);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "PositionSaver cancelled. Result: " + result);
+ }
+ }
+
+ private MediaPlayer.OnPreparedListener preparedListener = new MediaPlayer.OnPreparedListener() {
+ @Override
+ public void onPrepared(MediaPlayer mp) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Resource prepared");
+ mp.seekTo(media.getPosition());
+ if (media.getDuration() == 0) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Setting duration of media");
+ media.setDuration(mp.getDuration());
+ }
+ setStatus(PlayerStatus.PREPARED);
+ if (chapterLoader != null) {
+ chapterLoader.interrupt();
+ }
+ chapterLoader = new Thread() {
+ @Override
+ public void run() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Chapter loader started");
+ if (media != null && media.getChapters() == null) {
+ media.loadChapterMarks();
+ if (!isInterrupted() && media.getChapters() != null) {
+ sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
+ 0);
+ }
+ }
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Chapter loader stopped");
+ }
+ };
+ chapterLoader.start();
+
+ if (startWhenPrepared) {
+ play();
+ }
+ }
+ };
+
+ private MediaPlayer.OnSeekCompleteListener onSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() {
+
+ @Override
+ public void onSeekComplete(MediaPlayer mp) {
+ if (status == PlayerStatus.SEEKING) {
+ setStatus(statusBeforeSeek);
+ }
+
+ }
+ };
+
+ private MediaPlayer.OnInfoListener onInfoListener = new MediaPlayer.OnInfoListener() {
+
+ @Override
+ public boolean onInfo(MediaPlayer mp, int what, int extra) {
+ switch (what) {
+ case MediaPlayer.MEDIA_INFO_BUFFERING_START:
+ sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
+ return true;
+ case MediaPlayer.MEDIA_INFO_BUFFERING_END:
+ sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
+ return true;
+ default:
+ return false;
+ }
+ }
+ };
+
+ private MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() {
+ private static final String TAG = "PlaybackService.onErrorListener";
+
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ Log.w(TAG, "An error has occured: " + what);
+ if (mp.isPlaying()) {
+ pause(true, true);
+ }
+ sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
+ setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
+ stopSelf();
+ return true;
+ }
+ };
+
+ private MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() {
+
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ endPlayback(true);
+ }
+ };
+
+ private MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {
+
+ @Override
+ public void onBufferingUpdate(MediaPlayer mp, int percent) {
+ sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
+
+ }
+ };
private void endPlayback(boolean playNextEpisode) {
if (AppConfig.DEBUG)
@@ -833,757 +840,738 @@ public class PlaybackService extends Service {
}
}
- public void setSleepTimer(long waitingTime) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime)
- + " milliseconds");
- if (sleepTimerFuture != null) {
- sleepTimerFuture.cancel(true);
- }
- sleepTimer = new SleepTimer(waitingTime);
- sleepTimerFuture = schedExecutor.submit(sleepTimer);
- sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
- }
-
- public void disableSleepTimer() {
- if (sleepTimerFuture != null) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Disabling sleep timer");
- sleepTimerFuture.cancel(true);
- sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
- }
- }
-
- /**
- * Saves the current position and pauses playback. Note that, if audiofocus
- * is abandoned, the lockscreen controls will also disapear.
- *
- * @param abandonFocus is true if the service should release audio focus
- * @param reinit is true if service should reinit after pausing if the media
- * file is being streamed
- */
- public void pause(boolean abandonFocus, boolean reinit) {
- if (player.isPlaying()) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Pausing playback.");
- player.pause();
- if (abandonFocus) {
- audioManager.abandonAudioFocus(audioFocusChangeListener);
- pausedBecauseOfTransientAudiofocusLoss = false;
- disableSleepTimer();
- }
- cancelPositionSaver();
- saveCurrentPosition();
- setStatus(PlayerStatus.PAUSED);
- stopWidgetUpdater();
- stopForeground(true);
- if (shouldStream && reinit) {
- reinit();
- }
- }
- }
-
- /**
- * Pauses playback and destroys service. Recommended for video playback.
- */
- public void stop() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Stopping playback");
- if (status == PlayerStatus.PREPARED || status == PlayerStatus.PAUSED
- || status == PlayerStatus.STOPPED
- || status == PlayerStatus.PLAYING) {
- player.stop();
- }
- setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
- stopSelf();
- }
-
- /**
- * Prepared media player for playback if the service is in the INITALIZED
- * state.
- */
- public void prepare() {
- if (status == PlayerStatus.INITIALIZED) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Preparing media player");
- setStatus(PlayerStatus.PREPARING);
- player.prepareAsync();
- }
- }
-
- /**
- * Resets the media player and moves into INITIALIZED state.
- */
- public void reinit() {
- player.reset();
- player = createMediaPlayer(player);
- initMediaplayer(false);
- }
-
- @SuppressLint("NewApi")
- public void play() {
- if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED
- || status == PlayerStatus.STOPPED) {
- int focusGained = audioManager.requestAudioFocus(
- audioFocusChangeListener, AudioManager.STREAM_MUSIC,
- AudioManager.AUDIOFOCUS_GAIN);
-
- if (focusGained == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Audiofocus successfully requested");
- if (AppConfig.DEBUG)
- Log.d(TAG, "Resuming/Starting playback");
- writePlaybackPreferences();
-
- player.start();
- if (status != PlayerStatus.PAUSED) {
- player.seekTo((int) media.getPosition());
- }
- setStatus(PlayerStatus.PLAYING);
- setupPositionSaver();
- setupWidgetUpdater();
- setupNotification();
- pausedBecauseOfTransientAudiofocusLoss = false;
- if (android.os.Build.VERSION.SDK_INT >= 14) {
- audioManager
- .registerRemoteControlClient(remoteControlClient);
- }
- audioManager
- .registerMediaButtonEventReceiver(mediaButtonReceiver);
- media.onPlaybackStart();
- } else {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Failed to request Audiofocus");
- }
- }
- }
-
- private void writePlaybackPreferences() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Writing playback preferences");
-
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()).edit();
- if (media != null) {
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
- media.getPlayableType());
- editor.putBoolean(
- PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM,
- shouldStream);
- editor.putBoolean(
- PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO,
- playingVideo);
- if (media instanceof FeedMedia) {
- FeedMedia fMedia = (FeedMedia) media;
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
- fMedia.getItem().getFeed().getId());
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
- fMedia.getId());
- } else {
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- }
- media.writeToPreferences(editor);
- } else {
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- }
-
- editor.commit();
- }
-
- private void setStatus(PlayerStatus newStatus) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Setting status to " + newStatus);
- status = newStatus;
- sendBroadcast(new Intent(ACTION_PLAYER_STATUS_CHANGED));
- updateWidget();
- refreshRemoteControlClientState();
- bluetoothNotifyChange();
- }
-
- /**
- * Send ACTION_PLAYER_STATUS_CHANGED without changing the status attribute.
- */
- private void postStatusUpdateIntent() {
- setStatus(status);
- }
-
- private void sendNotificationBroadcast(int type, int code) {
- Intent intent = new Intent(ACTION_PLAYER_NOTIFICATION);
- intent.putExtra(EXTRA_NOTIFICATION_TYPE, type);
- intent.putExtra(EXTRA_NOTIFICATION_CODE, code);
- sendBroadcast(intent);
- }
-
- /**
- * Used by setupNotification to load notification data in another thread.
- */
- private AsyncTask<Void, Void, Void> notificationSetupTask;
-
- /**
- * Prepares notification and starts the service in the foreground.
- */
- @SuppressLint("NewApi")
- private void setupNotification() {
- final PendingIntent pIntent = PendingIntent.getActivity(this, 0,
- PlaybackService.getPlayerActivityIntent(this),
- PendingIntent.FLAG_UPDATE_CURRENT);
-
- if (notificationSetupTask != null) {
- notificationSetupTask.cancel(true);
- }
- notificationSetupTask = new AsyncTask<Void, Void, Void>() {
- Bitmap icon = null;
-
- @Override
- protected Void doInBackground(Void... params) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Starting background work");
- if (android.os.Build.VERSION.SDK_INT >= 11) {
- if (media != null && media != null) {
- int iconSize = getResources().getDimensionPixelSize(
- android.R.dimen.notification_large_icon_width);
- icon = BitmapDecoder
- .decodeBitmapFromWorkerTaskResource(iconSize,
- media);
- }
-
- }
- if (icon == null) {
- icon = BitmapFactory.decodeResource(getResources(),
- R.drawable.ic_stat_antenna);
- }
-
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- if (!isCancelled() && status == PlayerStatus.PLAYING
- && media != null) {
- String contentText = media.getFeedTitle();
- String contentTitle = media.getEpisodeTitle();
- Notification notification = null;
- if (android.os.Build.VERSION.SDK_INT >= 16) {
- Intent pauseButtonIntent = new Intent(
- PlaybackService.this, PlaybackService.class);
- pauseButtonIntent.putExtra(
- MediaButtonReceiver.EXTRA_KEYCODE,
- KeyEvent.KEYCODE_MEDIA_PAUSE);
- PendingIntent pauseButtonPendingIntent = PendingIntent
- .getService(PlaybackService.this, 0,
- pauseButtonIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- Notification.Builder notificationBuilder = new Notification.Builder(
- PlaybackService.this)
- .setContentTitle(contentTitle)
- .setContentText(contentText)
- .setOngoing(true)
- .setContentIntent(pIntent)
- .setLargeIcon(icon)
- .setSmallIcon(R.drawable.ic_stat_antenna)
- .addAction(android.R.drawable.ic_media_pause,
- getString(R.string.pause_label),
- pauseButtonPendingIntent);
- notification = notificationBuilder.build();
- } else {
- NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
- PlaybackService.this)
- .setContentTitle(contentTitle)
- .setContentText(contentText).setOngoing(true)
- .setContentIntent(pIntent).setLargeIcon(icon)
- .setSmallIcon(R.drawable.ic_stat_antenna);
- notification = notificationBuilder.getNotification();
- }
- startForeground(NOTIFICATION_ID, notification);
- if (AppConfig.DEBUG)
- Log.d(TAG, "Notification set up");
- }
- }
-
- };
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- notificationSetupTask
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- notificationSetupTask.execute();
- }
-
- }
-
- /**
- * Seek a specific position from the current position
- *
- * @param delta offset from current position (positive or negative)
- */
- public void seekDelta(int delta) {
- int position = getCurrentPositionSafe();
- if (position != INVALID_TIME) {
- seek(player.getCurrentPosition() + delta);
- }
- }
-
- public void seek(int i) {
- saveCurrentPosition();
- if (status == PlayerStatus.INITIALIZED
- || status == PlayerStatus.INITIALIZING
- || status == PlayerStatus.PREPARING) {
- media.setPosition(i);
- setStartWhenPrepared(true);
- prepare();
- } else {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Seeking position " + i);
- if (shouldStream) {
- if (status != PlayerStatus.SEEKING) {
- statusBeforeSeek = status;
- }
- setStatus(PlayerStatus.SEEKING);
- }
- player.seekTo(i);
- }
- }
-
- public void seekToChapter(Chapter chapter) {
- seek((int) chapter.getStart());
- }
-
- /**
- * Saves the current position of the media file to the DB
- */
- private synchronized void saveCurrentPosition() {
- int position = getCurrentPositionSafe();
- if (position != INVALID_TIME) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Saving current position to " + position);
- media.saveCurrentPosition(PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()),
- position);
- }
- }
-
- private void stopWidgetUpdater() {
- if (widgetUpdaterFuture != null) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Stopping widgetUpdateWorker");
- widgetUpdaterFuture.cancel(true);
- }
- sendBroadcast(new Intent(PlayerWidget.STOP_WIDGET_UPDATE));
- }
-
- @SuppressLint("NewApi")
- private void setupWidgetUpdater() {
- if (widgetUpdaterFuture == null
- || (widgetUpdaterFuture.isCancelled() || widgetUpdaterFuture
- .isDone())) {
- widgetUpdater = new WidgetUpdateWorker();
- widgetUpdaterFuture = schedExecutor.scheduleAtFixedRate(
- widgetUpdater, WidgetUpdateWorker.NOTIFICATION_INTERVALL,
- WidgetUpdateWorker.NOTIFICATION_INTERVALL,
- TimeUnit.MILLISECONDS);
- }
- }
-
- private void updateWidget() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Sending widget update request");
- PlaybackService.this.sendBroadcast(new Intent(
- PlayerWidget.FORCE_WIDGET_UPDATE));
- }
-
- public boolean sleepTimerActive() {
- return sleepTimer != null && sleepTimer.isWaiting();
- }
-
- public long getSleepTimerTimeLeft() {
- if (sleepTimerActive()) {
- return sleepTimer.getWaitingTime();
- } else {
- return 0;
- }
- }
-
- @SuppressLint("NewApi")
- private RemoteControlClient setupRemoteControlClient() {
- Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- mediaButtonIntent.setComponent(mediaButtonReceiver);
- PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(
- getApplicationContext(), 0, mediaButtonIntent, 0);
- remoteControlClient = new RemoteControlClient(mediaPendingIntent);
- int controlFlags;
- if (android.os.Build.VERSION.SDK_INT < 16) {
- controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
- | RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
- } else {
- controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
- }
- remoteControlClient.setTransportControlFlags(controlFlags);
- return remoteControlClient;
- }
-
- /**
- * Refresh player status and metadata.
- */
- @SuppressLint("NewApi")
- private void refreshRemoteControlClientState() {
- if (android.os.Build.VERSION.SDK_INT >= 14) {
- if (remoteControlClient != null) {
- switch (status) {
- case PLAYING:
- remoteControlClient
- .setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
- break;
- case PAUSED:
- case INITIALIZED:
- remoteControlClient
- .setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED);
- break;
- case STOPPED:
- remoteControlClient
- .setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED);
- break;
- case ERROR:
- remoteControlClient
- .setPlaybackState(RemoteControlClient.PLAYSTATE_ERROR);
- break;
- default:
- remoteControlClient
- .setPlaybackState(RemoteControlClient.PLAYSTATE_BUFFERING);
- }
- if (media != null) {
- MetadataEditor editor = remoteControlClient
- .editMetadata(false);
- editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE,
- media.getEpisodeTitle());
-
- editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM,
- media.getFeedTitle());
-
- editor.apply();
- }
- if (AppConfig.DEBUG)
- Log.d(TAG, "RemoteControlClient state was refreshed");
- }
- }
- }
-
- private void bluetoothNotifyChange() {
- boolean isPlaying = false;
-
- if (status == PlayerStatus.PLAYING) {
- isPlaying = true;
- }
-
- Intent i = new Intent(AVRCP_ACTION_PLAYER_STATUS_CHANGED);
- i.putExtra("id", 1);
- i.putExtra("artist", "");
- i.putExtra("album", media.getFeedTitle());
- i.putExtra("track", media.getEpisodeTitle());
- i.putExtra("playing", isPlaying);
+ public void setSleepTimer(long waitingTime) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime)
+ + " milliseconds");
+ if (sleepTimerFuture != null) {
+ sleepTimerFuture.cancel(true);
+ }
+ sleepTimer = new SleepTimer(waitingTime);
+ sleepTimerFuture = schedExecutor.submit(sleepTimer);
+ sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
+ }
+
+ public void disableSleepTimer() {
+ if (sleepTimerFuture != null) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Disabling sleep timer");
+ sleepTimerFuture.cancel(true);
+ sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
+ }
+ }
+
+ /**
+ * Saves the current position and pauses playback. Note that, if audiofocus
+ * is abandoned, the lockscreen controls will also disapear.
+ *
+ * @param abandonFocus
+ * is true if the service should release audio focus
+ * @param reinit
+ * is true if service should reinit after pausing if the media
+ * file is being streamed
+ */
+ public void pause(boolean abandonFocus, boolean reinit) {
+ if (player.isPlaying()) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Pausing playback.");
+ player.pause();
+ if (abandonFocus) {
+ audioManager.abandonAudioFocus(audioFocusChangeListener);
+ pausedBecauseOfTransientAudiofocusLoss = false;
+ disableSleepTimer();
+ }
+ cancelPositionSaver();
+ saveCurrentPosition();
+ setStatus(PlayerStatus.PAUSED);
+ stopWidgetUpdater();
+ stopForeground(true);
+ if (shouldStream && reinit) {
+ reinit();
+ }
+ }
+ }
+
+ /** Pauses playback and destroys service. Recommended for video playback. */
+ public void stop() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Stopping playback");
+ if (status == PlayerStatus.PREPARED || status == PlayerStatus.PAUSED
+ || status == PlayerStatus.STOPPED
+ || status == PlayerStatus.PLAYING) {
+ player.stop();
+ }
+ setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
+ stopSelf();
+ }
+
+ /**
+ * Prepared media player for playback if the service is in the INITALIZED
+ * state.
+ */
+ public void prepare() {
+ if (status == PlayerStatus.INITIALIZED) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Preparing media player");
+ setStatus(PlayerStatus.PREPARING);
+ player.prepareAsync();
+ }
+ }
+
+ /** Resets the media player and moves into INITIALIZED state. */
+ public void reinit() {
+ player.reset();
+ player = createMediaPlayer(player);
+ initMediaplayer(false);
+ }
+
+ @SuppressLint("NewApi")
+ public void play() {
+ if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED
+ || status == PlayerStatus.STOPPED) {
+ int focusGained = audioManager.requestAudioFocus(
+ audioFocusChangeListener, AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+
+ if (focusGained == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Audiofocus successfully requested");
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Resuming/Starting playback");
+ writePlaybackPreferences();
+
+ player.start();
+ if (status != PlayerStatus.PAUSED) {
+ player.seekTo((int) media.getPosition());
+ }
+ setStatus(PlayerStatus.PLAYING);
+ setupPositionSaver();
+ setupWidgetUpdater();
+ setupNotification();
+ pausedBecauseOfTransientAudiofocusLoss = false;
+ if (android.os.Build.VERSION.SDK_INT >= 14) {
+ audioManager
+ .registerRemoteControlClient(remoteControlClient);
+ }
+ audioManager
+ .registerMediaButtonEventReceiver(mediaButtonReceiver);
+ media.onPlaybackStart();
+ } else {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Failed to request Audiofocus");
+ }
+ }
+ }
+
+ private void writePlaybackPreferences() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Writing playback preferences");
+
+ SharedPreferences.Editor editor = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext()).edit();
+ if (media != null) {
+ editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
+ media.getPlayableType());
+ editor.putBoolean(
+ PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM,
+ shouldStream);
+ editor.putBoolean(
+ PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO,
+ playingVideo);
+ if (media instanceof FeedMedia) {
+ FeedMedia fMedia = (FeedMedia) media;
+ editor.putLong(
+ PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
+ fMedia.getItem().getFeed().getId());
+ editor.putLong(
+ PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
+ fMedia.getId());
+ } else {
+ editor.putLong(
+ PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
+ PlaybackPreferences.NO_MEDIA_PLAYING);
+ editor.putLong(
+ PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
+ PlaybackPreferences.NO_MEDIA_PLAYING);
+ }
+ media.writeToPreferences(editor);
+ } else {
+ editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
+ PlaybackPreferences.NO_MEDIA_PLAYING);
+ editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
+ PlaybackPreferences.NO_MEDIA_PLAYING);
+ editor.putLong(
+ PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
+ PlaybackPreferences.NO_MEDIA_PLAYING);
+ }
+
+ editor.commit();
+ }
+
+ private void setStatus(PlayerStatus newStatus) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Setting status to " + newStatus);
+ status = newStatus;
+ sendBroadcast(new Intent(ACTION_PLAYER_STATUS_CHANGED));
+ updateWidget();
+ refreshRemoteControlClientState();
+ bluetoothNotifyChange();
+ }
+
+ /** Send ACTION_PLAYER_STATUS_CHANGED without changing the status attribute. */
+ private void postStatusUpdateIntent() {
+ setStatus(status);
+ }
+
+ private void sendNotificationBroadcast(int type, int code) {
+ Intent intent = new Intent(ACTION_PLAYER_NOTIFICATION);
+ intent.putExtra(EXTRA_NOTIFICATION_TYPE, type);
+ intent.putExtra(EXTRA_NOTIFICATION_CODE, code);
+ sendBroadcast(intent);
+ }
+
+ /** Used by setupNotification to load notification data in another thread. */
+ private AsyncTask<Void, Void, Void> notificationSetupTask;
+
+ /** Prepares notification and starts the service in the foreground. */
+ @SuppressLint("NewApi")
+ private void setupNotification() {
+ final PendingIntent pIntent = PendingIntent.getActivity(this, 0,
+ PlaybackService.getPlayerActivityIntent(this),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ if (notificationSetupTask != null) {
+ notificationSetupTask.cancel(true);
+ }
+ notificationSetupTask = new AsyncTask<Void, Void, Void>() {
+ Bitmap icon = null;
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Starting background work");
+ if (android.os.Build.VERSION.SDK_INT >= 11) {
+ if (media != null && media != null) {
+ int iconSize = getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_width);
+ icon = BitmapDecoder
+ .decodeBitmapFromWorkerTaskResource(iconSize,
+ media);
+ }
+
+ }
+ if (icon == null) {
+ icon = BitmapFactory.decodeResource(getResources(),
+ R.drawable.ic_stat_antenna);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ if (!isCancelled() && status == PlayerStatus.PLAYING
+ && media != null) {
+ String contentText = media.getFeedTitle();
+ String contentTitle = media.getEpisodeTitle();
+ Notification notification = null;
+ if (android.os.Build.VERSION.SDK_INT >= 16) {
+ Intent pauseButtonIntent = new Intent(
+ PlaybackService.this, PlaybackService.class);
+ pauseButtonIntent.putExtra(
+ MediaButtonReceiver.EXTRA_KEYCODE,
+ KeyEvent.KEYCODE_MEDIA_PAUSE);
+ PendingIntent pauseButtonPendingIntent = PendingIntent
+ .getService(PlaybackService.this, 0,
+ pauseButtonIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ Notification.Builder notificationBuilder = new Notification.Builder(
+ PlaybackService.this)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .setOngoing(true)
+ .setContentIntent(pIntent)
+ .setLargeIcon(icon)
+ .setSmallIcon(R.drawable.ic_stat_antenna)
+ .addAction(android.R.drawable.ic_media_pause,
+ getString(R.string.pause_label),
+ pauseButtonPendingIntent);
+ notification = notificationBuilder.build();
+ } else {
+ NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
+ PlaybackService.this)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText).setOngoing(true)
+ .setContentIntent(pIntent).setLargeIcon(icon)
+ .setSmallIcon(R.drawable.ic_stat_antenna);
+ notification = notificationBuilder.getNotification();
+ }
+ startForeground(NOTIFICATION_ID, notification);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Notification set up");
+ }
+ }
+
+ };
+ if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
+ notificationSetupTask
+ .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ } else {
+ notificationSetupTask.execute();
+ }
+
+ }
+
+ /**
+ * Seek a specific position from the current position
+ *
+ * @param delta
+ * offset from current position (positive or negative)
+ * */
+ public void seekDelta(int delta) {
+ int position = getCurrentPositionSafe();
+ if (position != INVALID_TIME) {
+ seek(player.getCurrentPosition() + delta);
+ }
+ }
+
+ public void seek(int i) {
+ saveCurrentPosition();
+ if (status == PlayerStatus.INITIALIZED
+ || status == PlayerStatus.INITIALIZING
+ || status == PlayerStatus.PREPARING) {
+ media.setPosition(i);
+ setStartWhenPrepared(true);
+ prepare();
+ } else {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Seeking position " + i);
+ if (shouldStream) {
+ if (status != PlayerStatus.SEEKING) {
+ statusBeforeSeek = status;
+ }
+ setStatus(PlayerStatus.SEEKING);
+ }
+ player.seekTo(i);
+ }
+ }
+
+ public void seekToChapter(Chapter chapter) {
+ seek((int) chapter.getStart());
+ }
+
+ /** Saves the current position of the media file to the DB */
+ private synchronized void saveCurrentPosition() {
+ int position = getCurrentPositionSafe();
+ if (position != INVALID_TIME) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Saving current position to " + position);
+ media.saveCurrentPosition(PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext()),
+ position);
+ }
+ }
+
+ private void stopWidgetUpdater() {
+ if (widgetUpdaterFuture != null) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Stopping widgetUpdateWorker");
+ widgetUpdaterFuture.cancel(true);
+ }
+ sendBroadcast(new Intent(PlayerWidget.STOP_WIDGET_UPDATE));
+ }
+
+ @SuppressLint("NewApi")
+ private void setupWidgetUpdater() {
+ if (widgetUpdaterFuture == null
+ || (widgetUpdaterFuture.isCancelled() || widgetUpdaterFuture
+ .isDone())) {
+ widgetUpdater = new WidgetUpdateWorker();
+ widgetUpdaterFuture = schedExecutor.scheduleAtFixedRate(
+ widgetUpdater, WidgetUpdateWorker.NOTIFICATION_INTERVALL,
+ WidgetUpdateWorker.NOTIFICATION_INTERVALL,
+ TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private void updateWidget() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Sending widget update request");
+ PlaybackService.this.sendBroadcast(new Intent(
+ PlayerWidget.FORCE_WIDGET_UPDATE));
+ }
+
+ public boolean sleepTimerActive() {
+ return sleepTimer != null && sleepTimer.isWaiting();
+ }
+
+ public long getSleepTimerTimeLeft() {
+ if (sleepTimerActive()) {
+ return sleepTimer.getWaitingTime();
+ } else {
+ return 0;
+ }
+ }
+
+ @SuppressLint("NewApi")
+ private RemoteControlClient setupRemoteControlClient() {
+ Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ mediaButtonIntent.setComponent(mediaButtonReceiver);
+ PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(
+ getApplicationContext(), 0, mediaButtonIntent, 0);
+ remoteControlClient = new RemoteControlClient(mediaPendingIntent);
+ int controlFlags;
+ if (android.os.Build.VERSION.SDK_INT < 16) {
+ controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
+ | RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
+ } else {
+ controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
+ }
+ remoteControlClient.setTransportControlFlags(controlFlags);
+ return remoteControlClient;
+ }
+
+ /** Refresh player status and metadata. */
+ @SuppressLint("NewApi")
+ private void refreshRemoteControlClientState() {
+ if (android.os.Build.VERSION.SDK_INT >= 14) {
+ if (remoteControlClient != null) {
+ switch (status) {
+ case PLAYING:
+ remoteControlClient
+ .setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
+ break;
+ case PAUSED:
+ case INITIALIZED:
+ remoteControlClient
+ .setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED);
+ break;
+ case STOPPED:
+ remoteControlClient
+ .setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED);
+ break;
+ case ERROR:
+ remoteControlClient
+ .setPlaybackState(RemoteControlClient.PLAYSTATE_ERROR);
+ break;
+ default:
+ remoteControlClient
+ .setPlaybackState(RemoteControlClient.PLAYSTATE_BUFFERING);
+ }
+ if (media != null) {
+ MetadataEditor editor = remoteControlClient
+ .editMetadata(false);
+ editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE,
+ media.getEpisodeTitle());
+
+ editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM,
+ media.getFeedTitle());
+
+ editor.apply();
+ }
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "RemoteControlClient state was refreshed");
+ }
+ }
+ }
+
+ private void bluetoothNotifyChange() {
+ boolean isPlaying = false;
+
+ if (status == PlayerStatus.PLAYING) {
+ isPlaying = true;
+ }
+
+ Intent i = new Intent(AVRCP_ACTION_PLAYER_STATUS_CHANGED);
+ i.putExtra("id", 1);
+ i.putExtra("artist", "");
+ i.putExtra("album", media.getFeedTitle());
+ i.putExtra("track", media.getEpisodeTitle());
+ i.putExtra("playing", isPlaying);
if (queue != null) {
i.putExtra("ListSize", queue.size());
}
i.putExtra("duration", media.getDuration());
- i.putExtra("position", media.getPosition());
- sendBroadcast(i);
- }
-
- /**
- * Pauses playback when the headset is disconnected and the preference is
- * set
- */
- private BroadcastReceiver headsetDisconnected = new BroadcastReceiver() {
- private static final String TAG = "headsetDisconnected";
- private static final int UNPLUGGED = 0;
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
- int state = intent.getIntExtra("state", -1);
- if (state != -1) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Headset plug event. State is " + state);
- if (state == UNPLUGGED && status == PlayerStatus.PLAYING) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Headset was unplugged during playback.");
- pauseIfPauseOnDisconnect();
- }
- } else {
- Log.e(TAG, "Received invalid ACTION_HEADSET_PLUG intent");
- }
- }
- }
- };
-
- private BroadcastReceiver audioBecomingNoisy = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- // sound is about to change, eg. bluetooth -> speaker
- if (AppConfig.DEBUG)
- Log.d(TAG, "Pausing playback because audio is becoming noisy");
- pauseIfPauseOnDisconnect();
- }
- // android.media.AUDIO_BECOMING_NOISY
- };
-
- /**
- * Pauses playback if PREF_PAUSE_ON_HEADSET_DISCONNECT was set to true.
- */
- private void pauseIfPauseOnDisconnect() {
- if (UserPreferences.isPauseOnHeadsetDisconnect()
- && status == PlayerStatus.PLAYING) {
- pause(true, true);
- }
- }
-
- private BroadcastReceiver shutdownReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
- schedExecutor.shutdownNow();
- stop();
- media = null;
- }
- }
-
- };
-
- private BroadcastReceiver skipCurrentEpisodeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(ACTION_SKIP_CURRENT_EPISODE)) {
-
- if (AppConfig.DEBUG)
- Log.d(TAG, "Received SKIP_CURRENT_EPISODE intent");
- if (media != null) {
- setStatus(PlayerStatus.STOPPED);
- player.reset();
- endPlayback(false);
- }
- }
- }
- };
-
- /**
- * Periodically saves the position of the media file
- */
- class PositionSaver implements Runnable {
- public static final int WAITING_INTERVALL = 5000;
-
- @Override
- public void run() {
- if (player != null && player.isPlaying()) {
- try {
- saveCurrentPosition();
- } catch (IllegalStateException e) {
- Log.w(TAG,
- "saveCurrentPosition was called in illegal state");
- }
- }
- }
- }
-
- /**
- * Notifies the player widget in the specified intervall
- */
- class WidgetUpdateWorker implements Runnable {
- private static final int NOTIFICATION_INTERVALL = 1000;
-
- @Override
- public void run() {
- if (PlaybackService.isRunning) {
- updateWidget();
- }
- }
- }
-
- /**
- * Sleeps for a given time and then pauses playback.
- */
- class SleepTimer implements Runnable {
- private static final String TAG = "SleepTimer";
- private static final long UPDATE_INTERVALL = 1000L;
- private volatile long waitingTime;
- private boolean isWaiting;
-
- public SleepTimer(long waitingTime) {
- super();
- this.waitingTime = waitingTime;
- }
-
- @Override
- public void run() {
- isWaiting = true;
- if (AppConfig.DEBUG)
- Log.d(TAG, "Starting");
- while (waitingTime > 0) {
- try {
- Thread.sleep(UPDATE_INTERVALL);
- waitingTime -= UPDATE_INTERVALL;
-
- if (waitingTime <= 0) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Waiting completed");
- if (status == PlayerStatus.PLAYING) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Pausing playback");
- pause(true, true);
- }
- postExecute();
- }
- } catch (InterruptedException e) {
- Log.d(TAG, "Thread was interrupted while waiting");
- break;
- }
- }
- postExecute();
- }
-
- protected void postExecute() {
- isWaiting = false;
- sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
- }
-
- public long getWaitingTime() {
- return waitingTime;
- }
-
- public boolean isWaiting() {
- return isWaiting;
- }
-
- }
-
- public static boolean isPlayingVideo() {
- return playingVideo;
- }
-
- public boolean isShouldStream() {
- return shouldStream;
- }
-
- public PlayerStatus getStatus() {
- return status;
- }
-
- public Playable getMedia() {
- return media;
- }
-
- public MediaPlayer getPlayer() {
- return player;
- }
-
- public boolean isStartWhenPrepared() {
- return startWhenPrepared;
- }
-
- public void setStartWhenPrepared(boolean startWhenPrepared) {
- this.startWhenPrepared = startWhenPrepared;
- postStatusUpdateIntent();
- }
-
- /**
- * call getDuration() on mediaplayer or return INVALID_TIME if player is in
- * an invalid state. This method should be used instead of calling
- * getDuration() directly to avoid an error.
- */
- public int getDurationSafe() {
- if (status != null && player != null) {
- switch (status) {
- case PREPARED:
- case PLAYING:
- case PAUSED:
- case SEEKING:
- try {
- return player.getDuration();
- } catch (IllegalStateException e) {
- e.printStackTrace();
- return INVALID_TIME;
- }
- default:
- return INVALID_TIME;
- }
- } else {
- return INVALID_TIME;
- }
- }
-
- /**
- * call getCurrentPosition() on mediaplayer or return INVALID_TIME if player
- * is in an invalid state. This method should be used instead of calling
- * getCurrentPosition() directly to avoid an error.
- */
- public int getCurrentPositionSafe() {
- if (status != null && player != null) {
- switch (status) {
- case PREPARED:
- case PLAYING:
- case PAUSED:
- case SEEKING:
- return player.getCurrentPosition();
- default:
- return INVALID_TIME;
- }
- } else {
- return INVALID_TIME;
- }
- }
-
- private void setCurrentlyPlayingMedia(long id) {
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()).edit();
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, id);
- editor.commit();
- }
-
- private static class InitTask extends AsyncTask<Playable, Void, Playable> {
- private Playable playable;
- public PlayableException exception;
-
- @Override
- protected Playable doInBackground(Playable... params) {
- if (params[0] == null) {
- throw new IllegalArgumentException("Playable must not be null");
- }
- playable = params[0];
-
- try {
- playable.loadMetadata();
- } catch (PlayableException e) {
- e.printStackTrace();
- exception = e;
- return null;
- }
- return playable;
- }
-
- @SuppressLint("NewApi")
- public void executeAsync(Playable playable) {
- FlattrUtils.hasToken();
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- executeOnExecutor(THREAD_POOL_EXECUTOR, playable);
- } else {
- execute(playable);
- }
- }
-
- }
+ i.putExtra("position", media.getPosition());
+ sendBroadcast(i);
+ }
+
+ /**
+ * Pauses playback when the headset is disconnected and the preference is
+ * set
+ */
+ private BroadcastReceiver headsetDisconnected = new BroadcastReceiver() {
+ private static final String TAG = "headsetDisconnected";
+ private static final int UNPLUGGED = 0;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
+ int state = intent.getIntExtra("state", -1);
+ if (state != -1) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Headset plug event. State is " + state);
+ if (state == UNPLUGGED && status == PlayerStatus.PLAYING) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Headset was unplugged during playback.");
+ pauseIfPauseOnDisconnect();
+ }
+ } else {
+ Log.e(TAG, "Received invalid ACTION_HEADSET_PLUG intent");
+ }
+ }
+ }
+ };
+
+ private BroadcastReceiver audioBecomingNoisy = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // sound is about to change, eg. bluetooth -> speaker
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Pausing playback because audio is becoming noisy");
+ pauseIfPauseOnDisconnect();
+ }
+ // android.media.AUDIO_BECOMING_NOISY
+ };
+
+ /** Pauses playback if PREF_PAUSE_ON_HEADSET_DISCONNECT was set to true. */
+ private void pauseIfPauseOnDisconnect() {
+ if (UserPreferences.isPauseOnHeadsetDisconnect()
+ && status == PlayerStatus.PLAYING) {
+ pause(true, true);
+ }
+ }
+
+ private BroadcastReceiver shutdownReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
+ schedExecutor.shutdownNow();
+ stop();
+ media = null;
+ }
+ }
+
+ };
+
+ private BroadcastReceiver skipCurrentEpisodeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(ACTION_SKIP_CURRENT_EPISODE)) {
+
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Received SKIP_CURRENT_EPISODE intent");
+ if (media != null) {
+ setStatus(PlayerStatus.STOPPED);
+ player.reset();
+ endPlayback(false);
+ }
+ }
+ }
+ };
+
+ /** Periodically saves the position of the media file */
+ class PositionSaver implements Runnable {
+ public static final int WAITING_INTERVALL = 5000;
+
+ @Override
+ public void run() {
+ if (player != null && player.isPlaying()) {
+ try {
+ saveCurrentPosition();
+ } catch (IllegalStateException e) {
+ Log.w(TAG,
+ "saveCurrentPosition was called in illegal state");
+ }
+ }
+ }
+ }
+
+ /** Notifies the player widget in the specified intervall */
+ class WidgetUpdateWorker implements Runnable {
+ private static final int NOTIFICATION_INTERVALL = 1000;
+
+ @Override
+ public void run() {
+ if (PlaybackService.isRunning) {
+ updateWidget();
+ }
+ }
+ }
+
+ /** Sleeps for a given time and then pauses playback. */
+ class SleepTimer implements Runnable {
+ private static final String TAG = "SleepTimer";
+ private static final long UPDATE_INTERVALL = 1000L;
+ private volatile long waitingTime;
+ private boolean isWaiting;
+
+ public SleepTimer(long waitingTime) {
+ super();
+ this.waitingTime = waitingTime;
+ }
+
+ @Override
+ public void run() {
+ isWaiting = true;
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Starting");
+ while (waitingTime > 0) {
+ try {
+ Thread.sleep(UPDATE_INTERVALL);
+ waitingTime -= UPDATE_INTERVALL;
+
+ if (waitingTime <= 0) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Waiting completed");
+ if (status == PlayerStatus.PLAYING) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Pausing playback");
+ pause(true, true);
+ }
+ postExecute();
+ }
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Thread was interrupted while waiting");
+ break;
+ }
+ }
+ postExecute();
+ }
+
+ protected void postExecute() {
+ isWaiting = false;
+ sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
+ }
+
+ public long getWaitingTime() {
+ return waitingTime;
+ }
+
+ public boolean isWaiting() {
+ return isWaiting;
+ }
+
+ }
+
+ public static boolean isPlayingVideo() {
+ return playingVideo;
+ }
+
+ public boolean isShouldStream() {
+ return shouldStream;
+ }
+
+ public PlayerStatus getStatus() {
+ return status;
+ }
+
+ public Playable getMedia() {
+ return media;
+ }
+
+ public MediaPlayer getPlayer() {
+ return player;
+ }
+
+ public boolean isStartWhenPrepared() {
+ return startWhenPrepared;
+ }
+
+ public void setStartWhenPrepared(boolean startWhenPrepared) {
+ this.startWhenPrepared = startWhenPrepared;
+ postStatusUpdateIntent();
+ }
+
+ /**
+ * call getDuration() on mediaplayer or return INVALID_TIME if player is in
+ * an invalid state. This method should be used instead of calling
+ * getDuration() directly to avoid an error.
+ */
+ public int getDurationSafe() {
+ if (status != null && player != null) {
+ switch (status) {
+ case PREPARED:
+ case PLAYING:
+ case PAUSED:
+ case SEEKING:
+ try {
+ return player.getDuration();
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ return INVALID_TIME;
+ }
+ default:
+ return INVALID_TIME;
+ }
+ } else {
+ return INVALID_TIME;
+ }
+ }
+
+ /**
+ * call getCurrentPosition() on mediaplayer or return INVALID_TIME if player
+ * is in an invalid state. This method should be used instead of calling
+ * getCurrentPosition() directly to avoid an error.
+ */
+ public int getCurrentPositionSafe() {
+ if (status != null && player != null) {
+ switch (status) {
+ case PREPARED:
+ case PLAYING:
+ case PAUSED:
+ case SEEKING:
+ return player.getCurrentPosition();
+ default:
+ return INVALID_TIME;
+ }
+ } else {
+ return INVALID_TIME;
+ }
+ }
+
+ private void setCurrentlyPlayingMedia(long id) {
+ SharedPreferences.Editor editor = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext()).edit();
+ editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, id);
+ editor.commit();
+ }
+
+ private static class InitTask extends AsyncTask<Playable, Void, Playable> {
+ private Playable playable;
+ public PlayableException exception;
+
+ @Override
+ protected Playable doInBackground(Playable... params) {
+ if (params[0] == null) {
+ throw new IllegalArgumentException("Playable must not be null");
+ }
+ playable = params[0];
+
+ try {
+ playable.loadMetadata();
+ } catch (PlayableException e) {
+ e.printStackTrace();
+ exception = e;
+ return null;
+ }
+ return playable;
+ }
+
+ @SuppressLint("NewApi")
+ public void executeAsync(Playable playable) {
+ FlattrUtils.hasToken();
+ if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
+ executeOnExecutor(THREAD_POOL_EXECUTOR, playable);
+ } else {
+ execute(playable);
+ }
+ }
+
+ }
private void loadQueue() {
dbLoaderExecutor.submit(new QueueLoaderTask());
diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java
index b19b0482e..2056efab2 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadService.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadService.java
@@ -608,7 +608,7 @@ public class DownloadService extends Service {
private DownloadRequest request;
- private int reason;
+ private DownloadError reason;
private boolean successful;
public FeedSyncThread(DownloadRequest request) {
@@ -626,7 +626,7 @@ public class DownloadService extends Service {
feed.setFile_url(request.getDestination());
feed.setDownloaded(true);
- reason = 0;
+ reason = null;
String reasonDetailed = null;
successful = true;
FeedHandler feedHandler = new FeedHandler();
diff --git a/src/de/danoeh/antennapod/service/download/DownloadStatus.java b/src/de/danoeh/antennapod/service/download/DownloadStatus.java
index 76091ec67..62e54cbb4 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadStatus.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadStatus.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.service.download;
import java.util.Date;
import de.danoeh.antennapod.feed.FeedFile;
+import de.danoeh.antennapod.util.DownloadError;
/** Contains status attributes for one download */
public class DownloadStatus {
@@ -21,7 +22,7 @@ public class DownloadStatus {
* URL if the download has no other title.
*/
protected String title;
- protected int reason;
+ protected DownloadError reason;
/**
* A message which can be presented to the user to give more information.
* Should be null if Download was successful.
@@ -43,7 +44,7 @@ public class DownloadStatus {
/** Constructor for restoring Download status entries from DB. */
public DownloadStatus(long id, String title, long feedfileId,
- int feedfileType, boolean successful, int reason,
+ int feedfileType, boolean successful, DownloadError reason,
Date completionDate, String reasonDetailed) {
this.id = id;
this.title = title;
@@ -56,7 +57,7 @@ public class DownloadStatus {
this.feedfileType = feedfileType;
}
- public DownloadStatus(DownloadRequest request, int reason,
+ public DownloadStatus(DownloadRequest request, DownloadError reason,
boolean successful, boolean cancelled, String reasonDetailed) {
if (request == null) {
throw new IllegalArgumentException("request must not be null");
@@ -72,7 +73,7 @@ public class DownloadStatus {
}
/** Constructor for creating new completed downloads. */
- public DownloadStatus(FeedFile feedfile, String title, int reason,
+ public DownloadStatus(FeedFile feedfile, String title, DownloadError reason,
boolean successful, String reasonDetailed) {
if (feedfile == null) {
throw new IllegalArgumentException("feedfile must not be null");
@@ -90,7 +91,7 @@ public class DownloadStatus {
/** Constructor for creating new completed downloads. */
public DownloadStatus(long feedfileId, int feedfileType, String title,
- int reason, boolean successful, String reasonDetailed) {
+ DownloadError reason, boolean successful, String reasonDetailed) {
this.title = title;
this.done = true;
this.feedfileId = feedfileId;
@@ -111,48 +112,70 @@ public class DownloadStatus {
+ ", cancelled=" + cancelled + "]";
}
- public long getId() {
- return id;
- }
-
- public String getTitle() {
- return title;
- }
-
- public int getReason() {
- return reason;
- }
-
- public String getReasonDetailed() {
- return reasonDetailed;
- }
-
- public boolean isSuccessful() {
- return successful;
- }
-
- public Date getCompletionDate() {
- return completionDate;
- }
-
- public long getFeedfileId() {
- return feedfileId;
- }
-
- public int getFeedfileType() {
- return feedfileType;
- }
-
- public boolean isDone() {
- return done;
- }
-
- public boolean isCancelled() {
- return cancelled;
- }
-
- public void setId(long id) {
- this.id = id;
- }
-
+ public long getId() {
+ return id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public DownloadError getReason() {
+ return reason;
+ }
+
+ public String getReasonDetailed() {
+ return reasonDetailed;
+ }
+
+ public boolean isSuccessful() {
+ return successful;
+ }
+
+ public Date getCompletionDate() {
+ return completionDate;
+ }
+
+ public long getFeedfileId() {
+ return feedfileId;
+ }
+
+ public int getFeedfileType() {
+ return feedfileType;
+ }
+
+ public boolean isDone() {
+ return done;
+ }
+
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ public void setSuccessful() {
+ this.successful = true;
+ this.reason = DownloadError.SUCCESS;
+ this.done = true;
+ }
+
+ public void setFailed(DownloadError reason, String reasonDetailed) {
+ this.successful = false;
+ this.reason = reason;
+ this.reasonDetailed = reasonDetailed;
+ }
+
+ public void setCancelled() {
+ this.successful = false;
+ this.reason = DownloadError.ERROR_DOWNLOAD_CANCELLED;
+ this.done = true;
+ this.cancelled = true;
+ }
+
+ public void setCompletionDate(Date completionDate) {
+ this.completionDate = completionDate;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
} \ No newline at end of file
diff --git a/src/de/danoeh/antennapod/service/download/Downloader.java b/src/de/danoeh/antennapod/service/download/Downloader.java
index 67507d40f..84731fe9f 100644
--- a/src/de/danoeh/antennapod/service/download/Downloader.java
+++ b/src/de/danoeh/antennapod/service/download/Downloader.java
@@ -20,6 +20,7 @@ public abstract class Downloader implements Callable<Downloader> {
this.request = request;
this.request.setStatusMsg(R.string.download_pending);
this.cancelled = false;
+ this.result = new DownloadStatus(request, null, false, false, null);
}
protected abstract void download();
diff --git a/src/de/danoeh/antennapod/service/download/HttpDownloader.java b/src/de/danoeh/antennapod/service/download/HttpDownloader.java
index b533ca676..c9671ceb3 100644
--- a/src/de/danoeh/antennapod/service/download/HttpDownloader.java
+++ b/src/de/danoeh/antennapod/service/download/HttpDownloader.java
@@ -153,23 +153,21 @@ public class HttpDownloader extends Downloader {
private void onSuccess() {
if (AppConfig.DEBUG)
Log.d(TAG, "Download was successful");
- result = new DownloadStatus(request, 0, true, false, null);
+ result.setSuccessful();
}
- private void onFail(int reason, String reasonDetailed) {
+ private void onFail(DownloadError reason, String reasonDetailed) {
if (AppConfig.DEBUG) {
Log.d(TAG, "Download failed");
}
- result = new DownloadStatus(request, reason, false, false,
- reasonDetailed);
+ result.setFailed(reason, reasonDetailed);
cleanup();
}
private void onCancelled() {
if (AppConfig.DEBUG)
Log.d(TAG, "Download was cancelled");
- result = new DownloadStatus(request,
- DownloadError.ERROR_DOWNLOAD_CANCELLED, false, true, null);
+ result.setCancelled();
cleanup();
}
diff --git a/src/de/danoeh/antennapod/storage/DBReader.java b/src/de/danoeh/antennapod/storage/DBReader.java
index ababcdf78..4f08c2b00 100644
--- a/src/de/danoeh/antennapod/storage/DBReader.java
+++ b/src/de/danoeh/antennapod/storage/DBReader.java
@@ -19,6 +19,7 @@ import de.danoeh.antennapod.feed.ID3Chapter;
import de.danoeh.antennapod.feed.SimpleChapter;
import de.danoeh.antennapod.feed.VorbisCommentChapter;
import de.danoeh.antennapod.service.download.*;
+import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.comparator.DownloadStatusComparator;
import de.danoeh.antennapod.util.comparator.FeedItemPubdateComparator;
@@ -428,7 +429,7 @@ public final class DBReader {
logCursor
.getLong(PodDBAdapter.KEY_COMPLETION_DATE_INDEX));
downloadLog.add(new DownloadStatus(id, title, feedfileId,
- feedfileType, successful, reason, completionDate,
+ feedfileType, successful, DownloadError.fromCode(reason), completionDate,
reasonDetailed));
} while (logCursor.moveToNext());
diff --git a/src/de/danoeh/antennapod/storage/PodDBAdapter.java b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
index 5171b6932..5718e03c0 100644
--- a/src/de/danoeh/antennapod/storage/PodDBAdapter.java
+++ b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
@@ -519,7 +519,7 @@ public class PodDBAdapter {
ContentValues values = new ContentValues();
values.put(KEY_FEEDFILE, status.getFeedfileId());
values.put(KEY_FEEDFILETYPE, status.getFeedfileType());
- values.put(KEY_REASON, status.getReason());
+ values.put(KEY_REASON, status.getReason().getCode());
values.put(KEY_SUCCESSFUL, status.isSuccessful());
values.put(KEY_COMPLETION_DATE, status.getCompletionDate().getTime());
values.put(KEY_REASON_DETAILED, status.getReasonDetailed());
diff --git a/src/de/danoeh/antennapod/syndication/namespace/NSRSS20.java b/src/de/danoeh/antennapod/syndication/namespace/NSRSS20.java
index 4d0b42132..5a2c6005e 100644
--- a/src/de/danoeh/antennapod/syndication/namespace/NSRSS20.java
+++ b/src/de/danoeh/antennapod/syndication/namespace/NSRSS20.java
@@ -78,6 +78,15 @@ public class NSRSS20 extends Namespace {
@Override
public void handleElementEnd(String localName, HandlerState state) {
if (localName.equals(ITEM)) {
+ if (state.getCurrentItem() != null) {
+ // the title tag is optional in RSS 2.0. The description is used
+ // as a
+ // title if the item has no title-tag.
+ if (state.getCurrentItem().getTitle() == null) {
+ state.getCurrentItem().setTitle(
+ state.getCurrentItem().getDescription());
+ }
+ }
state.setCurrentItem(null);
} else if (state.getTagstack().size() >= 2
&& state.getContentBuf() != null) {
@@ -98,7 +107,8 @@ public class NSRSS20 extends Namespace {
state.getCurrentItem().setTitle(content);
} else if (second.equals(CHANNEL)) {
state.getFeed().setTitle(content);
- } else if (second.equals(IMAGE) && third != null && third.equals(CHANNEL)) {
+ } else if (second.equals(IMAGE) && third != null
+ && third.equals(CHANNEL)) {
state.getFeed().getImage().setTitle(content);
}
} else if (top.equals(LINK)) {
@@ -110,7 +120,8 @@ public class NSRSS20 extends Namespace {
} else if (top.equals(PUBDATE) && second.equals(ITEM)) {
state.getCurrentItem().setPubDate(
SyndDateUtils.parseRFC822Date(content));
- } else if (top.equals(URL) && second.equals(IMAGE) && third != null && third.equals(CHANNEL)) {
+ } else if (top.equals(URL) && second.equals(IMAGE) && third != null
+ && third.equals(CHANNEL)) {
state.getFeed().getImage().setDownload_url(content);
} else if (localName.equals(DESCR)) {
if (second.equals(CHANNEL)) {
diff --git a/src/de/danoeh/antennapod/util/ConnectionTester.java b/src/de/danoeh/antennapod/util/ConnectionTester.java
index 2fd22d356..5d940d9e1 100644
--- a/src/de/danoeh/antennapod/util/ConnectionTester.java
+++ b/src/de/danoeh/antennapod/util/ConnectionTester.java
@@ -14,7 +14,7 @@ public class ConnectionTester implements Runnable {
private static final String TAG = "ConnectionTester";
private String strUrl;
private Callback callback;
- private int reason;
+ private DownloadError reason;
private Handler handler;
@@ -68,10 +68,10 @@ public class ConnectionTester implements Runnable {
public static abstract class Callback {
public abstract void onConnectionSuccessful();
- public abstract void onConnectionFailure(int reason);
+ public abstract void onConnectionFailure(DownloadError reason);
}
- public int getReason() {
+ public DownloadError getReason() {
return reason;
}
diff --git a/src/de/danoeh/antennapod/util/DownloadError.java b/src/de/danoeh/antennapod/util/DownloadError.java
index 4723a521c..c37a14584 100644
--- a/src/de/danoeh/antennapod/util/DownloadError.java
+++ b/src/de/danoeh/antennapod/util/DownloadError.java
@@ -4,54 +4,46 @@ import android.content.Context;
import de.danoeh.antennapod.R;
/** Utility class for Download Errors. */
-public class DownloadError {
- public static final int ERROR_PARSER_EXCEPTION = 1;
- public static final int ERROR_UNSUPPORTED_TYPE = 2;
- public static final int ERROR_CONNECTION_ERROR = 3;
- public static final int ERROR_MALFORMED_URL = 4;
- public static final int ERROR_IO_ERROR = 5;
- public static final int ERROR_FILE_EXISTS = 6;
- public static final int ERROR_DOWNLOAD_CANCELLED = 7;
- public static final int ERROR_DEVICE_NOT_FOUND = 8;
- public static final int ERROR_HTTP_DATA_ERROR = 9;
- public static final int ERROR_NOT_ENOUGH_SPACE = 10;
- public static final int ERROR_UNKNOWN_HOST = 11;
- public static final int ERROR_REQUEST_ERROR = 12;
-
- /** Get a human-readable string for a specific error code. */
- public static String getErrorString(Context context, int code) {
- int resId;
- switch(code) {
- case ERROR_NOT_ENOUGH_SPACE:
- resId = R.string.download_error_insufficient_space;
- break;
- case ERROR_DEVICE_NOT_FOUND:
- resId = R.string.download_error_device_not_found;
- break;
- case ERROR_IO_ERROR:
- resId = R.string.download_error_io_error;
- break;
- case ERROR_HTTP_DATA_ERROR:
- resId = R.string.download_error_http_data_error;
- break;
- case ERROR_PARSER_EXCEPTION:
- resId = R.string.download_error_parser_exception;
- break;
- case ERROR_UNSUPPORTED_TYPE:
- resId = R.string.download_error_unsupported_type;
- break;
- case ERROR_CONNECTION_ERROR:
- resId = R.string.download_error_connection_error;
- break;
- case ERROR_UNKNOWN_HOST:
- resId = R.string.download_error_unknown_host;
- break;
- case ERROR_REQUEST_ERROR:
- resId = R.string.download_error_request_error;
- break;
- default:
- resId = R.string.download_error_error_unknown;
+public enum DownloadError {
+ SUCCESS(0, R.string.download_successful),
+ ERROR_PARSER_EXCEPTION(1, R.string.download_error_parser_exception),
+ ERROR_UNSUPPORTED_TYPE(2, R.string.download_error_unsupported_type),
+ ERROR_CONNECTION_ERROR(3, R.string.download_error_connection_error),
+ ERROR_MALFORMED_URL(4, R.string.download_error_error_unknown),
+ ERROR_IO_ERROR(5, R.string.download_error_io_error),
+ ERROR_FILE_EXISTS(6, R.string.download_error_error_unknown),
+ ERROR_DOWNLOAD_CANCELLED(7, R.string.download_error_error_unknown),
+ ERROR_DEVICE_NOT_FOUND(8, R.string.download_error_device_not_found),
+ ERROR_HTTP_DATA_ERROR(9, R.string.download_error_http_data_error),
+ ERROR_NOT_ENOUGH_SPACE(10, R.string.download_error_insufficient_space),
+ ERROR_UNKNOWN_HOST(11, R.string.download_error_unknown_host),
+ ERROR_REQUEST_ERROR(12, R.string.download_error_request_error);
+
+ private final int code;
+ private final int resId;
+
+ private DownloadError(int code, int resId) {
+ this.code = code;
+ this.resId = resId;
+ }
+
+ /** Return DownloadError from its associated code. */
+ public static DownloadError fromCode(int code) {
+ for (DownloadError reason : values()) {
+ if (reason.getCode() == code) {
+ return reason;
+ }
}
+ throw new IllegalArgumentException("unknown code: " + code);
+ }
+
+ /** Get machine-readable code. */
+ public int getCode() {
+ return code;
+ }
+
+ /** Get a human-readable string. */
+ public String getErrorString(Context context) {
return context.getString(resId);
}
diff --git a/src/de/danoeh/antennapod/util/UndoBarController.java b/src/de/danoeh/antennapod/util/UndoBarController.java
index e726717a1..a0240e7ce 100644
--- a/src/de/danoeh/antennapod/util/UndoBarController.java
+++ b/src/de/danoeh/antennapod/util/UndoBarController.java
@@ -16,15 +16,17 @@
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 com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.AnimatorListenerAdapter;
+import com.nineoldandroids.view.ViewHelper;
+import com.nineoldandroids.view.ViewPropertyAnimator;
+import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
import de.danoeh.antennapod.R;
@@ -46,7 +48,7 @@ public class UndoBarController {
public UndoBarController(View undoBarView, UndoListener undoListener) {
mBarView = undoBarView;
- mBarAnimator = mBarView.animate();
+ mBarAnimator = animate(mBarView);
mUndoListener = undoListener;
mMessageView = (TextView) mBarView.findViewById(R.id.undobar_message);
@@ -73,7 +75,7 @@ public class UndoBarController {
mBarView.setVisibility(View.VISIBLE);
if (immediate) {
- mBarView.setAlpha(1);
+ ViewHelper.setAlpha(mBarView, 1);
} else {
mBarAnimator.cancel();
mBarAnimator
@@ -89,7 +91,7 @@ public class UndoBarController {
mHideHandler.removeCallbacks(mHideRunnable);
if (immediate) {
mBarView.setVisibility(View.GONE);
- mBarView.setAlpha(0);
+ ViewHelper.setAlpha(mBarView, 0);
mUndoMessage = null;
mUndoToken = null;
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/playback/PlaybackController.java b/src/de/danoeh/antennapod/util/playback/PlaybackController.java
index 847e08b4a..5a5b43a6e 100644
--- a/src/de/danoeh/antennapod/util/playback/PlaybackController.java
+++ b/src/de/danoeh/antennapod/util/playback/PlaybackController.java
@@ -44,8 +44,8 @@ import de.danoeh.antennapod.util.playback.Playable.PlayableUtils;
public abstract class PlaybackController {
private static final String TAG = "PlaybackController";
- static final int DEFAULT_SEEK_DELTA = 30000;
- public static final int INVALID_TIME = -1;
+ public static final int DEFAULT_SEEK_DELTA = 30000;
+ public static final int INVALID_TIME = -1;
private Activity activity;