Adapted remaining classes to new playback service model

This commit is contained in:
daniel oeh 2013-12-29 01:27:18 +01:00
parent e05a5c265b
commit b864927709
11 changed files with 776 additions and 716 deletions

View File

@ -11,15 +11,10 @@ import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.View.OnLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.view.Window;
import android.widget.*;
import android.widget.ImageView.ScaleType;
import android.widget.ListView;
import android.widget.TextView;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.ChapterListAdapter;
@ -35,7 +30,9 @@ import de.danoeh.antennapod.service.playback.PlaybackService;
import de.danoeh.antennapod.util.playback.ExternalMedia;
import de.danoeh.antennapod.util.playback.Playable;
/** Activity for playing audio files. */
/**
* Activity for playing audio files.
*/
public class AudioplayerActivity extends MediaplayerActivity {
private static final int POS_COVER = 0;
private static final int POS_DESCR = 1;
@ -55,7 +52,9 @@ public class AudioplayerActivity extends MediaplayerActivity {
private Fragment currentlyShownFragment;
private int currentlyShownPosition = -1;
/** Used if onResume was called without loadMediaInfo. */
/**
* Used if onResume was called without loadMediaInfo.
*/
private int savedPosition = -1;
private TextView txtvTitle;
@ -170,7 +169,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
* preferences.
*
* @return true if restoreFromPrefernces changed the activity's state
* */
*/
private boolean restoreFromPreferences() {
if (AppConfig.DEBUG)
Log.d(TAG, "Restoring instance state");
@ -234,6 +233,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
@Override
protected void onAwaitingVideoSurface() {
if (AppConfig.DEBUG) Log.d(TAG, "onAwaitingVideoSurface was called in audio player -> switching to video player");
startActivity(new Intent(this, VideoplayerActivity.class));
}
@ -253,7 +253,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
* Changes the currently displayed fragment.
*
* @param pos Must be POS_COVER, POS_DESCR, or POS_CHAPTERS
* */
*/
private void switchToFragment(int pos) {
if (AppConfig.DEBUG)
Log.d(TAG, "Switching contentView to position " + pos);
@ -464,10 +464,14 @@ public class AudioplayerActivity extends MediaplayerActivity {
}
@Override
protected void loadMediaInfo() {
super.loadMediaInfo();
protected boolean loadMediaInfo() {
if (!super.loadMediaInfo()) {
return false;
}
final Playable media = controller.getMedia();
if (media != null) {
if (media == null) {
return false;
}
txtvTitle.setText(media.getEpisodeTitle());
txtvFeed.setText(media.getFeedTitle());
if (media.getChapters() != null) {
@ -476,7 +480,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
butNavRight.setVisibility(View.GONE);
}
}
if (currentlyShownPosition == -1) {
if (!restoreFromPreferences()) {
switchToFragment(POS_COVER);
@ -487,6 +491,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
.onDataSetChanged(media);
}
updateButPlaybackSpeed();
return true;
}
public void notifyMediaPositionChanged() {

View File

@ -106,8 +106,8 @@ public abstract class MediaplayerActivity extends ActionBarActivity
}
@Override
public void loadMediaInfo() {
MediaplayerActivity.this.loadMediaInfo();
public boolean loadMediaInfo() {
return MediaplayerActivity.this.loadMediaInfo();
}
@Override
@ -339,8 +339,9 @@ public abstract class MediaplayerActivity extends ActionBarActivity
}
/**
* Called by 'handleStatus()' when the PlaybackService is in the
* AWAITING_VIDEO_SURFACE state.
* Called by 'handleStatus()' when the PlaybackService is waiting for
* a video surface.
*
*/
protected abstract void onAwaitingVideoSurface();
@ -380,7 +381,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
* to the PlaybackService to ensure that the activity has the right
* FeedMedia object.
*/
protected void loadMediaInfo() {
protected boolean loadMediaInfo() {
if (AppConfig.DEBUG)
Log.d(TAG, "Loading media info");
Playable media = controller.getMedia();
@ -395,6 +396,9 @@ public abstract class MediaplayerActivity extends ActionBarActivity
/ media.getDuration();
sbPosition.setProgress((int) (progress * sbPosition.getMax()));
}
return true;
} else {
return false;
}
}

View File

@ -11,7 +11,6 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.VideoView;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.MediaType;
@ -21,12 +20,15 @@ import de.danoeh.antennapod.service.playback.PlayerStatus;
import de.danoeh.antennapod.util.playback.ExternalMedia;
import de.danoeh.antennapod.util.playback.Playable;
/** Activity for playing audio files. */
public class VideoplayerActivity extends MediaplayerActivity implements
SurfaceHolder.Callback {
/**
* Activity for playing audio files.
*/
public class VideoplayerActivity extends MediaplayerActivity {
private static final String TAG = "VideoplayerActivity";
/** True if video controls are currently visible. */
/**
* True if video controls are currently visible.
*/
private boolean videoControlsShowing = true;
private boolean videoSurfaceCreated = false;
private VideoControlsHider videoControlsToggler;
@ -74,13 +76,18 @@ public class VideoplayerActivity extends MediaplayerActivity implements
}
@Override
protected void loadMediaInfo() {
super.loadMediaInfo();
protected boolean loadMediaInfo() {
if (!super.loadMediaInfo()) {
return false;
}
Playable media = controller.getMedia();
if (media != null) {
getSupportActionBar().setSubtitle(media.getEpisodeTitle());
getSupportActionBar().setTitle(media.getFeedTitle());
return true;
}
return false;
}
@Override
@ -89,7 +96,7 @@ public class VideoplayerActivity extends MediaplayerActivity implements
videoOverlay = (LinearLayout) findViewById(R.id.overlay);
videoview = (VideoView) findViewById(R.id.videoview);
progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator);
videoview.getHolder().addCallback(this);
videoview.getHolder().addCallback(surfaceHolderCallback);
videoview.setOnTouchListener(onVideoviewTouched);
setupVideoControlsToggler();
@ -167,7 +174,9 @@ public class VideoplayerActivity extends MediaplayerActivity implements
videoControlsShowing = !videoControlsShowing;
}
/** Hides the videocontrols after a certain period of time. */
/**
* Hides the videocontrols after a certain period of time.
*/
public class VideoControlsHider extends AsyncTask<Void, Void, Void> {
@Override
protected void onCancelled() {
@ -206,6 +215,7 @@ public class VideoplayerActivity extends MediaplayerActivity implements
}
private final SurfaceHolder.Callback surfaceHolderCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
@ -217,7 +227,7 @@ public class VideoplayerActivity extends MediaplayerActivity implements
if (AppConfig.DEBUG)
Log.d(TAG, "Videoview holder created");
videoSurfaceCreated = true;
if (controller.getStatus() == PlayerStatus.AWAITING_VIDEO_SURFACE) {
if (controller.getStatus() == PlayerStatus.PLAYING) {
if (controller.serviceAvailable()) {
controller.setVideoSurface(holder);
} else {
@ -235,6 +245,8 @@ public class VideoplayerActivity extends MediaplayerActivity implements
videoSurfaceCreated = false;
controller.notifyVideoSurfaceAbandoned();
}
};
@Override
protected void onReloadNotification(int notificationCode) {

View File

@ -137,10 +137,12 @@ public class ExternalPlayerFragment extends Fragment {
}
@Override
public void loadMediaInfo() {
public boolean loadMediaInfo() {
ExternalPlayerFragment fragment = ExternalPlayerFragment.this;
if (fragment != null) {
fragment.loadMediaInfo();
return fragment.loadMediaInfo();
} else {
return false;
}
}
@ -209,7 +211,7 @@ public class ExternalPlayerFragment extends Fragment {
}
}
private void loadMediaInfo() {
private boolean loadMediaInfo() {
if (AppConfig.DEBUG)
Log.d(TAG, "Loading media info");
if (controller.serviceAvailable()) {
@ -230,13 +232,16 @@ public class ExternalPlayerFragment extends Fragment {
} else {
butPlay.setVisibility(View.VISIBLE);
}
return true;
} else {
Log.w(TAG,
"loadMediaInfo was called while the media object of playbackService was null!");
return false;
}
} else {
Log.w(TAG,
"loadMediaInfo was called while playbackService was null!");
return false;
}
}

View File

@ -14,6 +14,7 @@ import android.media.RemoteControlClient;
import android.media.RemoteControlClient.MetadataEditor;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
@ -133,8 +134,6 @@ public class PlaybackService extends Service {
private final IBinder mBinder = new LocalBinder();
private volatile List<FeedItem> queue;
public class LocalBinder extends Binder {
public PlaybackService getService() {
return PlaybackService.this;
@ -322,6 +321,7 @@ public class PlaybackService extends Service {
}
public void notifyVideoSurfaceAbandoned() {
stopForeground(true);
mediaPlayer.resetVideoSurface();
}
@ -352,6 +352,7 @@ public class PlaybackService extends Service {
private final PlaybackServiceMediaPlayer.PSMPCallback mediaPlayerCallback = new PlaybackServiceMediaPlayer.PSMPCallback() {
@Override
public void statusChanged(PlaybackServiceMediaPlayer.PSMPInfo newInfo) {
currentMediaType = mediaPlayer.getCurrentMediaType();
switch (newInfo.playerStatus) {
case PREPARED:
taskManager.startChapterLoader(newInfo.playable);
@ -365,8 +366,8 @@ public class PlaybackService extends Service {
break;
case STOPPED:
setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
stopSelf();
//setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
//stopSelf();
break;
case PLAYING:
@ -375,7 +376,6 @@ public class PlaybackService extends Service {
if (AppConfig.DEBUG)
Log.d(TAG, "Resuming/Starting playback");
currentMediaType = mediaPlayer.getCurrentMediaType();
writePlaybackPreferences();
taskManager.startPositionSaver();
taskManager.startWidgetUpdater();
@ -463,16 +463,19 @@ public class PlaybackService extends Service {
if (media instanceof FeedMedia) {
FeedItem item = ((FeedMedia) media).getItem();
DBWriter.markItemRead(PlaybackService.this, item, true, true);
nextItem = DBTasks.getQueueSuccessorOfItem(this, item.getId(), queue);
try {
final List<FeedItem> queue = taskManager.getQueue();
isInQueue = QueueAccess.ItemListAccess(queue).contains(((FeedMedia) media).getItem().getId());
nextItem = DBTasks.getQueueSuccessorOfItem(this, item.getId(), queue);
} catch (InterruptedException e) {
e.printStackTrace();
// isInQueue remains false
}
if (isInQueue) {
DBWriter.removeQueueItem(PlaybackService.this, item.getId(), true);
}
DBWriter.addItemToPlaybackHistory(PlaybackService.this, (FeedMedia) media);
long autoDeleteMediaId = ((FeedComponent) media).getId();
if (mediaPlayer.isStreaming()) {
autoDeleteMediaId = -1;
}
}
// Load next episode if previous episode was in the queue and if there
@ -514,7 +517,7 @@ public class PlaybackService extends Service {
notificationCode);
} else {
sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0);
stopSelf();
//stopSelf();
}
}
@ -713,8 +716,6 @@ public class PlaybackService extends Service {
}
private void updateWidget() {
if (AppConfig.DEBUG)
Log.d(TAG, "Sending widget update request");
PlaybackService.this.sendBroadcast(new Intent(
PlayerWidget.FORCE_WIDGET_UPDATE));
}
@ -729,6 +730,10 @@ public class PlaybackService extends Service {
@SuppressLint("NewApi")
private RemoteControlClient setupRemoteControlClient() {
if (Build.VERSION.SDK_INT < 14) {
return null;
}
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setComponent(new ComponentName(getPackageName(),
MediaButtonReceiver.class.getName()));
@ -806,6 +811,7 @@ public class PlaybackService extends Service {
i.putExtra("album", info.playable.getFeedTitle());
i.putExtra("track", info.playable.getEpisodeTitle());
i.putExtra("playing", isPlaying);
final List<FeedItem> queue = taskManager.getQueueIfLoaded();
if (queue != null) {
i.putExtra("ListSize", queue.size());
}
@ -896,6 +902,10 @@ public class PlaybackService extends Service {
mediaPlayer.resume();
}
public void prepare() {
mediaPlayer.prepare();
}
public void pause(boolean abandonAudioFocus, boolean reinit) {
mediaPlayer.pause(abandonAudioFocus, reinit);
}
@ -904,6 +914,10 @@ public class PlaybackService extends Service {
mediaPlayer.reinit();
}
public PlaybackServiceMediaPlayer.PSMPInfo getPSMPInfo() {
return mediaPlayer.getPSMPInfo();
}
public PlayerStatus getStatus() {
return mediaPlayer.getPSMPInfo().playerStatus;
}

View File

@ -144,15 +144,16 @@ public class PlaybackServiceMediaPlayer {
return;
} else {
// stop playback of this episode
setPlayerStatus(PlayerStatus.STOPPED, null);
if (playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PLAYING || playerStatus == PlayerStatus.PREPARED) {
mediaPlayer.stop();
}
setPlayerStatus(PlayerStatus.INDETERMINATE, null);
}
}
createMediaPlayer();
this.media = playable;
this.stream = stream;
this.mediaType = media.getMediaType();
PlaybackServiceMediaPlayer.this.startWhenPrepared.set(startWhenPrepared);
setPlayerStatus(PlayerStatus.INITIALIZING, media);
try {
@ -372,8 +373,8 @@ public class PlaybackServiceMediaPlayer {
|| playerStatus == PlayerStatus.PAUSED
|| playerStatus == PlayerStatus.PREPARED) {
if (stream) {
statusBeforeSeeking = playerStatus;
setPlayerStatus(PlayerStatus.SEEKING, media);
// statusBeforeSeeking = playerStatus;
// setPlayerStatus(PlayerStatus.SEEKING, media);
}
mediaPlayer.seekTo(t);
@ -505,9 +506,8 @@ public class PlaybackServiceMediaPlayer {
private void setSpeedSync(float speed) {
playerLock.lock();
if (media != null && media.getMediaType() == MediaType.AUDIO) {
AudioPlayer audioPlayer = (AudioPlayer) mediaPlayer;
if (audioPlayer.canSetSpeed()) {
audioPlayer.setPlaybackSpeed((float) speed);
if (mediaPlayer.canSetSpeed()) {
mediaPlayer.setPlaybackSpeed((float) speed);
if (AppConfig.DEBUG)
Log.d(TAG, "Playback speed was set to " + speed);
callback.playbackSpeedChanged(speed);

View File

@ -102,6 +102,23 @@ public class PlaybackServiceTaskManager {
}
}
/**
* Returns the queue if it is already loaded or null if it hasn't been loaded yet.
* In order to wait until the queue has been loaded, use getQueue()
*/
public synchronized List<FeedItem> getQueueIfLoaded() {
if (queueFuture.isDone()) {
try {
return queueFuture.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
return null;
}
/**
* Returns the queue or waits until the PSTM has loaded the queue from the database.
*/

View File

@ -1,6 +1,7 @@
package de.danoeh.antennapod.service.playback;
public enum PlayerStatus {
INDETERMINATE, // player is currently changing its state, listeners should wait until the player has left this state.
ERROR,
PREPARING,
PAUSED,
@ -8,7 +9,6 @@ public enum PlayerStatus {
STOPPED,
PREPARED,
SEEKING,
AWAITING_VIDEO_SURFACE, // player has been initialized and the media type to be played is a video.
INITIALIZING, // playback service is loading the Playable's metadata
INITIALIZED // playback service was started, data source of media player was set.
}

View File

@ -72,9 +72,11 @@ public class PlayerWidgetService extends Service {
}
private void updateViews() {
if (playbackService == null) {
return;
}
isUpdating = true;
if (AppConfig.DEBUG)
Log.d(TAG, "Updating widget views");
ComponentName playerWidget = new ComponentName(this, PlayerWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
RemoteViews views = new RemoteViews(getPackageName(),
@ -101,8 +103,6 @@ public class PlayerWidgetService extends Service {
views.setOnClickPendingIntent(R.id.butPlay,
createMediaButtonIntent());
} else {
if (AppConfig.DEBUG)
Log.d(TAG, "No media playing. Displaying defaultt views");
views.setViewVisibility(R.id.txtvProgress, View.INVISIBLE);
views.setTextViewText(R.id.txtvTitle,
this.getString(R.string.no_media_playing_label));

View File

@ -33,6 +33,7 @@ import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.preferences.PlaybackPreferences;
import de.danoeh.antennapod.service.playback.PlaybackService;
import de.danoeh.antennapod.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.service.playback.PlayerStatus;
import de.danoeh.antennapod.storage.DBTasks;
import de.danoeh.antennapod.util.Converter;
@ -48,7 +49,7 @@ public abstract class PlaybackController {
public static final int DEFAULT_SEEK_DELTA = 30000;
public static final int INVALID_TIME = -1;
private Activity activity;
private final Activity activity;
private PlaybackService playbackService;
private Playable media;
@ -70,6 +71,8 @@ public abstract class PlaybackController {
private boolean reinitOnPause;
public PlaybackController(Activity activity, boolean reinitOnPause) {
if (activity == null)
throw new IllegalArgumentException("activity = null");
this.activity = activity;
this.reinitOnPause = reinitOnPause;
schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOLSIZE,
@ -298,7 +301,9 @@ public abstract class PlaybackController {
if (AppConfig.DEBUG)
Log.d(TAG, "Received statusUpdate Intent.");
if (isConnectedToPlaybackService()) {
status = playbackService.getStatus();
PlaybackServiceMediaPlayer.PSMPInfo info = playbackService.getPSMPInfo();
status = info.playerStatus;
media = info.playable;
handleStatus();
} else {
Log.w(TAG,
@ -422,6 +427,9 @@ public abstract class PlaybackController {
case PLAYING:
clearStatusMsg();
checkMediaInfoLoaded();
if (PlaybackService.getCurrentMediaType() == MediaType.VIDEO) {
onAwaitingVideoSurface();
}
setupPositionObserver();
updatePlayButtonAppearance(pauseResource);
break;
@ -447,9 +455,6 @@ public abstract class PlaybackController {
case SEEKING:
postStatusMsg(R.string.player_seeking_msg);
break;
case AWAITING_VIDEO_SURFACE:
onAwaitingVideoSurface();
break;
case INITIALIZED:
checkMediaInfoLoaded();
clearStatusMsg();
@ -459,10 +464,7 @@ public abstract class PlaybackController {
}
private void checkMediaInfoLoaded() {
if (!mediaInfoLoaded) {
loadMediaInfo();
}
mediaInfoLoaded = true;
mediaInfoLoaded = (mediaInfoLoaded || loadMediaInfo());
}
private void updatePlayButtonAppearance(int resource) {
@ -476,7 +478,7 @@ public abstract class PlaybackController {
public abstract void clearStatusMsg();
public abstract void loadMediaInfo();
public abstract boolean loadMediaInfo();
public abstract void onAwaitingVideoSurface();
@ -490,6 +492,7 @@ public abstract class PlaybackController {
if (playbackService != null) {
status = playbackService.getStatus();
media = playbackService.getPlayable();
/*
if (media == null) {
Log.w(TAG,
"PlaybackService has no media object. Trying to restore last played media.");
@ -498,6 +501,7 @@ public abstract class PlaybackController {
activity.startService(serviceIntent);
}
}
*/
onServiceQueried();
setupGUI();
@ -570,7 +574,7 @@ public abstract class PlaybackController {
break;
case INITIALIZED:
playbackService.setStartWhenPrepared(true);
playbackService.resume();
playbackService.prepare();
break;
}
} else {

View File

@ -82,7 +82,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
case INITIALIZED:
case INITIALIZING:
case SEEKING:
case AWAITING_VIDEO_SURFACE:
assertNotNull(info.playable);
break;
case STOPPED: