Start PlaybackService lazily to allow using ForegroundService

This commit is contained in:
ByteHamster 2018-04-22 22:21:46 +02:00
parent 7ad176ce29
commit 3e98ab61ea
20 changed files with 234 additions and 75 deletions

View File

@ -1,6 +1,7 @@
package de.danoeh.antennapod.activity; package de.danoeh.antennapod.activity;
import android.content.Intent; import android.content.Intent;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewCompat;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -41,7 +42,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, false); launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, false);
launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY,
true); true);
startService(launchIntent); ContextCompat.startForegroundService(this, launchIntent);
} else if (PlaybackService.isCasting()) { } else if (PlaybackService.isCasting()) {
Intent intent = PlaybackService.getPlayerActivityIntent(this); Intent intent = PlaybackService.getPlayerActivityIntent(this);
if (intent.getComponent() != null && if (intent.getComponent() != null &&

View File

@ -30,6 +30,7 @@ import android.widget.ListView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.util.NotificationUtils; import de.danoeh.antennapod.core.util.NotificationUtils;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
@ -741,6 +742,15 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
loadData(); loadData();
} }
public void onEventMainThread(ServiceEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
switch(event.action) {
case SERVICE_STARTED:
externalPlayerFragment.connectToPlaybackService();
break;
}
}
public void onEventMainThread(ProgressEvent event) { public void onEventMainThread(ProgressEvent event) {
Log.d(TAG, "onEvent(" + event + ")"); Log.d(TAG, "onEvent(" + event + ")");
switch(event.action) { switch(event.action) {

View File

@ -34,6 +34,7 @@ import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import java.util.Locale; import java.util.Locale;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
@ -270,6 +271,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.release(); controller.release();
} }
controller = newPlaybackController(); controller = newPlaybackController();
setupGUI();
loadMediaInfo();
onPositionObserverUpdate();
} }
@Override @Override
@ -608,8 +612,23 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
super.onResume(); super.onResume();
Log.d(TAG, "onResume()"); Log.d(TAG, "onResume()");
StorageUtils.checkStorageAvailability(this); StorageUtils.checkStorageAvailability(this);
if(controller != null) { if (controller != null) {
controller.init(); if (PlaybackService.isRunning) {
controller.init();
} else {
controller.resumeServiceNotRunning();
}
}
}
public void onEventMainThread(ServiceEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
switch(event.action) {
case SERVICE_STARTED:
if (controller != null) {
controller.init();
}
break;
} }
} }
@ -853,6 +872,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
if(controller == null) { if(controller == null) {
return; return;
} }
controller.init();
controller.playPause(); controller.playPause();
} }

View File

@ -6,6 +6,7 @@ import android.graphics.drawable.ColorDrawable;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.WindowCompat; import android.support.v4.view.WindowCompat;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.util.Log; import android.util.Log;
@ -30,6 +31,7 @@ import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil; import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
import de.danoeh.antennapod.core.util.playback.ExternalMedia; import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.service.PlayerWidgetService;
import de.danoeh.antennapod.view.AspectRatioVideoView; import de.danoeh.antennapod.view.AspectRatioVideoView;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -89,7 +91,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, false); launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, false);
launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY,
true); true);
startService(launchIntent); ContextCompat.startForegroundService(this, launchIntent);
} else if (PlaybackService.isCasting()) { } else if (PlaybackService.isCasting()) {
Intent intent = PlaybackService.getPlayerActivityIntent(this); Intent intent = PlaybackService.getPlayerActivityIntent(this);
if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) { if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) {

View File

@ -81,9 +81,15 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
} }
} else { // media is downloaded } else { // media is downloaded
if (item.hasMedia() && item.getMedia().isCurrentlyPlaying()) { if (item.hasMedia() && item.getMedia().isCurrentlyPlaying()) {
if (!PlaybackService.isRunning) {
PlaybackService.startService(context, media, true, false);
}
context.sendBroadcast(new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE)); context.sendBroadcast(new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
} }
else if (item.hasMedia() && item.getMedia().isCurrentlyPaused()) { else if (item.hasMedia() && item.getMedia().isCurrentlyPaused()) {
if (!PlaybackService.isRunning) {
PlaybackService.startService(context, media, true, false);
}
context.sendBroadcast(new Intent(PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE)); context.sendBroadcast(new Intent(PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE));
} }
else { else {

View File

@ -37,7 +37,6 @@ public class ExternalPlayerFragment extends Fragment {
private ImageButton butPlay; private ImageButton butPlay;
private TextView mFeedName; private TextView mFeedName;
private ProgressBar mProgressBar; private ProgressBar mProgressBar;
private PlaybackController controller; private PlaybackController controller;
public ExternalPlayerFragment() { public ExternalPlayerFragment() {
@ -83,6 +82,11 @@ public class ExternalPlayerFragment extends Fragment {
controller.playPause(); controller.playPause();
} }
}); });
loadMediaInfo();
}
public void connectToPlaybackService() {
controller.init();
} }
private PlaybackController setupPlaybackController() { private PlaybackController setupPlaybackController() {
@ -123,7 +127,11 @@ public class ExternalPlayerFragment extends Fragment {
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
controller.init(); if (PlaybackService.isRunning) {
controller.init();
} else {
controller.resumeServiceNotRunning();
}
onPositionObserverUpdate(); onPositionObserverUpdate();
} }
@ -164,13 +172,12 @@ public class ExternalPlayerFragment extends Fragment {
private boolean loadMediaInfo() { private boolean loadMediaInfo() {
Log.d(TAG, "Loading media info"); Log.d(TAG, "Loading media info");
if (controller != null && controller.serviceAvailable()) { if (controller != null) {
Playable media = controller.getMedia(); Playable media = controller.getMedia();
if (media != null) { if (media != null) {
txtvTitle.setText(media.getEpisodeTitle()); txtvTitle.setText(media.getEpisodeTitle());
mFeedName.setText(media.getFeedTitle()); mFeedName.setText(media.getFeedTitle());
mProgressBar.setProgress((int) onPositionObserverUpdate();
((double) controller.getPosition() / controller.getDuration() * 100));
Glide.with(getActivity()) Glide.with(getActivity())
.load(media.getImageLocation()) .load(media.getImageLocation())

View File

@ -34,6 +34,7 @@ import com.bumptech.glide.Glide;
import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconButton; import com.joanzapata.iconify.widget.IconButton;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.NetworkUtils;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
@ -243,6 +244,9 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
if (item.hasMedia()) { if (item.hasMedia()) {
FeedMedia media = item.getMedia(); FeedMedia media = item.getMedia();
if (!media.isDownloaded()) { if (!media.isDownloaded()) {
if (!PlaybackService.isRunning) {
PlaybackService.startService(getActivity(), media, true, false);
}
DBTasks.playMedia(getActivity(), media, true, true, true); DBTasks.playMedia(getActivity(), media, true, true, true);
((MainActivity) getActivity()).dismissChildFragment(); ((MainActivity) getActivity()).dismissChildFragment();
} else { } else {

View File

@ -5,6 +5,7 @@ import android.appwidget.AppWidgetProvider;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -60,7 +61,7 @@ public class PlayerWidget extends AppWidgetProvider {
private void startUpdate(Context context) { private void startUpdate(Context context) {
Log.d(TAG, "startUpdate() called with: " + "context = [" + context + "]"); Log.d(TAG, "startUpdate() called with: " + "context = [" + context + "]");
context.startService(new Intent(context, PlayerWidgetService.class)); ContextCompat.startForegroundService(context, new Intent(context, PlayerWidgetService.class));
} }
private void stopUpdate(Context context) { private void stopUpdate(Context context) {

View File

@ -44,6 +44,7 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:contentDescription="@string/pause_label" android:contentDescription="@string/pause_label"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:src="?attr/av_play_big"
tools:src="@drawable/ic_play_arrow_white_36dp"/> tools:src="@drawable/ic_play_arrow_white_36dp"/>
<TextView <TextView

View File

@ -110,9 +110,9 @@
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:contentDescription="@string/pause_label" android:contentDescription="@string/pause_label"
android:src="?attr/av_pause" android:src="?attr/av_play"
android:scaleType="fitCenter" android:scaleType="fitCenter"
tools:src="@drawable/ic_pause_white_36dp" tools:src="@drawable/ic_play_arrow_white_24dp"
tools:background="@android:color/holo_green_dark" /> tools:background="@android:color/holo_green_dark" />
<ImageButton <ImageButton

View File

@ -0,0 +1,13 @@
package de.danoeh.antennapod.core.event;
public class ServiceEvent {
public enum Action {
SERVICE_STARTED
}
public final Action action;
public ServiceEvent(Action action) {
this.action = action;
}
}

View File

@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.receiver;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.support.v4.content.ContextCompat;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
@ -29,7 +30,7 @@ public class MediaButtonReceiver extends BroadcastReceiver {
Intent serviceIntent = new Intent(context, PlaybackService.class); Intent serviceIntent = new Intent(context, PlaybackService.class);
serviceIntent.putExtra(EXTRA_KEYCODE, event.getKeyCode()); serviceIntent.putExtra(EXTRA_KEYCODE, event.getKeyCode());
serviceIntent.putExtra(EXTRA_SOURCE, event.getSource()); serviceIntent.putExtra(EXTRA_SOURCE, event.getSource());
context.startService(serviceIntent); ContextCompat.startForegroundService(context, serviceIntent);
} }
} }

View File

@ -8,6 +8,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.util.ArrayMap; import android.support.v4.util.ArrayMap;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
@ -58,6 +59,18 @@ public class GpodnetSyncService extends Service {
private boolean syncSubscriptions = false; private boolean syncSubscriptions = false;
private boolean syncActions = false; private boolean syncActions = false;
private static final int NOTIFICATION_ID = 2;
@Override
public void onCreate() {
super.onCreate();
startForeground(NOTIFICATION_ID,
new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_ID_GPODNET)
.setSmallIcon(R.drawable.stat_notify_sync)
.setContentTitle(getString(R.string.gpodnet_main_label))
.setContentText(getString(R.string.synchronizing))
.build());
}
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
@ -110,6 +123,7 @@ public class GpodnetSyncService extends Service {
private synchronized void sync() { private synchronized void sync() {
if (!GpodnetPreferences.loggedIn() || !NetworkUtils.networkAvailable()) { if (!GpodnetPreferences.loggedIn() || !NetworkUtils.networkAvailable()) {
stopForeground(true);
stopSelf(); stopSelf();
return; return;
} }
@ -126,6 +140,7 @@ public class GpodnetSyncService extends Service {
} }
syncActions = false; syncActions = false;
} }
stopForeground(true);
stopSelf(); stopSelf();
} }
@ -394,7 +409,7 @@ public class GpodnetSyncService extends Service {
if (GpodnetPreferences.loggedIn()) { if (GpodnetPreferences.loggedIn()) {
Intent intent = new Intent(context, GpodnetSyncService.class); Intent intent = new Intent(context, GpodnetSyncService.class);
intent.putExtra(ARG_ACTION, ACTION_SYNC); intent.putExtra(ARG_ACTION, ACTION_SYNC);
context.startService(intent); ContextCompat.startForegroundService(context, intent);
} }
} }
@ -402,7 +417,7 @@ public class GpodnetSyncService extends Service {
if (GpodnetPreferences.loggedIn()) { if (GpodnetPreferences.loggedIn()) {
Intent intent = new Intent(context, GpodnetSyncService.class); Intent intent = new Intent(context, GpodnetSyncService.class);
intent.putExtra(ARG_ACTION, ACTION_SYNC_SUBSCRIPTIONS); intent.putExtra(ARG_ACTION, ACTION_SYNC_SUBSCRIPTIONS);
context.startService(intent); ContextCompat.startForegroundService(context, intent);
} }
} }
@ -410,7 +425,7 @@ public class GpodnetSyncService extends Service {
if (GpodnetPreferences.loggedIn()) { if (GpodnetPreferences.loggedIn()) {
Intent intent = new Intent(context, GpodnetSyncService.class); Intent intent = new Intent(context, GpodnetSyncService.class);
intent.putExtra(ARG_ACTION, ACTION_SYNC_ACTIONS); intent.putExtra(ARG_ACTION, ACTION_SYNC_ACTIONS);
context.startService(intent); ContextCompat.startForegroundService(context, intent);
} }
} }
} }

View File

@ -296,6 +296,7 @@ public class DownloadService extends Service {
setupNotificationBuilders(); setupNotificationBuilders();
requester = DownloadRequester.getInstance(); requester = DownloadRequester.getInstance();
startForeground(NOTIFICATION_ID, updateNotifications());
} }
@Override @Override
@ -353,7 +354,7 @@ public class DownloadService extends Service {
/** /**
* Updates the contents of the service's notifications. Should be called * Updates the contents of the service's notifications. Should be called
* before setupNotificationBuilders. * after setupNotificationBuilders.
*/ */
private Notification updateNotifications() { private Notification updateNotifications() {
if (notificationCompatBuilder == null) { if (notificationCompatBuilder == null) {

View File

@ -24,6 +24,7 @@ import android.os.Vibrator;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.StringRes; import android.support.annotation.StringRes;
import android.support.v4.content.ContextCompat;
import android.support.v4.media.MediaBrowserCompat; import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaBrowserServiceCompat; import android.support.v4.media.MediaBrowserServiceCompat;
import android.support.v4.media.MediaDescriptionCompat; import android.support.v4.media.MediaDescriptionCompat;
@ -49,6 +50,7 @@ import java.util.List;
import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.MessageEvent; import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
@ -314,6 +316,34 @@ public class PlaybackService extends MediaBrowserServiceCompat {
flavorHelper.initializeMediaPlayer(PlaybackService.this); flavorHelper.initializeMediaPlayer(PlaybackService.this);
mediaSession.setActive(true); mediaSession.setActive(true);
NotificationCompat.Builder notificationBuilder = createBasicNotification();
startForeground(NOTIFICATION_ID, notificationBuilder.build());
EventBus.getDefault().post(new ServiceEvent(ServiceEvent.Action.SERVICE_STARTED));
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
long currentlyPlayingMedia = PlaybackPreferences.getCurrentlyPlayingMedia();
Playable lastPlayable = Playable.PlayableUtils.createInstanceFromPreferences(
getApplicationContext(), (int) currentlyPlayingMedia, prefs);
setupNotification(lastPlayable);
}
private NotificationCompat.Builder createBasicNotification() {
final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext());
final PendingIntent pIntent = PendingIntent.getActivity(this, 0,
PlaybackService.getPlayerActivityIntent(this),
PendingIntent.FLAG_UPDATE_CURRENT);
return new NotificationCompat.Builder(
this, NotificationUtils.CHANNEL_ID_PLAYING)
.setContentTitle(getString(R.string.app_name))
.setContentText("Service is running") // Just in case the notification is not updated (should not occur)
.setOngoing(false)
.setContentIntent(pIntent)
.setWhen(0) // we don't need the time
.setSmallIcon(smallIcon)
.setPriority(NotificationCompat.PRIORITY_MIN);
} }
@Override @Override
@ -568,8 +598,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
} }
public void notifyVideoSurfaceAbandoned() { public void notifyVideoSurfaceAbandoned() {
stopForeground(!UserPreferences.isPersistNotify()); mediaPlayer.pause(true, false);
mediaPlayer.resetVideoSurface(); mediaPlayer.resetVideoSurface();
setupNotification(getPlayable());
stopForeground(!UserPreferences.isPersistNotify());
} }
private final PlaybackServiceTaskManager.PSTMCallback taskManagerCallback = new PlaybackServiceTaskManager.PSTMCallback() { private final PlaybackServiceTaskManager.PSTMCallback taskManagerCallback = new PlaybackServiceTaskManager.PSTMCallback() {
@ -763,6 +795,15 @@ public class PlaybackService extends MediaBrowserServiceCompat {
} }
}; };
public static void startService(final Context context, final Playable media, boolean startWhenPrepared, boolean shouldStream) {
Intent launchIntent = new Intent(context, PlaybackService.class);
launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, startWhenPrepared);
launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, shouldStream);
launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, true);
ContextCompat.startForegroundService(context, launchIntent);
}
private Playable getNextInQueue(final Playable currentMedia) { private Playable getNextInQueue(final Playable currentMedia) {
if (!(currentMedia instanceof FeedMedia)) { if (!(currentMedia instanceof FeedMedia)) {
Log.d(TAG, "getNextInQueue(), but playable not an instance of FeedMedia, so not proceeding"); Log.d(TAG, "getNextInQueue(), but playable not an instance of FeedMedia, so not proceeding");
@ -1172,10 +1213,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
* Prepares notification and starts the service in the foreground. * Prepares notification and starts the service in the foreground.
*/ */
private void setupNotification(final PlaybackServiceMediaPlayer.PSMPInfo info) { private void setupNotification(final PlaybackServiceMediaPlayer.PSMPInfo info) {
final PendingIntent pIntent = PendingIntent.getActivity(this, 0, setupNotification(info.playable);
PlaybackService.getPlayerActivityIntent(this), }
PendingIntent.FLAG_UPDATE_CURRENT);
private synchronized void setupNotification(final Playable playable) {
if (notificationSetupThread != null) { if (notificationSetupThread != null) {
notificationSetupThread.interrupt(); notificationSetupThread.interrupt();
} }
@ -1185,12 +1226,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override @Override
public void run() { public void run() {
Log.d(TAG, "Starting background work"); Log.d(TAG, "Starting background work");
if (info.playable != null) { if (playable != null) {
int iconSize = getResources().getDimensionPixelSize( int iconSize = getResources().getDimensionPixelSize(
android.R.dimen.notification_large_icon_width); android.R.dimen.notification_large_icon_width);
try { try {
icon = Glide.with(PlaybackService.this) icon = Glide.with(PlaybackService.this)
.load(info.playable.getImageLocation()) .load(playable.getImageLocation())
.asBitmap() .asBitmap()
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.centerCrop() .centerCrop()
@ -1209,24 +1250,18 @@ public class PlaybackService extends MediaBrowserServiceCompat {
return; return;
} }
PlayerStatus playerStatus = mediaPlayer.getPlayerStatus(); PlayerStatus playerStatus = mediaPlayer.getPlayerStatus();
final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext());
if (!Thread.currentThread().isInterrupted() && started && info.playable != null) { if (!Thread.currentThread().isInterrupted() && started && playable != null) {
String contentText = info.playable.getEpisodeTitle(); String contentText = playable.getEpisodeTitle();
String contentTitle = info.playable.getFeedTitle(); String contentTitle = playable.getFeedTitle();
Notification notification; Notification notification;
// Builder is v7, even if some not overwritten methods return its parent's v4 interface // Builder is v7, even if some not overwritten methods return its parent's v4 interface
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder( NotificationCompat.Builder notificationBuilder = createBasicNotification();
PlaybackService.this, NotificationUtils.CHANNEL_ID_PLAYING) notificationBuilder.setContentTitle(contentTitle)
.setContentTitle(contentTitle)
.setContentText(contentText) .setContentText(contentText)
.setOngoing(false) .setPriority(UserPreferences.getNotifyPriority())
.setContentIntent(pIntent) .setLargeIcon(icon); // set notification priority
.setLargeIcon(icon)
.setSmallIcon(smallIcon)
.setWhen(0) // we don't need the time
.setPriority(UserPreferences.getNotifyPriority()); // set notification priority
IntList compactActionList = new IntList(); IntList compactActionList = new IntList();
int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.Cursor; import android.database.Cursor;
import android.support.v4.content.ContextCompat;
import android.util.Log; import android.util.Log;
import java.util.ArrayList; import java.util.ArrayList;
@ -123,16 +124,7 @@ public final class DBTasks {
media); media);
} }
} }
// Start playback Service PlaybackService.startService(context, media, startWhenPrepared, shouldStream);
Intent launchIntent = new Intent(context, PlaybackService.class);
launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED,
startWhenPrepared);
launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM,
shouldStream);
launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY,
true);
context.startService(launchIntent);
if (showPlayer) { if (showPlayer) {
// Launch media player // Launch media player
context.startActivity(PlaybackService.getPlayerActivityIntent( context.startActivity(PlaybackService.getPlayerActivityIntent(

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.webkit.URLUtil; import android.webkit.URLUtil;
@ -81,7 +82,7 @@ public class DownloadRequester {
Intent launchIntent = new Intent(context, DownloadService.class); Intent launchIntent = new Intent(context, DownloadService.class);
launchIntent.putExtra(DownloadService.EXTRA_REQUEST, request); launchIntent.putExtra(DownloadService.EXTRA_REQUEST, request);
context.startService(launchIntent); ContextCompat.startForegroundService(context, launchIntent);
return true; return true;
} }

View File

@ -13,6 +13,7 @@ public class NotificationUtils {
public static final String CHANNEL_ID_DOWNLOADING = "downloading"; public static final String CHANNEL_ID_DOWNLOADING = "downloading";
public static final String CHANNEL_ID_PLAYING = "playing"; public static final String CHANNEL_ID_PLAYING = "playing";
public static final String CHANNEL_ID_ERROR = "error"; public static final String CHANNEL_ID_ERROR = "error";
public static final String CHANNEL_ID_GPODNET = "gpodnet";
public static void createChannels(Context context) { public static void createChannels(Context context) {
if (android.os.Build.VERSION.SDK_INT < 26) { if (android.os.Build.VERSION.SDK_INT < 26) {
@ -25,6 +26,7 @@ public class NotificationUtils {
mNotificationManager.createNotificationChannel(createChannelDownloading(context)); mNotificationManager.createNotificationChannel(createChannelDownloading(context));
mNotificationManager.createNotificationChannel(createChannelPlaying(context)); mNotificationManager.createNotificationChannel(createChannelPlaying(context));
mNotificationManager.createNotificationChannel(createChannelError(context)); mNotificationManager.createNotificationChannel(createChannelError(context));
mNotificationManager.createNotificationChannel(createChannelGpodnet(context));
} }
} }
@ -59,4 +61,12 @@ public class NotificationUtils {
mChannel.setDescription(c.getString(R.string.notification_channel_error_description)); mChannel.setDescription(c.getString(R.string.notification_channel_error_description));
return mChannel; return mChannel;
} }
@RequiresApi(api = Build.VERSION_CODES.O)
private static NotificationChannel createChannelGpodnet(Context c) {
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_GPODNET,
c.getString(R.string.notification_channel_gpodnet), NotificationManager.IMPORTANCE_MIN);
mChannel.setDescription(c.getString(R.string.notification_channel_gpodnet_description));
return mChannel;
}
} }

View File

@ -14,6 +14,7 @@ import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
@ -59,7 +60,7 @@ public abstract class PlaybackController {
private PlaybackService playbackService; private PlaybackService playbackService;
private Playable media; private Playable media;
private PlayerStatus status; private PlayerStatus status = PlayerStatus.STOPPED;
private final ScheduledThreadPoolExecutor schedExecutor; private final ScheduledThreadPoolExecutor schedExecutor;
private static final int SCHED_EX_POOLSIZE = 1; private static final int SCHED_EX_POOLSIZE = 1;
@ -69,6 +70,7 @@ public abstract class PlaybackController {
private boolean mediaInfoLoaded = false; private boolean mediaInfoLoaded = false;
private boolean released = false; private boolean released = false;
private boolean initialized = false;
private Subscription serviceBinder; private Subscription serviceBinder;
@ -92,10 +94,14 @@ public abstract class PlaybackController {
} }
/** /**
* Creates a new connection to the playbackService. Should be called in the * Creates a new connection to the playbackService.
* activity's onResume() method.
*/ */
public void init() { public synchronized void init() {
if (initialized) {
return;
}
initialized = true;
activity.registerReceiver(statusUpdate, new IntentFilter( activity.registerReceiver(statusUpdate, new IntentFilter(
PlaybackService.ACTION_PLAYER_STATUS_CHANGED)); PlaybackService.ACTION_PLAYER_STATUS_CHANGED));
@ -167,7 +173,7 @@ public abstract class PlaybackController {
*/ */
private void bindToService() { private void bindToService() {
Log.d(TAG, "Trying to connect to service"); Log.d(TAG, "Trying to connect to service");
if(serviceBinder != null) { if (serviceBinder != null) {
serviceBinder.unsubscribe(); serviceBinder.unsubscribe();
} }
serviceBinder = Observable.fromCallable(this::getPlayLastPlayedMediaIntent) serviceBinder = Observable.fromCallable(this::getPlayLastPlayedMediaIntent)
@ -178,7 +184,7 @@ public abstract class PlaybackController {
if (!PlaybackService.started) { if (!PlaybackService.started) {
if (intent != null) { if (intent != null) {
Log.d(TAG, "Calling start service"); Log.d(TAG, "Calling start service");
activity.startService(intent); ContextCompat.startForegroundService(activity, intent);
bound = activity.bindService(intent, mConnection, 0); bound = activity.bindService(intent, mConnection, 0);
} else { } else {
status = PlayerStatus.STOPPED; status = PlayerStatus.STOPPED;
@ -194,32 +200,37 @@ public abstract class PlaybackController {
}, error -> Log.e(TAG, Log.getStackTraceString(error))); }, error -> Log.e(TAG, Log.getStackTraceString(error)));
} }
private Playable getMediaFromPreferences() {
long currentlyPlayingMedia = PlaybackPreferences.getCurrentlyPlayingMedia();
if (currentlyPlayingMedia != PlaybackPreferences.NO_MEDIA_PLAYING) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
activity.getApplicationContext());
return PlayableUtils.createInstanceFromPreferences(activity,
(int) currentlyPlayingMedia, prefs);
}
return null;
}
/** /**
* Returns an intent that starts the PlaybackService and plays the last * Returns an intent that starts the PlaybackService and plays the last
* played media or null if no last played media could be found. * played media or null if no last played media could be found.
*/ */
private Intent getPlayLastPlayedMediaIntent() { private Intent getPlayLastPlayedMediaIntent() {
Log.d(TAG, "Trying to restore last played media"); Log.d(TAG, "Trying to restore last played media");
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( Playable media = getMediaFromPreferences();
activity.getApplicationContext()); if (media != null) {
long currentlyPlayingMedia = PlaybackPreferences.getCurrentlyPlayingMedia(); Intent serviceIntent = new Intent(activity, PlaybackService.class);
if (currentlyPlayingMedia != PlaybackPreferences.NO_MEDIA_PLAYING) { serviceIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
Playable media = PlayableUtils.createInstanceFromPreferences(activity, serviceIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, false);
(int) currentlyPlayingMedia, prefs); serviceIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, true);
if (media != null) { boolean fileExists = media.localFileAvailable();
Intent serviceIntent = new Intent(activity, PlaybackService.class); boolean lastIsStream = PlaybackPreferences.getCurrentEpisodeIsStream();
serviceIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media); if (!fileExists && !lastIsStream && media instanceof FeedMedia) {
serviceIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, false); DBTasks.notifyMissingFeedMediaFile(activity, (FeedMedia) media);
serviceIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, true);
boolean fileExists = media.localFileAvailable();
boolean lastIsStream = PlaybackPreferences.getCurrentEpisodeIsStream();
if (!fileExists && !lastIsStream && media instanceof FeedMedia) {
DBTasks.notifyMissingFeedMediaFile(activity, (FeedMedia) media);
}
serviceIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM,
lastIsStream || !fileExists);
return serviceIntent;
} }
serviceIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM,
lastIsStream || !fileExists);
return serviceIntent;
} }
Log.d(TAG, "No last played media found"); Log.d(TAG, "No last played media found");
return null; return null;
@ -511,7 +522,7 @@ public abstract class PlaybackController {
"PlaybackService has no media object. Trying to restore last played media."); "PlaybackService has no media object. Trying to restore last played media.");
Intent serviceIntent = getPlayLastPlayedMediaIntent(); Intent serviceIntent = getPlayLastPlayedMediaIntent();
if (serviceIntent != null) { if (serviceIntent != null) {
activity.startService(serviceIntent); ContextCompat.startForegroundService(activity, serviceIntent);
} }
} }
*/ */
@ -576,6 +587,7 @@ public abstract class PlaybackController {
public void playPause() { public void playPause() {
if (playbackService == null) { if (playbackService == null) {
PlaybackService.startService(activity, media, true, false);
Log.w(TAG, "Play/Pause button was pressed, but playbackservice was null!"); Log.w(TAG, "Play/Pause button was pressed, but playbackservice was null!");
return; return;
} }
@ -609,6 +621,8 @@ public abstract class PlaybackController {
public int getPosition() { public int getPosition() {
if (playbackService != null) { if (playbackService != null) {
return playbackService.getCurrentPosition(); return playbackService.getCurrentPosition();
} else if (media != null) {
return media.getPosition();
} else { } else {
return PlaybackService.INVALID_TIME; return PlaybackService.INVALID_TIME;
} }
@ -617,12 +631,17 @@ public abstract class PlaybackController {
public int getDuration() { public int getDuration() {
if (playbackService != null) { if (playbackService != null) {
return playbackService.getDuration(); return playbackService.getDuration();
} else if (media != null) {
return media.getDuration();
} else { } else {
return PlaybackService.INVALID_TIME; return PlaybackService.INVALID_TIME;
} }
} }
public Playable getMedia() { public Playable getMedia() {
if (media == null) {
media = getMediaFromPreferences();
}
return media; return media;
} }
@ -714,8 +733,13 @@ public abstract class PlaybackController {
} }
public boolean isPlayingVideoLocally() { public boolean isPlayingVideoLocally() {
return playbackService != null && PlaybackService.getCurrentMediaType() == MediaType.VIDEO if (PlaybackService.isCasting()) {
&& !PlaybackService.isCasting(); return false;
} else if (playbackService != null) {
return PlaybackService.getCurrentMediaType() == MediaType.VIDEO;
} else {
return getMedia() != null && getMedia().getMediaType() == MediaType.VIDEO;
}
} }
public Pair<Integer, Integer> getVideoSize() { public Pair<Integer, Integer> getVideoSize() {
@ -755,6 +779,18 @@ public abstract class PlaybackController {
} }
} }
public void resumeServiceNotRunning() {
if (getMedia().getMediaType() == MediaType.AUDIO) {
TypedArray res = activity.obtainStyledAttributes(new int[]{
de.danoeh.antennapod.core.R.attr.av_play_big});
getPlayButton().setImageResource(
res.getResourceId(0, de.danoeh.antennapod.core.R.drawable.ic_play_arrow_grey600_36dp));
res.recycle();
} else {
getPlayButton().setImageResource(R.drawable.ic_av_play_circle_outline_80dp);
}
}
/** /**
* Refreshes the current position of the media file that is playing. * Refreshes the current position of the media file that is playing.
*/ */

View File

@ -29,6 +29,7 @@
<string name="free_space_label">%1$s free</string> <string name="free_space_label">%1$s free</string>
<string name="episode_cache_full_title">Episode cache full</string> <string name="episode_cache_full_title">Episode cache full</string>
<string name="episode_cache_full_message">The episode cache limit has been reached. You can increase the cache size in the Settings.</string> <string name="episode_cache_full_message">The episode cache limit has been reached. You can increase the cache size in the Settings.</string>
<string name="synchronizing">Synchronizing…</string>
<!-- Statistics fragment --> <!-- Statistics fragment -->
<string name="total_time_listened_to_podcasts">Total time of podcasts played:</string> <string name="total_time_listened_to_podcasts">Total time of podcasts played:</string>
@ -719,4 +720,6 @@
<string name="notification_channel_playing_description">Allows to control playback</string> <string name="notification_channel_playing_description">Allows to control playback</string>
<string name="notification_channel_error">Errors</string> <string name="notification_channel_error">Errors</string>
<string name="notification_channel_error_description">Shown if something went wrong</string> <string name="notification_channel_error_description">Shown if something went wrong</string>
<string name="notification_channel_gpodnet">gpodder.net</string>
<string name="notification_channel_gpodnet_description">Shown while currently synchronizing</string>
</resources> </resources>