summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java29
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java36
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java59
-rw-r--r--app/src/main/res/layout/all_episodes_fragment.xml4
-rw-r--r--app/src/main/res/layout/empty_view_layout.xml30
-rw-r--r--app/src/main/res/layout/queue_fragment.xml10
-rw-r--r--app/src/main/res/xml/preferences_network.xml9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/UpdateManager.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java295
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Optional.java213
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java40
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java9
-rw-r--r--core/src/main/res/values/arrays.xml12
-rw-r--r--core/src/main/res/values/strings.xml20
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java2
30 files changed, 711 insertions, 198 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
index 78cc15b2c..fa5012b74 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -47,6 +47,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
*/
private boolean videoControlsShowing = true;
private boolean videoSurfaceCreated = false;
+ private boolean playbackStoppedUponExitVideo = false;
private boolean destroyingDueToReload = false;
private VideoControlsHider videoControlsHider = new VideoControlsHider(this);
@@ -77,6 +78,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onResume() {
super.onResume();
+ playbackStoppedUponExitVideo = false;
if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
playExternalMedia(getIntent(), MediaType.VIDEO);
} else if (PlaybackService.isCasting()) {
@@ -91,12 +93,32 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onStop() {
+ stopPlaybackIfUserPreferencesSpecified(); // MUST be called before super.onStop(), while it still has member variable controller
super.onStop();
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
videoControlsHider.stop();
}
}
+ void stopPlaybackIfUserPreferencesSpecified() {
+ // to avoid the method being called twice during leaving Videoplayer
+ // , which will double-pause the media
+ // (it is usually first called by surfaceHolderCallback.surfaceDestroyed(),
+ // then VideoplayerActivity.onStop() , but sometimes VideoplayerActivity.onStop()
+ // will first be invoked.)
+ if (playbackStoppedUponExitVideo) {
+ return;
+ }
+ playbackStoppedUponExitVideo = true;
+
+ if (controller != null && !destroyingDueToReload
+ && UserPreferences.getVideoBackgroundBehavior()
+ != UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) {
+ Log.v(TAG, "stop video playback per UserPreference");
+ controller.notifyVideoSurfaceAbandoned();
+ }
+ }
+
@Override
public void onUserLeaveHint () {
if (!PictureInPictureUtil.isInPictureInPictureMode(this) && UserPreferences.getVideoBackgroundBehavior()
@@ -275,13 +297,12 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
- Log.d(TAG, "Videosurface was destroyed");
+ Log.d(TAG, "Videosurface was destroyed." );
+ Log.v(TAG, " hasController=" + (controller != null)
+ + " , destroyingDueToReload=" + destroyingDueToReload
+ + " , videoBackgroundBehavior=" + UserPreferences.getVideoBackgroundBehavior());
videoSurfaceCreated = false;
- if (controller != null && !destroyingDueToReload
- && UserPreferences.getVideoBackgroundBehavior()
- != UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) {
- controller.notifyVideoSurfaceAbandoned();
- }
+ stopPlaybackIfUserPreferencesSpecified();
}
};
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
index 1286d9dc7..f54b9266e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
@@ -1,8 +1,6 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
-import android.support.annotation.NonNull;
-import android.content.Intent;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
@@ -30,7 +28,7 @@ import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
*/
public class DefaultActionButtonCallback implements ActionButtonCallback {
- private static final String TAG = "DefaultActionButtonCallback";
+ private static final String TAG = "DefaultActionBtnCb";
private final Context context;
@@ -84,13 +82,9 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
}
} else { // media is downloaded
if (media.isCurrentlyPlaying()) {
- new PlaybackServiceStarter(context, media)
- .startWhenPrepared(true)
- .shouldStream(false)
- .start();
IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
} else if (media.isCurrentlyPaused()) {
- new PlaybackServiceStarter(context, media)
+ new PlaybackServiceStarter(context, media) // need to start the service in case it's been stopped by system.
.startWhenPrepared(true)
.shouldStream(false)
.start();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
index beca6ab5c..5997d227a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -20,6 +20,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
+import android.widget.TextView;
import android.widget.Toast;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
@@ -74,6 +75,7 @@ public class AllEpisodesFragment extends Fragment {
RecyclerView recyclerView;
AllEpisodesRecycleAdapter listAdapter;
private ProgressBar progLoading;
+ private View emptyView;
List<FeedItem> episodes;
private List<Downloader> downloaderList;
@@ -331,17 +333,31 @@ public class AllEpisodesFragment extends Fragment {
onFragmentLoaded();
}
+ emptyView = (View) root.findViewById(R.id.emptyView);
+ emptyView.setVisibility(View.GONE);
+ ((TextView)emptyView.findViewById(R.id.emptyViewTitle)).setText(R.string.no_all_episodes_head_label);
+ ((TextView)emptyView.findViewById(R.id.emptyViewMessage)).setText(R.string.no_all_episodes_label);
+
return root;
}
private void onFragmentLoaded() {
- if (listAdapter == null) {
- MainActivity mainActivity = (MainActivity) getActivity();
- listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess,
- new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes());
- listAdapter.setHasStableIds(true);
- recyclerView.setAdapter(listAdapter);
+ if (episodes != null && episodes.size() > 0) {
+ if (listAdapter == null) {
+ MainActivity mainActivity = (MainActivity) getActivity();
+ listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess,
+ new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes());
+ listAdapter.setHasStableIds(true);
+ recyclerView.setAdapter(listAdapter);
+ }
+ emptyView.setVisibility(View.GONE);
+ recyclerView.setVisibility(View.VISIBLE);
+ } else {
+ listAdapter = null;
+ recyclerView.setVisibility(View.GONE);
+ emptyView.setVisibility(View.VISIBLE);
}
+
listAdapter.notifyDataSetChanged();
restoreScrollPosition();
getActivity().supportInvalidateOptionsMenu();
@@ -473,6 +489,7 @@ public class AllEpisodesFragment extends Fragment {
}
if (viewsCreated && !itemsLoaded) {
recyclerView.setVisibility(View.GONE);
+ emptyView.setVisibility(View.GONE);
progLoading.setVisibility(View.VISIBLE);
}
disposable = Observable.fromCallable(this::loadData)
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
index c26d7498e..b52fd444f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -21,6 +21,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -106,6 +107,11 @@ public class CompletedDownloadsFragment extends ListFragment {
if (items != null && getActivity() != null) {
onFragmentLoaded();
}
+
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setTitle(R.string.no_comp_downloads_head_label);
+ emptyView.setMessage(R.string.no_comp_downloads_label);
+ emptyView.attachToListView(getListView());
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
index 9f8f59f7f..973772049 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -24,6 +24,7 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -75,6 +76,12 @@ public class DownloadLogFragment extends ListFragment {
if (itemsLoaded) {
onFragmentLoaded();
}
+
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setTitle(R.string.no_log_downloads_head_label);
+ emptyView.setMessage(R.string.no_log_downloads_label);
+ emptyView.attachToListView(getListView());
+
}
private void onFragmentLoaded() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
index 70f82c2ec..cda89bbd3 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -8,6 +8,7 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.TextView;
import java.util.List;
@@ -50,6 +51,8 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = super.onCreateViewHelper(inflater, container, savedInstanceState,
R.layout.all_episodes_fragment);
+ ((TextView)root.findViewById(R.id.emptyViewTitle)).setText(R.string.no_fav_episodes_head_label);
+ ((TextView)root.findViewById(R.id.emptyViewMessage)).setText(R.string.no_fav_episodes_label);
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index 4ee7a06ad..124665a2f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -307,7 +307,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
- webvDescription.loadDataWithBaseURL(null, data, "text/html",
+ webvDescription.loadDataWithBaseURL("https://127.0.0.1", data, "text/html",
"utf-8", "about:blank");
Log.d(TAG, "Webview loaded");
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index bcca281d4..e6e02296d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -358,7 +358,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
private void onFragmentLoaded() {
if (webviewData != null) {
- webvDescription.loadDataWithBaseURL(null, webviewData, "text/html", "utf-8", "about:blank");
+ webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData, "text/html", "utf-8", "about:blank");
}
updateAppearance();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
index 335ee224b..c2b61bf75 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -7,9 +7,8 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
+import android.widget.TextView;
import java.util.List;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
import de.danoeh.antennapod.core.event.FeedItemEvent;
@@ -26,9 +25,7 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
public class NewEpisodesFragment extends AllEpisodesFragment {
public static final String TAG = "NewEpisodesFragment";
-
private static final String PREF_NAME = "PrefNewEpisodesFragment";
-
@Override
protected boolean showOnlyNewEpisodes() { return true; }
@@ -49,6 +46,8 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = super.onCreateViewHelper(inflater, container, savedInstanceState,
R.layout.all_episodes_fragment);
+ ((TextView)root.findViewById(R.id.emptyViewTitle)).setText(R.string.no_new_episodes_head_label);
+ ((TextView)root.findViewById(R.id.emptyViewMessage)).setText(R.string.no_new_episodes_label);
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index c2a9200c8..bd4a43f83 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -29,6 +29,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import de.greenrobot.event.EventBus;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -81,6 +82,12 @@ public class PlaybackHistoryFragment extends ListFragment {
if (itemsLoaded) {
onFragmentLoaded();
}
+
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setTitle(R.string.no_history_head_label);
+ emptyView.setMessage(R.string.no_history_label);
+ emptyView.attachToListView(getListView());
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index d65c6ae18..0e8f2f083 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -72,7 +72,7 @@ public class QueueFragment extends Fragment {
private TextView infoBar;
private RecyclerView recyclerView;
private QueueRecyclerAdapter recyclerAdapter;
- private TextView txtvEmpty;
+ private View emptyView;
private ProgressBar progLoading;
private List<FeedItem> queue;
@@ -493,9 +493,12 @@ public class QueueFragment extends Fragment {
}
);
itemTouchHelper.attachToRecyclerView(recyclerView);
+ //empty view
+ emptyView = (View) root.findViewById(R.id.emptyView);
+ emptyView.setVisibility(View.GONE);
+ ((TextView)emptyView.findViewById(R.id.emptyViewTitle)).setText(R.string.no_items_header_label);
+ ((TextView)emptyView.findViewById(R.id.emptyViewMessage)).setText(R.string.no_items_label);
- txtvEmpty = root.findViewById(android.R.id.empty);
- txtvEmpty.setVisibility(View.GONE);
progLoading = root.findViewById(R.id.progLoading);
progLoading.setVisibility(View.VISIBLE);
@@ -503,19 +506,20 @@ public class QueueFragment extends Fragment {
}
private void onFragmentLoaded(final boolean restoreScrollPosition) {
- if (recyclerAdapter == null) {
- MainActivity activity = (MainActivity) getActivity();
- recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess,
- new DefaultActionButtonCallback(activity), itemTouchHelper);
- recyclerAdapter.setHasStableIds(true);
- recyclerView.setAdapter(recyclerAdapter);
- }
- if(queue == null || queue.size() == 0) {
- recyclerView.setVisibility(View.GONE);
- txtvEmpty.setVisibility(View.VISIBLE);
- } else {
- txtvEmpty.setVisibility(View.GONE);
+ if (queue != null && queue.size() > 0) {
+ if (recyclerAdapter == null) {
+ MainActivity activity = (MainActivity) getActivity();
+ recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess,
+ new DefaultActionButtonCallback(activity), itemTouchHelper);
+ recyclerAdapter.setHasStableIds(true);
+ recyclerView.setAdapter(recyclerAdapter);
+ }
+ emptyView.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
+ } else {
+ recyclerAdapter = null;
+ recyclerView.setVisibility(View.GONE);
+ emptyView.setVisibility(View.VISIBLE);
}
if (restoreScrollPosition) {
@@ -628,7 +632,7 @@ public class QueueFragment extends Fragment {
}
if (queue == null) {
recyclerView.setVisibility(View.GONE);
- txtvEmpty.setVisibility(View.GONE);
+ emptyView.setVisibility(View.GONE);
progLoading.setVisibility(View.VISIBLE);
}
disposable = Observable.fromCallable(DBReader::getQueue)
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
index 66c59b7f7..3c40b542c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -20,6 +20,7 @@ import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import de.greenrobot.event.EventBus;
/**
@@ -44,6 +45,12 @@ public class RunningDownloadsFragment extends ListFragment {
adapter = new DownloadlistAdapter(getActivity(), itemAccess);
setListAdapter(adapter);
+
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setTitle(R.string.no_run_downloads_head_label);
+ emptyView.setMessage(R.string.no_run_downloads_label);
+ emptyView.attachToListView(getListView());
+
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
new file mode 100644
index 000000000..e3fd63235
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
@@ -0,0 +1,59 @@
+package de.danoeh.antennapod.view;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import de.danoeh.antennapod.R;
+
+public class EmptyViewHandler extends View {
+ private Activity activity;
+ private int title;
+ private int message;
+
+ public EmptyViewHandler(Context context) {
+ super(context);
+ this.setActivity((Activity) context);
+ }
+
+ public int getTitle() {
+ return title;
+ }
+
+ public void setTitle(int title) {
+ this.title = title;
+ }
+
+ public int getMessage() {
+ return message;
+ }
+
+ public void setMessage(int message) {
+ this.message = message;
+ }
+
+ public void attachToListView(ListView listView){
+
+ View emptyView = getActivity().getLayoutInflater().inflate(R.layout.empty_view_layout, null);
+ ((ViewGroup) listView.getParent()).addView(emptyView);
+ listView.setEmptyView(emptyView);
+
+ TextView tvTitle = (TextView) emptyView.findViewById(R.id.emptyViewTitle);
+ tvTitle.setText(title);
+
+ TextView tvMessage = (TextView) emptyView.findViewById(R.id.emptyViewMessage);
+ tvMessage.setText(message);
+
+ }
+
+ public Activity getActivity() {
+ return activity;
+ }
+
+ public void setActivity(Activity activity) {
+ this.activity = activity;
+ }
+}
diff --git a/app/src/main/res/layout/all_episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml
index 89f900a1f..099216007 100644
--- a/app/src/main/res/layout/all_episodes_fragment.xml
+++ b/app/src/main/res/layout/all_episodes_fragment.xml
@@ -17,6 +17,10 @@
tools:itemCount="13"
tools:listitem="@layout/new_episodes_listitem" />
+ <include
+ android:id="@+id/emptyView"
+ layout="@layout/empty_view_layout"/>
+
<ProgressBar
android:id="@+id/progLoading"
android:layout_width="wrap_content"
diff --git a/app/src/main/res/layout/empty_view_layout.xml b/app/src/main/res/layout/empty_view_layout.xml
new file mode 100644
index 000000000..051773e51
--- /dev/null
+++ b/app/src/main/res/layout/empty_view_layout.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <TextView
+ android:id="@+id/emptyViewTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ tools:text="empty"
+ android:textSize="20sp"
+ android:textStyle="bold"
+ android:paddingBottom="10dp"/>
+
+ <TextView
+ android:id="@+id/emptyViewMessage"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="15sp"
+ tools:text="empty"
+ android:paddingLeft="15dp"
+ android:paddingRight="15dp"
+ android:textAlignment="center"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/queue_fragment.xml b/app/src/main/res/layout/queue_fragment.xml
index 71bf16d30..cf00f2b1b 100644
--- a/app/src/main/res/layout/queue_fragment.xml
+++ b/app/src/main/res/layout/queue_fragment.xml
@@ -27,13 +27,9 @@
android:layout_below="@id/divider"
android:scrollbars="vertical"/>
- <TextView
- android:id="@id/android:empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_centerInParent="true"
- android:gravity="center"
- android:text="@string/no_items_label" />
+ <include
+ android:id="@+id/emptyView"
+ layout="@layout/empty_view_layout"/>
<ProgressBar
android:id="@+id/progLoading"
diff --git a/app/src/main/res/xml/preferences_network.xml b/app/src/main/res/xml/preferences_network.xml
index c37a99465..59dd0c51b 100644
--- a/app/src/main/res/xml/preferences_network.xml
+++ b/app/src/main/res/xml/preferences_network.xml
@@ -16,10 +16,11 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/download_pref_details">
- <SwitchPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefMobileUpdate"
+ <ListPreference
+ android:defaultValue="images"
+ android:entries="@array/mobile_update_entries"
+ android:entryValues="@array/mobile_update_values"
+ android:key="prefMobileUpdateAllowed"
android:summary="@string/pref_mobileUpdate_sum"
android:title="@string/pref_mobileUpdate_title"/>
<de.danoeh.antennapod.preferences.NumberPickerPreference
diff --git a/core/src/main/java/de/danoeh/antennapod/core/UpdateManager.java b/core/src/main/java/de/danoeh/antennapod/core/UpdateManager.java
index 29e2456b2..1b4cbc0ea 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/UpdateManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/UpdateManager.java
@@ -71,6 +71,11 @@ class UpdateManager {
UserPreferences.setEpisodeCleanupValue(oldValueInDays * 24);
} // else 0 or special negative values, no change needed
}
+ if (oldVersionCode < 1070197) {
+ if (prefs.getBoolean("prefMobileUpdate", false)) {
+ prefs.edit().putString(UserPreferences.PREF_MOBILE_UPDATE, "everything").apply();
+ }
+ }
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
index ec10b78aa..552c1b691 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
@@ -31,6 +31,6 @@ public class ApGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
- registry.append(String.class, InputStream.class, new ApOkHttpUrlLoader.Factory());
+ registry.replace(String.class, InputStream.class, new ApOkHttpUrlLoader.Factory());
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
index bd5276100..2e742e979 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
@@ -21,10 +21,8 @@ import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.service.download.HttpDownloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.NetworkUtils;
-import okhttp3.Interceptor;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.Response;
+import okhttp3.*;
+import okhttp3.internal.http.RealResponseBody;
/**
* @see com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
@@ -111,10 +109,16 @@ class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
@Override
public Response intercept(Chain chain) throws IOException {
- if (NetworkUtils.isDownloadAllowed()) {
+ if (NetworkUtils.isImageAllowed()) {
return chain.proceed(chain.request());
} else {
- return null;
+ return new Response.Builder()
+ .protocol(Protocol.HTTP_2)
+ .code(420)
+ .message("Policy Not Fulfilled")
+ .body(ResponseBody.create(null, new byte[0]))
+ .request(chain.request())
+ .build();
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
index 1feecd849..805f0c1b6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
@@ -77,7 +77,7 @@ public class UserPreferences {
// Network
private static final String PREF_ENQUEUE_DOWNLOADED = "prefEnqueueDownloaded";
public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
- private static final String PREF_MOBILE_UPDATE = "prefMobileUpdate";
+ public static final String PREF_MOBILE_UPDATE = "prefMobileUpdateAllowed";
public static final String PREF_EPISODE_CLEANUP = "prefEpisodeCleanup";
public static final String PREF_PARALLEL_DOWNLOADS = "prefParallelDownloads";
public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
@@ -380,8 +380,16 @@ public class UserPreferences {
}
}
+ public static String getMobileUpdatesEnabled() {
+ return prefs.getString(PREF_MOBILE_UPDATE, "images");
+ }
+
public static boolean isAllowMobileUpdate() {
- return prefs.getBoolean(PREF_MOBILE_UPDATE, false);
+ return getMobileUpdatesEnabled().equals("everything");
+ }
+
+ public static boolean isAllowMobileImages() {
+ return isAllowMobileUpdate() || getMobileUpdatesEnabled().equals("images");
}
public static int getParallelDownloads() {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index 827709350..7fe93a162 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -25,6 +25,7 @@ import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.v4.app.NotificationCompat;
+import android.support.v4.content.ContextCompat;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaBrowserServiceCompat;
import android.support.v4.media.MediaDescriptionCompat;
@@ -75,6 +76,11 @@ import de.greenrobot.event.EventBus;
/**
* Controls the MediaPlayer that plays a FeedMedia-file
+ *
+ * Callers should connect to the service with either:
+ * - .bindService()
+ * - ContextCompat.startForegroundService(), optionally with arguments, such as media to be played, in intent extras
+ *
*/
public class PlaybackService extends MediaBrowserServiceCompat {
/**
@@ -193,10 +199,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
*/
public static boolean isRunning = false;
/**
- * Is true if service has received a valid start command.
- */
- public static boolean started = false;
- /**
* Is true if the service was running, but paused due to headphone disconnect
*/
private static boolean transientPause = false;
@@ -264,9 +266,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Log.d(TAG, "Service created.");
isRunning = true;
- NotificationCompat.Builder notificationBuilder = createBasicNotification();
- startForeground(NOTIFICATION_ID, notificationBuilder.build());
-
registerReceiver(autoStateUpdated, new IntentFilter("com.google.android.gms.car.media.STATUS"));
registerReceiver(headsetDisconnected, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
registerReceiver(shutdownReceiver, new IntentFilter(ACTION_SHUTDOWN_PLAYBACK_SERVICE));
@@ -344,7 +343,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Log.d(TAG, "Service is about to be destroyed");
stopForeground(true);
isRunning = false;
- started = false;
currentMediaType = MediaType.UNKNOWN;
PreferenceManager.getDefaultSharedPreferences(this)
@@ -366,11 +364,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
taskManager.shutdown();
}
- private void stopService() {
- stopForeground(true);
- stopSelf();
- }
-
@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) {
Log.d(TAG, "OnGetRoot: clientPackageName=" + clientPackageName +
@@ -464,37 +457,32 @@ public class PlaybackService extends MediaBrowserServiceCompat {
final boolean castDisconnect = intent.getBooleanExtra(EXTRA_CAST_DISCONNECT, false);
Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE);
if (keycode == -1 && playable == null && !castDisconnect) {
- Log.e(TAG, "PlaybackService was started with no arguments");
- stopService();
+ // Typical cases when the service was started with no argument
+ // - when it is first bound, and then moved to startedState, as in <code>serviceManager.moveServiceToStartedState()</code>
+ // - callers (e.g., Controller) explicitly
+ Log.d(TAG, "PlaybackService was started with no arguments.");
return Service.START_NOT_STICKY;
}
- if ((flags & Service.START_FLAG_REDELIVERY) != 0) {
- Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.");
- stopForeground(true);
- } else {
- if (keycode != -1) {
- Log.d(TAG, "Received media button event");
- boolean handled = handleKeycode(keycode, true);
- if (!handled) {
- stopService();
- return Service.START_NOT_STICKY;
- }
- } else if (!flavorHelper.castDisconnect(castDisconnect) && playable != null) {
- started = true;
- boolean stream = intent.getBooleanExtra(EXTRA_SHOULD_STREAM,
- true);
- boolean startWhenPrepared = intent.getBooleanExtra(EXTRA_START_WHEN_PREPARED, false);
- boolean prepareImmediately = intent.getBooleanExtra(EXTRA_PREPARE_IMMEDIATELY, false);
- sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
- //If the user asks to play External Media, the casting session, if on, should end.
- flavorHelper.castDisconnect(playable instanceof ExternalMedia);
- if (playable instanceof FeedMedia) {
- playable = DBReader.getFeedMedia(((FeedMedia) playable).getId());
- }
- mediaPlayer.playMediaObject(playable, stream, startWhenPrepared, prepareImmediately);
+ if (keycode != -1) {
+ Log.d(TAG, "Received media button event");
+ boolean handled = handleKeycode(keycode, true);
+ if (!handled) {
+ // Just silently ignores unsupported keycode. Whether the service will
+ // continue to run is solely dependent on whether it is playing some media.
+ return Service.START_NOT_STICKY;
+ }
+ } else if (!flavorHelper.castDisconnect(castDisconnect) && playable != null) {
+ boolean stream = intent.getBooleanExtra(EXTRA_SHOULD_STREAM, true);
+ boolean startWhenPrepared = intent.getBooleanExtra(EXTRA_START_WHEN_PREPARED, false);
+ boolean prepareImmediately = intent.getBooleanExtra(EXTRA_PREPARE_IMMEDIATELY, false);
+ sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
+ //If the user asks to play External Media, the casting session, if on, should end.
+ flavorHelper.castDisconnect(playable instanceof ExternalMedia);
+ if (playable instanceof FeedMedia) {
+ playable = DBReader.getFeedMedia(((FeedMedia) playable).getId());
}
- setupNotification(playable);
+ mediaPlayer.playMediaObject(playable, stream, startWhenPrepared, prepareImmediately);
}
return Service.START_NOT_STICKY;
@@ -568,12 +556,23 @@ public class PlaybackService extends MediaBrowserServiceCompat {
mediaPlayer.seekDelta(-UserPreferences.getRewindSecs() * 1000);
return true;
case KeyEvent.KEYCODE_MEDIA_STOP:
+ // The logic gives UI illusion of stop by removing notification
+ // In the UI within AntennaPod, including widgets, it is seen as PAUSE, e.g.,
+ // users can still user on-screen widget to resume playing.
if (status == PlayerStatus.PLAYING) {
+ // Implementation note: Use of a state in serviceManager to tell it to
+ // show stop state UI (i.e., stopForeground(true)) is a bit awkward.
+ //
+ // More intuitive API would be for mediaPlayer.pause() returns a Future that
+ // returns after pause, including the related async notification work completes.
+ // However, it has its own complication, that mediaPlayer.pause() does not
+ // really know when all the related work completes, as they are buried into
+ // (asynchronous) callbacks.
+ serviceManager.treatNextPauseAsStopOnUI();
mediaPlayer.pause(true, true);
- started = false;
+ } else {
+ serviceManager.showUIForStopState();
}
-
- stopForeground(true); // gets rid of persistent notification
return true;
default:
Log.d(TAG, "Unhandled key code: " + keycode);
@@ -589,7 +588,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Playable playable = Playable.PlayableUtils.createInstanceFromPreferences(getApplicationContext());
if (playable != null) {
mediaPlayer.playMediaObject(playable, false, true, true);
- started = true;
PlaybackService.this.updateMediaSessionMetadata(playable);
}
}
@@ -604,10 +602,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
public void notifyVideoSurfaceAbandoned() {
+ Log.v(TAG, "notifyVideoSurfaceAbandoned()");
mediaPlayer.pause(true, false);
mediaPlayer.resetVideoSurface();
- setupNotification(getPlayable());
- stopForeground(!UserPreferences.isPersistNotify());
}
private final PlaybackServiceTaskManager.PSTMCallback taskManagerCallback = new PlaybackServiceTaskManager.PSTMCallback() {
@@ -670,27 +667,15 @@ public class PlaybackService extends MediaBrowserServiceCompat {
break;
case PAUSED:
- if ((UserPreferences.isPersistNotify() || isCasting) &&
- android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- // do not remove notification on pause based on user pref and whether android version supports expanded notifications
- // Change [Play] button to [Pause]
- setupNotification(newInfo);
- } else if (!UserPreferences.isPersistNotify() && !isCasting) {
- // remove notification on pause
- stopForeground(true);
- }
writePlayerStatusPlaybackPreferences();
break;
case STOPPED:
//writePlaybackPreferencesNoMediaPlaying();
- //stopService();
break;
case PLAYING:
writePlayerStatusPlaybackPreferences();
- setupNotification(newInfo);
- started = true;
// set sleep timer if auto-enabled
if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING &&
SleepTimerPreferences.autoEnable() && !sleepTimerActive()) {
@@ -701,7 +686,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case ERROR:
writePlaybackPreferencesNoMediaPlaying();
- stopService();
break;
}
@@ -714,7 +698,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void shouldStop() {
- stopService();
+ serviceManager.stopService();
}
@Override
@@ -763,7 +747,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
writePlaybackPreferencesNoMediaPlaying();
- stopService();
return true;
}
@@ -847,9 +830,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (stopPlaying) {
taskManager.cancelPositionSaver();
writePlaybackPreferencesNoMediaPlaying();
- if (!isCasting) {
- stopForeground(true);
- }
}
if (mediaType == null) {
sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0);
@@ -1063,6 +1043,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
private void updateMediaSession(final PlayerStatus playerStatus) {
PlaybackStateCompat.Builder sessionState = new PlaybackStateCompat.Builder();
+ @PlaybackStateCompat.State
int state;
if (playerStatus != null) {
switch (playerStatus) {
@@ -1126,7 +1107,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
flavorHelper.mediaSessionSetExtraForWear(mediaSession);
- mediaSession.setPlaybackState(sessionState.build());
+ final PlaybackStateCompat sessionStateBuilt = sessionState.build();
+ mediaSession.setPlaybackState(sessionStateBuilt);
+ serviceManager.onPlaybackStateChange(sessionStateBuilt);
}
private static boolean useSkipToPreviousForRewindInLockscreen() {
@@ -1180,7 +1163,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, imageLocation);
}
}
- if (!Thread.currentThread().isInterrupted() && started) {
+ if (!Thread.currentThread().isInterrupted() && isStarted()) {
mediaSession.setSessionActivity(PendingIntent.getActivity(this, 0,
PlaybackService.getPlayerActivityIntent(this),
PendingIntent.FLAG_UPDATE_CURRENT));
@@ -1203,21 +1186,14 @@ public class PlaybackService extends MediaBrowserServiceCompat {
*/
private Thread notificationSetupThread;
- /**
- * Prepares notification and starts the service in the foreground.
- */
- private void setupNotification(final PlaybackServiceMediaPlayer.PSMPInfo info) {
- setupNotification(info.playable);
- }
-
- private synchronized void setupNotification(final Playable playable) {
+ private synchronized void setupNotification(final Playable playable, boolean treatPauseAsStop) {
if (notificationSetupThread != null) {
notificationSetupThread.interrupt();
}
if (playable == null) {
Log.d(TAG, "setupNotification: playable is null");
- if (!started) {
- stopService();
+ if (!isStarted()) {
+ serviceManager.stopService();
}
return;
}
@@ -1226,12 +1202,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void run() {
- Log.d(TAG, "Starting background work");
+ Log.d(TAG, "notificationSetupTask: Starting background work");
if (mediaPlayer == null) {
Log.d(TAG, "notificationSetupTask: mediaPlayer is null");
- if (!started) {
- stopService();
+ if (!isStarted()) {
+ serviceManager.stopService();
}
return;
}
@@ -1256,8 +1232,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
PlayerStatus playerStatus = mediaPlayer.getPlayerStatus();
+ Log.v(TAG, "notificationSetupTask: playerStatus=" + playerStatus);
- if (!Thread.currentThread().isInterrupted() && started) {
+ if (!Thread.currentThread().isInterrupted() && isStarted()) {
String contentText = playable.getEpisodeTitle();
String contentTitle = playable.getFeedTitle();
Notification notification;
@@ -1349,15 +1326,33 @@ public class PlaybackService extends MediaBrowserServiceCompat {
playerStatus == PlayerStatus.PREPARING ||
playerStatus == PlayerStatus.SEEKING ||
isCasting) {
+ Log.v(TAG, "notificationSetupTask: make service foreground");
startForeground(NOTIFICATION_ID, notification);
+ } else if (playerStatus == PlayerStatus.PAUSED) {
+ if (treatPauseAsStop) {
+ stopForeground(true);
+ } else if ((UserPreferences.isPersistNotify() || isCasting) &&
+ android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ // do not remove notification on pause based on user pref and whether android version supports expanded notifications
+ // Change [Play] button to [Pause]
+ leaveNotificationAsBackground(notification);
+ } else if (!UserPreferences.isPersistNotify() && !isCasting) {
+ // remove notification on pause
+ stopForeground(true);
+ }
} else {
- stopForeground(false);
- NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- mNotificationManager.notify(NOTIFICATION_ID, notification);
+ leaveNotificationAsBackground(notification);
}
Log.d(TAG, "Notification set up");
}
}
+
+ private void leaveNotificationAsBackground(@NonNull Notification notification) {
+ stopForeground(false);
+ NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ mNotificationManager.notify(NOTIFICATION_ID, notification);
+ }
+
};
notificationSetupThread = new Thread(notificationSetupTask);
notificationSetupThread.start();
@@ -1552,7 +1547,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
- stopService();
+ serviceManager.stopService();
}
}
@@ -1850,8 +1845,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
void saveCurrentPosition(boolean fromMediaPlayer, Playable playable, int position);
- void setupNotification(boolean connected, PlaybackServiceMediaPlayer.PSMPInfo info);
-
MediaSessionCompat getMediaSession();
Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter);
@@ -1891,24 +1884,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
@Override
- public void setupNotification(boolean connected, PlaybackServiceMediaPlayer.PSMPInfo info) {
- if (connected) {
- PlaybackService.this.setupNotification(info);
- } else {
- PlayerStatus status = info.playerStatus;
- if ((status == PlayerStatus.PLAYING ||
- status == PlayerStatus.SEEKING ||
- status == PlayerStatus.PREPARING ||
- UserPreferences.isPersistNotify()) &&
- android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- PlaybackService.this.setupNotification(info);
- } else if (!UserPreferences.isPersistNotify()) {
- PlaybackService.this.stopForeground(true);
- }
- }
- }
-
- @Override
public MediaSessionCompat getMediaSession() {
return PlaybackService.this.mediaSession;
}
@@ -1923,4 +1898,116 @@ public class PlaybackService extends MediaBrowserServiceCompat {
PlaybackService.this.unregisterReceiver(receiver);
}
};
+
+ private boolean isStarted() {
+ return serviceManager.serviceInStartedState;
+ }
+
+ /**
+ * The helper that manages PlaybackService's foreground service life cycle and the associated
+ * notification control.
+ *
+ * The logic is adapted from a sample app from The Android Open Source Project.
+ * See https://github.com/googlesamples/android-MediaBrowserService/blob/6cf01be9ef82ca2dd653f03e2a4af0b075cc0021/Application/src/main/java/com/example/android/mediasession/service/MusicService.java#L211
+ *
+ */
+ private class ServiceManager {
+ private boolean serviceInStartedState;
+ private boolean toTreatNextPauseAsStopOnUI = false;
+
+ /**
+ *
+ * Entry point method for callers. Upon PlaybackState changes,
+ * the manager start/stop the PlaybackService as well as relevant notification
+ */
+ void onPlaybackStateChange(PlaybackStateCompat state) {
+ // Report the state to the MediaSession.
+
+ Log.v(TAG, "onPlaybackStateChange(" + (state != null ? state.getState() : "null") + ")");
+ try {
+ // Manage the started state of this service.
+ switch (state.getState()) {
+ case PlaybackStateCompat.STATE_CONNECTING:
+ // move the service to started, aka, making it foreground
+ // upon STATE_CONNECTING, i.e., in preparing to play a media.
+ // This is done so that in case the preparation takes a long time, e.g.,
+ // streaming over a slow network,
+ // the service won't be killed by the system prematurely.
+ moveServiceToStartedState(state);
+ break;
+ case PlaybackStateCompat.STATE_PLAYING:
+ moveServiceToStartedState(state);
+ break;
+ case PlaybackStateCompat.STATE_PAUSED:
+ updateNotificationForPause(state);
+ break;
+ case PlaybackStateCompat.STATE_STOPPED:
+ moveServiceOutOfStartedState(state);
+ break;
+ case PlaybackStateCompat.STATE_ERROR:
+ moveServiceOutOfStartedState(state);
+ break;
+ }
+ } finally {
+ if (toTreatNextPauseAsStopOnUI) {
+ Log.v(TAG, "onPlaybackStateChange() - toTreatNextPauseAsStopOnUI enabled. The actual state (should be PAUSED, aka 2): " + state.getState());
+ toTreatNextPauseAsStopOnUI = false;
+ }
+ }
+ }
+
+ /**
+ * Tell service manager that on the next state change, if the state is STATE_PAUSED,
+ * give UI treatment as if it is stopped.
+ *
+ * @see #handleKeycode(int, boolean) the use case
+ */
+ public void treatNextPauseAsStopOnUI() {
+ this.toTreatNextPauseAsStopOnUI = true;
+ }
+
+ public void showUIForStopState() {
+ Log.v(TAG, "serviceManager.showUIForStopState()");
+ stopForeground(true); // gets rid of persistent notification, to give the UI illusion of STOP
+ }
+
+ public void stopService() {
+ stopForeground(true);
+ stopSelf();
+ serviceInStartedState = false;
+ }
+
+ private void moveServiceToStartedState(PlaybackStateCompat state) {
+ if (!serviceInStartedState) {
+ ContextCompat.startForegroundService(
+ PlaybackService.this,
+ new Intent(PlaybackService.this, PlaybackService.class));
+ serviceInStartedState = true;
+ }
+
+ doSetupNotification();
+ }
+
+ private void updateNotificationForPause(PlaybackStateCompat state) {
+ doSetupNotification();
+ }
+
+ private void moveServiceOutOfStartedState(PlaybackStateCompat state) {
+ stopService();
+ }
+
+ private void doSetupNotification() {
+ if (mediaPlayer != null && mediaPlayer.getPlayable() != null) {
+ // it updates notification and set foreground status
+ // based on player status (similar to PlaybackState)
+ setupNotification(mediaPlayer.getPlayable(), toTreatNextPauseAsStopOnUI);
+ } else {
+ // should not happen unless there are bugs.
+ Log.e(TAG, "doSetupNotification() - unexpectedly there is no playable. No notification setup done. mediaPlayer." + mediaPlayer);
+ }
+ }
+ }
+
+ private final ServiceManager serviceManager = new ServiceManager();
+
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
index f1410b894..1daa43025 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
@@ -1253,9 +1253,15 @@ public class PodDBAdapter {
}
public final int getNumberOfNewItems() {
- final String query = "SELECT COUNT(" + KEY_ID + ")"
- + " FROM " + TABLE_NAME_FEED_ITEMS
- + " WHERE " + KEY_READ + "=" + FeedItem.NEW;
+ Object[] args = new String[]{
+ TABLE_NAME_FEED_ITEMS + "." + KEY_ID,
+ TABLE_NAME_FEED_ITEMS,
+ TABLE_NAME_FEEDS,
+ TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID,
+ TABLE_NAME_FEED_ITEMS + "." + KEY_READ + "=" + FeedItem.NEW
+ + " AND " + TABLE_NAME_FEEDS + "." + KEY_KEEP_UPDATED + " > 0"
+ };
+ final String query = String.format("SELECT COUNT(%s) FROM %s INNER JOIN %s ON %s WHERE %s", args);
Cursor c = db.rawQuery(query, null);
int result = 0;
if (c.moveToFirst()) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
index b34ba196d..9bdd375ce 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
@@ -90,9 +90,13 @@ public class NetworkUtils {
return info != null && info.isConnected();
}
- public static boolean isDownloadAllowed() {
- return UserPreferences.isAllowMobileUpdate() || !NetworkUtils.isNetworkMetered();
- }
+ public static boolean isDownloadAllowed() {
+ return UserPreferences.isAllowMobileUpdate() || !NetworkUtils.isNetworkMetered();
+ }
+
+ public static boolean isImageAllowed() {
+ return UserPreferences.isAllowMobileImages() || !NetworkUtils.isNetworkMetered();
+ }
private static boolean isNetworkMetered() {
ConnectivityManager connManager = (ConnectivityManager) context
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java b/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
new file mode 100644
index 000000000..0fe11ec53
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package de.danoeh.antennapod.core.util;
+
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+// AntennaPod's stripped-down version of Java/Android platform's java.util.Optional
+// so that it can be used on lower API level (API level 14)
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A container object which may or may not contain a non-null value.
+ * If a value is present, {@code isPresent()} will return {@code true} and
+ * {@code get()} will return the value.
+ *
+ * <p>Additional methods that depend on the presence or absence of a contained
+ * value are provided, such as {@link #orElse(java.lang.Object) orElse()}
+ * (return a default value if value not present) and
+ * {@link #ifPresent(java.util.function.Consumer) ifPresent()} (execute a block
+ * of code if the value is present).
+ *
+ * @since 1.8
+ */
+public final class Optional<T> {
+ /**
+ * Common instance for {@code empty()}.
+ */
+ private static final Optional<?> EMPTY = new Optional<>();
+
+ /**
+ * If non-null, the value; if null, indicates no value is present
+ */
+ private final T value;
+
+ /**
+ * Constructs an empty instance.
+ *
+ * @implNote Generally only one empty instance, {@link Optional#EMPTY},
+ * should exist per VM.
+ */
+ private Optional() {
+ this.value = null;
+ }
+
+ /**
+ * Returns an empty {@code Optional} instance. No value is present for this
+ * Optional.
+ *
+ * @apiNote Though it may be tempting to do so, avoid testing if an object
+ * is empty by comparing with {@code ==} against instances returned by
+ * {@code Option.empty()}. There is no guarantee that it is a singleton.
+ * Instead, use {@link #isPresent()}.
+ *
+ * @param <T> Type of the non-existent value
+ * @return an empty {@code Optional}
+ */
+ public static<T> Optional<T> empty() {
+ @SuppressWarnings("unchecked")
+ Optional<T> t = (Optional<T>) EMPTY;
+ return t;
+ }
+
+ /**
+ * Constructs an instance with the value present.
+ *
+ * @param value the non-null value to be present
+ * @throws NullPointerException if value is null
+ */
+ private Optional(T value) {
+ this.value = Objects.requireNonNull(value);
+ }
+
+ /**
+ * Returns an {@code Optional} with the specified present non-null value.
+ *
+ * @param <T> the class of the value
+ * @param value the value to be present, which must be non-null
+ * @return an {@code Optional} with the value present
+ * @throws NullPointerException if value is null
+ */
+ public static <T> Optional<T> of(T value) {
+ return new Optional<>(value);
+ }
+
+ /**
+ * Returns an {@code Optional} describing the specified value, if non-null,
+ * otherwise returns an empty {@code Optional}.
+ *
+ * @param <T> the class of the value
+ * @param value the possibly-null value to describe
+ * @return an {@code Optional} with a present value if the specified value
+ * is non-null, otherwise an empty {@code Optional}
+ */
+ public static <T> Optional<T> ofNullable(T value) {
+ return value == null ? empty() : of(value);
+ }
+
+ /**
+ * If a value is present in this {@code Optional}, returns the value,
+ * otherwise throws {@code NoSuchElementException}.
+ *
+ * @return the non-null value held by this {@code Optional}
+ * @throws NoSuchElementException if there is no value present
+ *
+ * @see Optional#isPresent()
+ */
+ public T get() {
+ if (value == null) {
+ throw new NoSuchElementException("No value present");
+ }
+ return value;
+ }
+
+ /**
+ * Return {@code true} if there is a value present, otherwise {@code false}.
+ *
+ * @return {@code true} if there is a value present, otherwise {@code false}
+ */
+ public boolean isPresent() {
+ return value != null;
+ }
+
+
+ /**
+ * Return the value if present, otherwise return {@code other}.
+ *
+ * @param other the value to be returned if there is no value present, may
+ * be null
+ * @return the value, if present, otherwise {@code other}
+ */
+ public T orElse(T other) {
+ return value != null ? value : other;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this Optional. The
+ * other object is considered equal if:
+ * <ul>
+ * <li>it is also an {@code Optional} and;
+ * <li>both instances have no value present or;
+ * <li>the present values are "equal to" each other via {@code equals()}.
+ * </ul>
+ *
+ * @param obj an object to be tested for equality
+ * @return {code true} if the other object is "equal to" this object
+ * otherwise {@code false}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof Optional)) {
+ return false;
+ }
+
+ Optional<?> other = (Optional<?>) obj;
+ return (value == other.value) || (value != null && value.equals(other.value));
+ }
+
+ /**
+ * Returns the hash code value of the present value, if any, or 0 (zero) if
+ * no value is present.
+ *
+ * @return hash code value of the present value or 0 if no value is present
+ */
+ @Override
+ public int hashCode() {
+ return value != null ? value.hashCode() : 0;
+ }
+
+ /**
+ * Returns a non-empty string representation of this Optional suitable for
+ * debugging. The exact presentation format is unspecified and may vary
+ * between implementations and versions.
+ *
+ * @implSpec If a value is present the result must include its string
+ * representation in the result. Empty and present Optionals must be
+ * unambiguously differentiable.
+ *
+ * @return the string representation of this instance
+ */
+ @Override
+ public String toString() {
+ return value != null
+ ? String.format("Optional[%s]", value)
+ : "Optional.empty";
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
index bc047bda5..6498b9ff1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
@@ -12,8 +12,6 @@ import android.media.MediaPlayer;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -37,6 +35,7 @@ import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.Optional;
import de.danoeh.antennapod.core.util.playback.Playable.PlayableUtils;
import io.reactivex.Maybe;
import io.reactivex.MaybeOnSubscribe;
@@ -105,6 +104,7 @@ public abstract class PlaybackController {
}
private synchronized void initServiceRunning() {
+ Log.v(TAG, "initServiceRunning()");
if (initialized) {
return;
}
@@ -187,22 +187,15 @@ public abstract class PlaybackController {
serviceBinder = Observable.fromCallable(this::getPlayLastPlayedMediaIntent)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(intent -> {
+ .subscribe(optionalIntent -> {
boolean bound = false;
- if (!PlaybackService.started) {
- if (intent != null) {
- Log.d(TAG, "Calling start service");
- ContextCompat.startForegroundService(activity, intent);
- bound = activity.bindService(intent, mConnection, 0);
- } else {
- status = PlayerStatus.STOPPED;
- setupGUI();
- handleStatus();
- }
+ if (optionalIntent.isPresent()) {
+ Log.d(TAG, "Calling bind service");
+ bound = activity.bindService(optionalIntent.get(), mConnection, 0);
} else {
- Log.d(TAG, "PlaybackService is running, trying to connect without start command.");
- bound = activity.bindService(new Intent(activity, PlaybackService.class),
- mConnection, 0);
+ status = PlayerStatus.STOPPED;
+ setupGUI();
+ handleStatus();
}
Log.d(TAG, "Result for service binding: " + bound);
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
@@ -212,24 +205,26 @@ public abstract class PlaybackController {
* Returns an intent that starts the PlaybackService and plays the last
* played media or null if no last played media could be found.
*/
- @Nullable private Intent getPlayLastPlayedMediaIntent() {
+ @NonNull
+ private Optional<Intent> getPlayLastPlayedMediaIntent() {
Log.d(TAG, "Trying to restore last played media");
Playable media = PlayableUtils.createInstanceFromPreferences(activity);
if (media == null) {
Log.d(TAG, "No last played media found");
- return null;
+ return Optional.empty();
}
+
boolean fileExists = media.localFileAvailable();
boolean lastIsStream = PlaybackPreferences.getCurrentEpisodeIsStream();
if (!fileExists && !lastIsStream && media instanceof FeedMedia) {
DBTasks.notifyMissingFeedMediaFile(activity, (FeedMedia) media);
}
- return new PlaybackServiceStarter(activity, media)
+ return Optional.of(new PlaybackServiceStarter(activity, media)
.startWhenPrepared(false)
.shouldStream(lastIsStream || !fileExists)
- .getIntent();
+ .getIntent());
}
@@ -587,7 +582,8 @@ public abstract class PlaybackController {
.startWhenPrepared(true)
.streamIfLastWasStream()
.start();
- Log.w(TAG, "Play/Pause button was pressed, but playbackservice was null!");
+ Log.d(TAG, "Play/Pause button was pressed, but playbackservice was null - " +
+ "it is likely to have been released by Android system. Restarting it.");
return;
}
switch (status) {
@@ -764,6 +760,7 @@ public abstract class PlaybackController {
}
public void notifyVideoSurfaceAbandoned() {
+ Log.v(TAG, "notifyVideoSurfaceAbandoned() - hasPlaybackService=" + (playbackService != null));
if (playbackService != null) {
playbackService.notifyVideoSurfaceAbandoned();
}
@@ -784,6 +781,7 @@ public abstract class PlaybackController {
}
private void initServiceNotRunning() {
+ Log.v(TAG, "initServiceNotRunning()");
mediaLoader = Maybe.create((MaybeOnSubscribe<Playable>) emitter -> {
Playable media = getMedia();
if (media != null) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java
index f7d2ee409..64cf61457 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java
@@ -2,14 +2,15 @@ package de.danoeh.antennapod.core.util.playback;
import android.content.Context;
import android.content.Intent;
-import android.media.MediaPlayer;
-import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
+import android.util.Log;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
public class PlaybackServiceStarter {
+ private static final String TAG = "PlaybackServiceStarter";
+
private final Context context;
private final Playable media;
private boolean startWhenPrepared = false;
@@ -66,6 +67,10 @@ public class PlaybackServiceStarter {
launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, shouldStream);
launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, prepareImmediately);
+ if (media == null) {
+ Log.e(TAG, "getIntent() - media is unexpectedly null. intent:" + launchIntent);
+ }
+
return launchIntent;
}
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index 157b040e1..6d310e0e5 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -55,6 +55,18 @@
<item>-1</item>
</string-array>
+ <string-array name="mobile_update_entries">
+ <item>@string/pref_mobileUpdate_nothing</item>
+ <item>@string/pref_mobileUpdate_images</item>
+ <item>@string/pref_mobileUpdate_everything</item>
+ </string-array>
+
+ <string-array name="mobile_update_values">
+ <item>nothing</item>
+ <item>images</item>
+ <item>everything</item>
+ </string-array>
+
<string-array name="episode_cleanup_entries">
<item>@string/episode_cleanup_queue_removal</item>
<item>0</item>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 6000873b5..3d730516e 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -339,10 +339,25 @@
<string name="enable_sonic">Enable Sonic</string>
<!-- Empty list labels -->
- <string name="no_items_label">There are no items in this list.</string>
+ <string name="no_items_header_label">No queued episodes</string>
+ <string name="no_items_label">You can add episodes to the queue by long-pressing or downloading them.</string>
<string name="no_feeds_label">You haven\'t subscribed to any podcasts yet.</string>
<string name="no_chapters_label">This episode has no chapters.</string>
<string name="no_shownotes_label">This episode has no shownotes.</string>
+ <string name="no_run_downloads_head_label">No downloads running</string>
+ <string name="no_run_downloads_label">You can download episodes on the podcast details screen.</string>
+ <string name="no_comp_downloads_head_label">No downloaded episodes</string>
+ <string name="no_comp_downloads_label">You can download episodes on the podcast details screen.</string>
+ <string name="no_log_downloads_head_label">No download log</string>
+ <string name="no_log_downloads_label">Download logs will appear here when available.</string>
+ <string name="no_history_head_label">No History</string>
+ <string name="no_history_label">After you listen to an episode, it will appear here.</string>
+ <string name="no_all_episodes_head_label">No Episodes</string>
+ <string name="no_all_episodes_label">When you add a podcast, the episodes will be shown here.</string>
+ <string name="no_new_episodes_head_label">No new episodes</string>
+ <string name="no_new_episodes_label">When new episodes arrive, they will be shown here.</string>
+ <string name="no_fav_episodes_head_label">No favorite episodes</string>
+ <string name="no_fav_episodes_label">You can add episodes to the favorites by long-pressing them.</string>
<!-- Preferences -->
<string name="storage_pref">Storage</string>
@@ -398,6 +413,9 @@
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth Reconnect</string>
<string name="pref_mobileUpdate_title">Mobile Updates</string>
<string name="pref_mobileUpdate_sum">Allow updates over the mobile data connection</string>
+ <string name="pref_mobileUpdate_nothing">Nothing</string>
+ <string name="pref_mobileUpdate_images">Images only</string>
+ <string name="pref_mobileUpdate_everything">Everything</string>
<string name="refreshing_label">Refreshing</string>
<string name="flattr_settings_label">Flattr settings</string>
<string name="pref_flattr_auth_title">Flattr sign-in</string>
diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
index 0cca2ffa4..a6b732a4f 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
@@ -151,7 +151,6 @@ public class PlaybackServiceFlavorHelper {
// hardware volume buttons control the local device volume
mediaRouter.setMediaSessionCompat(null);
unregisterWifiBroadcastReceiver();
- callback.setupNotification(false, info);
}
};
}
@@ -181,7 +180,6 @@ public class PlaybackServiceFlavorHelper {
// hardware volume buttons control the remote device volume
mediaRouter.setMediaSessionCompat(callback.getMediaSession());
registerWifiBroadcastReceiver();
- callback.setupNotification(true, info);
}
private void switchMediaPlayer(@NonNull PlaybackServiceMediaPlayer newPlayer,