summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS48
-rw-r--r--app/build.gradle5
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java81
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java11
-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/dialog/RatingDialog.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java39
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java2
-rwxr-xr-xcontributers.template.py12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java23
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java455
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java184
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java9
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java2
18 files changed, 543 insertions, 446 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 891a9784c..6cc7ae30e 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -3,14 +3,19 @@ DEVELOPERS
Alan Orth
Alexander Terczka
+Alexei Bendebury
+Ali
alifeflow
amhokies
Anders Bo Rasmussen
+Anderson Mesquita
Andrew Gaul
Andrey Krutov
Anthony Lieuallen
axq
+Borjan Tchakaloff
brad
+Burt Wiley Snyder
ByteHamster
Cameron Banga
Christian Ludwig
@@ -32,6 +37,7 @@ EirikV
Eoin Mcloughlin
eraymond
Ercan Erden
+falko
Falko Lehmann
Hannes Achleitner
hannesa2
@@ -42,6 +48,7 @@ Humberto Fraga
InsidE
James Falcon
Jan Niehusmann
+Jatin Kumar
Jens Klingenberg
Jens Müller
Johan Liesén
@@ -61,8 +68,10 @@ Martin Fietz
Martin Olsson
mat tso
mateoeh
+Matias de Andrea
Matthew Gaffen
Matthias Schütz
+Matthieu De Beule
Maurice Gilden
Meir Schwarz
Michael Kaiser
@@ -70,12 +79,17 @@ Michael Scarito
Mike Chelen
minusf
MolarAmbiguity
+Mostafa Ahangarha
Mounir Lamouri
mr-intj
+Nathan Mascitelli
Nis Wechselberg
Oliver Crow
orelogo
+pachecosf
Paul Ortyl
+Petar Kukolj
+qkolj
Raghul
Raghul Jagannathan
recalculated
@@ -88,6 +102,7 @@ Serge
Seth Golub
sevenmaster
Shantana Hardy
+Shinichiro Fujiwara
Simon Danner
Simon Rutishauser
Simon Schubert
@@ -129,13 +144,13 @@ Dutch: e2jk, glotzbach, rwv, Vistaus
English: mfietz, sterylmreep
Estonian: Eraser
Finnish: danieloeh
-French: cactux, ChaoticMind, clombion, e2jk, lacouture, Matth78, mfietz, Poussinou, PRIMOKORN, repat, sterylmreep, TacoTheDank, Tilwa, vcariven, whenrow
+French: cactux, ChaoticMind, clombion, e2jk, lacouture, Matth78, mfietz, Poussinou, PRIMOKORN, repat, Sioul, sterylmreep, TacoTheDank, Tilwa, vcariven, whenrow
Galician: antiparvos, pikamoku, Raichely
-German: 112358, altegedanken, barilla, bitsunited, Buggi, ceving, ChaoticMind, Chaquotay, dab0015, dadosch, DerSilly, DJaeger, elkangaroo, enz, fidel, finsterwalder, Foso, GNi33, HolgerJeromin, kalei, lohmann, LostInWeb, mfietz, nilso, repat, SAPlayer, schafia, Schroedingberg, sevenmaster, sucaml, Teaspoon, theonlytruth, weltenwort, Wyrrrd, ypid
+German: 112358, altegedanken, barilla, benedikt.g, bitsunited, Buggi, ceving, ChaoticMind, Chaquotay, dab0015, dadosch, DerSilly, die_otto, DJaeger, elkangaroo, enz, fidel, finsterwalder, Foso, GNi33, hightower5, HolgerJeromin, kalei, lohmann, LostInWeb, mfietz, nilso, repat, SAPlayer, schafia, Schroedingberg, sevenmaster, sucaml, Teaspoon, theonlytruth, weltenwort, Wyrrrd, ypid
Greek: antonist, danieloeh, hua2016s, MSavoritias, pavlosv
Hebrew (Israel): amir.dafnyman, E1i9, mongoose4004, pinkasey, rellieberman, Yaron, הלוי11
-Hindi (India): nmabhinandan, purple.coder, siddhusengar
-Hungarian: glatz.balazs, lna91, naren93, tszauer, ttyborg42
+Hindi (India): ankitiitb1069, Isaasu, nmabhinandan, purple.coder, siddhusengar
+Hungarian: glatz.balazs, lna91, marthynw, naren93, tszauer, ttyborg42
Icelandic: marthjod
Indonesian: jff, luke137, rezafaiza, silvanael16
Italian: aalex70, allin, apanontin, Bonnee, giuseppep, Guybrush88, marco_pag, neonsoftware, sevenmaster, theloca95
@@ -146,23 +161,24 @@ Korean: changwoo, seungrye, skcha
Korean (South Korea): changwoo, seungrye
Lithuanian: naglis
Macedonian: krisfremen
-Norwegian: hakonanes, timbast
-Norwegian Bokmål: corkie, hakonanes
-Norwegian Bokmål (Norway): corkie, hakonanes, kongk, timbast
+Malayalam: rashivkp
+Norwegian: timbast
+Norwegian Bokmål: corkie, danieloeh, heraldo
+Norwegian Bokmål (Norway): corkie, heraldo, kongk, timbast
Persian: ahangarha, F7D
-Polish: Iwangelion, maniexx, mfloryan, thedead4fun
-Polish (Poland): d6210809, Iwangelion, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
-Portuguese: domingos86, emansije, smarquespt
-Portuguese (Brazil): alexupits, alysonborges, arua, caioau, carlo_valente, castrors, deandreamatias, edman, Firmino, jackmiras, Junin, lipefire, lluccia, lucasmotacr, mbaltar, rogervezaro, RubeensVinicius, SamWilliam, silvanael16
+Polish: Iwangelion, maniexx, mateossh, mfloryan
+Polish (Poland): d6210809, hiro2020, Iwangelion, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
+Portuguese: andersonvom, domingos86, emansije, smarquespt
+Portuguese (Brazil): alexupits, alysonborges, andersonvom, arua, caioau, carlo_valente, castrors, deandreamatias, edman, Firmino, jackmiras, Junin, lipefire, lluccia, lucasmotacr, mbaltar, rogervezaro, RubeensVinicius, SamWilliam, silvanael16
Romanian (Romania): corneliu.e, fuzzmz, ralienpp
Russian (Russia): astra1, btimofeev, Duke_Raven, GaynullinDima, MegMasters98, mercutiy, null, overmind88, s.chebotar, shams4real, skvheadless, un_logic, whereisthetea, zhenya97
Slovenian (Slovenia): panter23
-Spanish: AleksSyntek, coperfix, deandreamatias, domingos86, dvd1985, Fitoschido, frandavid100, hard_ware, javiercoll, Juanmuto, lambdani, LatinSuD, leogrignafini, palopezv, TacoTheDank, tres.14159, wakutiteo
-Spanish (Spain): dvd1985, e2jk, frandavid100, hard_ware, palopezv, Raichely, TacoTheDank
+Spanish: AleksSyntek, andersonvom, coperfix, deandreamatias, domingos86, dvd1985, Fitoschido, frandavid100, hard_ware, javiercoll, Juanmuto, lambdani, LatinSuD, leogrignafini, palopezv, TacoTheDank, tres.14159, wakutiteo
+Spanish (Spain): andersonvom, dvd1985, e2jk, frandavid100, hard_ware, palopezv, Raichely, TacoTheDank
Swahili (Kenya): BonfaceKilz
Swedish (Sweden): albin.brantin, Bio, bpnilsson, ChaoticMind, jony08, nilso, SharpMelon, TwoD
-Telugu: veeven
-Turkish: basarancaner, brsata, Erdy, golcuk, overbite
-Ukrainian (Ukraine): older, sergiyr, zhenya97
+Telugu: Isaasu, veeven
+Turkish: basarancaner, brsata, Erdy, golcuk, overbite, Slsdem
+Ukrainian (Ukraine): older, paul_sm, sergiyr, zhenya97
Vietnamese: abnvolk, nguyenvui, ppanhh, vietnamesel10n
Vietnamese (Vietnam): bizover
diff --git a/app/build.gradle b/app/build.gradle
index 572575182..942a98bbf 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -18,8 +18,9 @@ android {
// Version code schema:
// "1.2.3-SNAPSHOT" -> 1020300
// "1.2.3-RC4" -> 1020304
- versionCode 1070204
- versionName "1.7.2-RC4"
+ // "1.2.3" -> 1020395
+ versionCode 1070296
+ versionName "1.7.2b"
testApplicationId "de.test.antennapod"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
generatedDensities = []
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
index 8be57a074..08e7f6fc5 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
@@ -1,6 +1,9 @@
package de.test.antennapod.service.playback;
import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.runner.AndroidJUnit4;
import android.test.InstrumentationTestCase;
import java.util.ArrayList;
@@ -17,37 +20,46 @@ import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.greenrobot.event.EventBus;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static junit.framework.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* Test class for PlaybackServiceTaskManager
*/
-public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class PlaybackServiceTaskManagerTest {
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() {
PodDBAdapter.deleteDatabase();
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
+ @Before
+ public void setUp() {
// create new database
- PodDBAdapter.init(getInstrumentation().getTargetContext());
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ PodDBAdapter.init(context);
PodDBAdapter.deleteDatabase();
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.close();
}
+ @Test
public void testInit() {
- PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(getInstrumentation().getTargetContext(), defaultPSTM);
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(context, defaultPSTM);
pstm.shutdown();
}
private List<FeedItem> writeTestQueue(String pref) {
- final Context c = getInstrumentation().getTargetContext();
final int NUM_ITEMS = 10;
Feed f = new Feed(0, null, "title", "link", "d", null, null, null, null, "id", null, "null", "url", false);
f.setItems(new ArrayList<>());
@@ -66,8 +78,9 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
return f.getItems();
}
+ @Test
public void testGetQueueWriteBeforeCreation() throws InterruptedException {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
List<FeedItem> queue = writeTestQueue("a");
assertNotNull(queue);
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
@@ -80,8 +93,9 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
pstm.shutdown();
}
+ @Test
public void testGetQueueWriteAfterCreation() throws InterruptedException {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
List<FeedItem> testQueue = pstm.getQueue();
@@ -111,8 +125,9 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
pstm.shutdown();
}
+ @Test
public void testStartPositionSaver() throws InterruptedException {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
final int NUM_COUNTDOWNS = 2;
final int TIMEOUT = 3 * PlaybackServiceTaskManager.POSITION_SAVER_WAITING_INTERVAL;
final CountDownLatch countDownLatch = new CountDownLatch(NUM_COUNTDOWNS);
@@ -152,16 +167,18 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
pstm.shutdown();
}
+ @Test
public void testIsPositionSaverActive() {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
pstm.startPositionSaver();
assertTrue(pstm.isPositionSaverActive());
pstm.shutdown();
}
+ @Test
public void testCancelPositionSaver() {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
pstm.startPositionSaver();
pstm.cancelPositionSaver();
@@ -169,8 +186,9 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
pstm.shutdown();
}
+ @Test
public void testStartWidgetUpdater() throws InterruptedException {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
final int NUM_COUNTDOWNS = 2;
final int TIMEOUT = 3 * PlaybackServiceTaskManager.WIDGET_UPDATER_NOTIFICATION_INTERVAL;
final CountDownLatch countDownLatch = new CountDownLatch(NUM_COUNTDOWNS);
@@ -210,16 +228,18 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
pstm.shutdown();
}
+ @Test
public void testIsWidgetUpdaterActive() {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
pstm.startWidgetUpdater();
assertTrue(pstm.isWidgetUpdaterActive());
pstm.shutdown();
}
+ @Test
public void testCancelWidgetUpdater() {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
pstm.startWidgetUpdater();
pstm.cancelWidgetUpdater();
@@ -227,8 +247,9 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
pstm.shutdown();
}
+ @Test
public void testCancelAllTasksNoTasksStarted() {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
pstm.cancelAllTasks();
assertFalse(pstm.isPositionSaverActive());
@@ -237,8 +258,10 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
pstm.shutdown();
}
+ @Test
+ @UiThreadTest
public void testCancelAllTasksAllTasksStarted() {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
pstm.startWidgetUpdater();
pstm.startPositionSaver();
@@ -250,8 +273,10 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
pstm.shutdown();
}
+ @Test
+ @UiThreadTest
public void testSetSleepTimer() throws InterruptedException {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
final long TIME = 2000;
final long TIMEOUT = 2 * TIME;
final CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -294,8 +319,10 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
pstm.shutdown();
}
+ @Test
+ @UiThreadTest
public void testDisableSleepTimer() throws InterruptedException {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
final long TIME = 1000;
final long TIMEOUT = 2 * TIME;
final CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -336,16 +363,20 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
pstm.shutdown();
}
+ @Test
+ @UiThreadTest
public void testIsSleepTimerActivePositive() {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
pstm.setSleepTimer(10000, false, false);
assertTrue(pstm.isSleepTimerActive());
pstm.shutdown();
}
+ @Test
+ @UiThreadTest
public void testIsSleepTimerActiveNegative() {
- final Context c = getInstrumentation().getTargetContext();
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
pstm.setSleepTimer(10000, false, false);
pstm.disableSleepTimer();
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
index 9a60b04b8..8e0064079 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -7,6 +7,7 @@ import android.support.test.espresso.intent.Intents;
import android.support.test.filters.FlakyTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.view.Gravity;
import android.widget.ListView;
import com.robotium.solo.Solo;
import com.robotium.solo.Timeout;
@@ -17,6 +18,7 @@ import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.fragment.DownloadsFragment;
import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
@@ -36,6 +38,8 @@ import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.longClick;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.contrib.DrawerMatchers.isClosed;
import static android.support.test.espresso.intent.Intents.intended;
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
@@ -76,6 +80,9 @@ public class MainActivityTest {
prefs = context.getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE);
prefs.edit().putBoolean(MainActivity.PREF_IS_FIRST_LAUNCH, false).commit();
+ RatingDialog.init(context);
+ RatingDialog.saveRated();
+
solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
}
@@ -89,7 +96,9 @@ public class MainActivityTest {
}
private void openNavDrawer() {
- onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
+ onView(withId(R.id.drawer_layout))
+ .check(matches(isClosed(Gravity.LEFT)))
+ .perform(DrawerActions.open());
}
@Test
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 fa5012b74..78cc15b2c 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -47,7 +47,6 @@ 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);
@@ -78,7 +77,6 @@ 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()) {
@@ -93,32 +91,12 @@ 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()
@@ -297,12 +275,13 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
- Log.d(TAG, "Videosurface was destroyed." );
- Log.v(TAG, " hasController=" + (controller != null)
- + " , destroyingDueToReload=" + destroyingDueToReload
- + " , videoBackgroundBehavior=" + UserPreferences.getVideoBackgroundBehavior());
+ Log.d(TAG, "Videosurface was destroyed");
videoSurfaceCreated = false;
- stopPlaybackIfUserPreferencesSpecified();
+ if (controller != null && !destroyingDueToReload
+ && UserPreferences.getVideoBackgroundBehavior()
+ != UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) {
+ controller.notifyVideoSurfaceAbandoned();
+ }
}
};
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 f54b9266e..1286d9dc7 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
@@ -1,6 +1,8 @@
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;
@@ -28,7 +30,7 @@ import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
*/
public class DefaultActionButtonCallback implements ActionButtonCallback {
- private static final String TAG = "DefaultActionBtnCb";
+ private static final String TAG = "DefaultActionButtonCallback";
private final Context context;
@@ -82,9 +84,13 @@ 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) // need to start the service in case it's been stopped by system.
+ new PlaybackServiceStarter(context, media)
.startWhenPrepared(true)
.shouldStream(false)
.start();
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
index ece184035..24656ed29 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
@@ -6,6 +6,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import android.util.Log;
import com.afollestad.materialdialogs.MaterialDialog;
@@ -73,7 +74,8 @@ public class RatingDialog {
return mPreferences.getBoolean(KEY_RATED, false);
}
- private static void saveRated() {
+ @VisibleForTesting
+ public static void saveRated() {
mPreferences
.edit()
.putBoolean(KEY_RATED, true)
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
index 8f2073ed0..c4b82459a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.fragment;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
+import android.util.Log;
import android.view.View;
import android.widget.ListView;
@@ -12,12 +13,17 @@ import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.greenrobot.event.EventBus;
+import io.reactivex.Maybe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
public class ChaptersFragment extends ListFragment {
private static final String TAG = "ChaptersFragment";
private ChaptersListAdapter adapter;
private PlaybackController controller;
+ private Disposable disposable;
@Override
@@ -42,10 +48,7 @@ public class ChaptersFragment extends ListFragment {
controller = new PlaybackController(getActivity(), false) {
@Override
public boolean loadMediaInfo() {
- if (getMedia() == null) {
- return false;
- }
- onMediaChanged(getMedia());
+ ChaptersFragment.this.loadMediaInfo();
return true;
}
@@ -55,11 +58,20 @@ public class ChaptersFragment extends ListFragment {
}
};
controller.init();
- onMediaChanged(controller.getMedia());
+ loadMediaInfo();
EventBus.getDefault().register(this);
}
@Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ @Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
@@ -73,6 +85,24 @@ public class ChaptersFragment extends ListFragment {
}
}
+ private void loadMediaInfo() {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Maybe.create(emitter -> {
+ Playable media = controller.getMedia();
+ if (media != null) {
+ emitter.onSuccess(media);
+ } else {
+ emitter.onComplete();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(media -> onMediaChanged((Playable) media),
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+
private void onMediaChanged(Playable media) {
if (adapter != null) {
adapter.setMedia(media);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index 88d748c5a..7bb9c60e4 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
@@ -18,6 +19,10 @@ import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.greenrobot.event.EventBus;
+import io.reactivex.Maybe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Displays the cover and the title of a FeedItem.
@@ -31,6 +36,7 @@ public class CoverFragment extends Fragment {
private TextView txtvEpisodeTitle;
private ImageView imgvCover;
private PlaybackController controller;
+ private Disposable disposable;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -44,11 +50,24 @@ public class CoverFragment extends Fragment {
}
private void loadMediaInfo() {
- Playable media = controller.getMedia();
- if (media == null) {
- Log.w(TAG, "loadMediaInfo was called while media was null");
- return;
+ if (disposable != null) {
+ disposable.dispose();
}
+ disposable = Maybe.create(emitter -> {
+ Playable media = controller.getMedia();
+ if (media != null) {
+ emitter.onSuccess(media);
+ } else {
+ emitter.onComplete();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(media -> displayMediaInfo((Playable) media),
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+
+ private void displayMediaInfo(@NonNull Playable media) {
txtvPodcastTitle.setText(media.getFeedTitle());
txtvEpisodeTitle.setText(media.getEpisodeTitle());
Glide.with(this)
@@ -73,9 +92,6 @@ public class CoverFragment extends Fragment {
controller = new PlaybackController(getActivity(), false) {
@Override
public boolean loadMediaInfo() {
- if (getMedia() == null) {
- return false;
- }
CoverFragment.this.loadMediaInfo();
return true;
}
@@ -99,4 +115,13 @@ public class CoverFragment extends Fragment {
controller.release();
controller = null;
}
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
}
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 e114ef405..4b8f6a16a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -592,7 +592,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
private FeedItem loadInBackground() {
FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]);
if (feedItem != null) {
- Timeline t = new Timeline(getActivity(), feedItem);
+ Timeline t = new Timeline(getContext(), feedItem);
webviewData = t.processShownotes(false);
}
return feedItem;
diff --git a/contributers.template.py b/contributers.template.py
index 0f4d78698..4492fae79 100755
--- a/contributers.template.py
+++ b/contributers.template.py
@@ -1,9 +1,17 @@
#!/usr/bin/env python3
import requests
import subprocess
+import configparser
+import os
-TRANSIFEX_USER = ""
-TRANSIFEX_PW = ""
+config = configparser.ConfigParser()
+config.read(os.path.expanduser("~") + '/.transifexrc')
+if 'https://www.transifex.com' in config:
+ TRANSIFEX_USER = config['https://www.transifex.com']['username']
+ TRANSIFEX_PW = config['https://www.transifex.com']['password']
+else:
+ TRANSIFEX_USER = ""
+ TRANSIFEX_PW = ""
print('DEVELOPERS\n==========\n')
p = subprocess.Popen("git log --format='%aN' --no-merges "
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
index a4099bf94..7988526d9 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
@@ -1092,13 +1092,22 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
if (seekLatch != null) {
seekLatch.countDown();
}
- playerLock.lock();
- if (playerStatus == PlayerStatus.PLAYING) {
- callback.onPlaybackStart(media, getPosition());
- }
- if (playerStatus == PlayerStatus.SEEKING) {
- setPlayerStatus(statusBeforeSeeking, media, getPosition());
+
+ Runnable r = () -> {
+ playerLock.lock();
+ if (playerStatus == PlayerStatus.PLAYING) {
+ callback.onPlaybackStart(media, getPosition());
+ }
+ if (playerStatus == PlayerStatus.SEEKING) {
+ setPlayerStatus(statusBeforeSeeking, media, getPosition());
+ }
+ playerLock.unlock();
+ };
+
+ if (useCallerThread) {
+ r.run();
+ } else {
+ executor.submit(r);
}
- playerLock.unlock();
}
}
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 7fe93a162..8395c227c 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,7 +25,6 @@ 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;
@@ -76,11 +75,6 @@ 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 {
/**
@@ -95,7 +89,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
/**
* True if cast session should disconnect.
*/
- private static final String EXTRA_CAST_DISCONNECT = "extra.de.danoeh.antennapod.core.service.castDisconnect";
+ public static final String EXTRA_CAST_DISCONNECT = "extra.de.danoeh.antennapod.core.service.castDisconnect";
/**
* True if media should be streamed.
*/
@@ -199,6 +193,10 @@ 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;
@@ -266,6 +264,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Log.d(TAG, "Service created.");
isRunning = true;
+ PlaybackServiceNotificationBuilder notificationBuilder = new PlaybackServiceNotificationBuilder(this);
+ 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));
@@ -319,30 +320,13 @@ public class PlaybackService extends MediaBrowserServiceCompat {
EventBus.getDefault().post(new ServiceEvent(ServiceEvent.Action.SERVICE_STARTED));
}
- 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
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Service is about to be destroyed");
stopForeground(true);
isRunning = false;
+ started = false;
currentMediaType = MediaType.UNKNOWN;
PreferenceManager.getDefaultSharedPreferences(this)
@@ -364,6 +348,11 @@ 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 +
@@ -421,7 +410,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
// Child List
try {
for (FeedItem feedItem : taskManager.getQueue()) {
- mediaItems.add(feedItem.getMedia().getMediaItem());
+ FeedMedia media = feedItem.getMedia();
+ if (media != null) {
+ mediaItems.add(media.getMediaItem());
+ }
}
} catch (InterruptedException e) {
e.printStackTrace();
@@ -457,32 +449,39 @@ 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) {
- // 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.");
+ Log.e(TAG, "PlaybackService was started with no arguments");
+ stopService();
return Service.START_NOT_STICKY;
}
- 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());
+ 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);
+ } else {
+ Log.d(TAG, "Did not handle intent to PlaybackService: " + intent);
+ Log.d(TAG, "Extras: " + intent.getExtras());
}
- mediaPlayer.playMediaObject(playable, stream, startWhenPrepared, prepareImmediately);
}
return Service.START_NOT_STICKY;
@@ -556,23 +555,12 @@ 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);
- } else {
- serviceManager.showUIForStopState();
+ started = false;
}
+
+ stopForeground(true); // gets rid of persistent notification
return true;
default:
Log.d(TAG, "Unhandled key code: " + keycode);
@@ -588,6 +576,7 @@ 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);
}
}
@@ -602,9 +591,10 @@ 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() {
@@ -667,15 +657,27 @@ 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()) {
@@ -686,6 +688,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case ERROR:
writePlaybackPreferencesNoMediaPlaying();
+ stopService();
break;
}
@@ -698,7 +701,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void shouldStop() {
- serviceManager.stopService();
+ stopService();
}
@Override
@@ -747,6 +750,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
writePlaybackPreferencesNoMediaPlaying();
+ stopService();
return true;
}
@@ -830,6 +834,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (stopPlaying) {
taskManager.cancelPositionSaver();
writePlaybackPreferencesNoMediaPlaying();
+ if (!isCasting) {
+ stopForeground(true);
+ }
}
if (mediaType == null) {
sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0);
@@ -1043,7 +1050,6 @@ 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) {
@@ -1107,9 +1113,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
flavorHelper.mediaSessionSetExtraForWear(mediaSession);
- final PlaybackStateCompat sessionStateBuilt = sessionState.build();
- mediaSession.setPlaybackState(sessionStateBuilt);
- serviceManager.onPlaybackStateChange(sessionStateBuilt);
+ mediaSession.setPlaybackState(sessionState.build());
}
private static boolean useSkipToPreviousForRewindInLockscreen() {
@@ -1163,7 +1167,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, imageLocation);
}
}
- if (!Thread.currentThread().isInterrupted() && isStarted()) {
+ if (!Thread.currentThread().isInterrupted() && started) {
mediaSession.setSessionActivity(PendingIntent.getActivity(this, 0,
PlaybackService.getPlayerActivityIntent(this),
PendingIntent.FLAG_UPDATE_CURRENT));
@@ -1186,190 +1190,69 @@ public class PlaybackService extends MediaBrowserServiceCompat {
*/
private Thread notificationSetupThread;
- private synchronized void setupNotification(final Playable playable, boolean treatPauseAsStop) {
+ /**
+ * 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) {
if (notificationSetupThread != null) {
notificationSetupThread.interrupt();
}
if (playable == null) {
- Log.d(TAG, "setupNotification: playable is null");
- if (!isStarted()) {
- serviceManager.stopService();
+ Log.d(TAG, "setupNotification: playable is null" + Log.getStackTraceString(new Exception()));
+ if (!started) {
+ stopService();
}
return;
}
Runnable notificationSetupTask = new Runnable() {
- Bitmap icon = null;
-
@Override
public void run() {
- Log.d(TAG, "notificationSetupTask: Starting background work");
+ Log.d(TAG, "Starting background work");
if (mediaPlayer == null) {
Log.d(TAG, "notificationSetupTask: mediaPlayer is null");
- if (!isStarted()) {
- serviceManager.stopService();
+ if (!started) {
+ stopService();
}
return;
}
-
- int iconSize = getResources().getDimensionPixelSize(
- android.R.dimen.notification_large_icon_width);
- try {
- icon = Glide.with(PlaybackService.this)
- .asBitmap()
- .load(playable.getImageLocation())
- .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY))
- .apply(new RequestOptions().centerCrop())
- .submit(iconSize, iconSize)
- .get();
- } catch (Throwable tr) {
- Log.e(TAG, "Error loading the media icon for the notification", tr);
- }
-
- if (icon == null) {
- icon = BitmapFactory.decodeResource(getApplicationContext().getResources(),
- ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext()));
- }
-
PlayerStatus playerStatus = mediaPlayer.getPlayerStatus();
- Log.v(TAG, "notificationSetupTask: playerStatus=" + playerStatus);
-
- if (!Thread.currentThread().isInterrupted() && isStarted()) {
- String contentText = playable.getEpisodeTitle();
- String contentTitle = playable.getFeedTitle();
- Notification notification;
-
- // Builder is v7, even if some not overwritten methods return its parent's v4 interface
- NotificationCompat.Builder notificationBuilder = createBasicNotification();
- notificationBuilder.setContentTitle(contentTitle)
- .setContentText(contentText)
- .setPriority(UserPreferences.getNotifyPriority())
- .setLargeIcon(icon); // set notification priority
- IntList compactActionList = new IntList();
-
- int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction
-
- if (isCasting) {
- Intent stopCastingIntent = new Intent(PlaybackService.this, PlaybackService.class);
- stopCastingIntent.putExtra(EXTRA_CAST_DISCONNECT, true);
- PendingIntent stopCastingPendingIntent = PendingIntent.getService(PlaybackService.this,
- numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- notificationBuilder.addAction(R.drawable.ic_media_cast_disconnect,
- getString(R.string.cast_disconnect_label),
- stopCastingPendingIntent);
- numActions++;
- }
-
- // always let them rewind
- PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_REWIND, numActions);
- notificationBuilder.addAction(android.R.drawable.ic_media_rew,
- getString(R.string.rewind_label),
- rewindButtonPendingIntent);
- if (UserPreferences.showRewindOnCompactNotification()) {
- compactActionList.add(numActions);
- }
- numActions++;
-
- if (playerStatus == PlayerStatus.PLAYING) {
- PendingIntent pauseButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_PAUSE, numActions);
- notificationBuilder.addAction(android.R.drawable.ic_media_pause, //pause action
- getString(R.string.pause_label),
- pauseButtonPendingIntent);
- compactActionList.add(numActions++);
- } else {
- PendingIntent playButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_PLAY, numActions);
- notificationBuilder.addAction(android.R.drawable.ic_media_play, //play action
- getString(R.string.play_label),
- playButtonPendingIntent);
- compactActionList.add(numActions++);
- }
-
- // ff follows play, then we have skip (if it's present)
- PendingIntent ffButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions);
- notificationBuilder.addAction(android.R.drawable.ic_media_ff,
- getString(R.string.fast_forward_label),
- ffButtonPendingIntent);
- if (UserPreferences.showFastForwardOnCompactNotification()) {
- compactActionList.add(numActions);
- }
- numActions++;
-
- if (UserPreferences.isFollowQueue()) {
- PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_NEXT, numActions);
- notificationBuilder.addAction(android.R.drawable.ic_media_next,
- getString(R.string.skip_episode_label),
- skipButtonPendingIntent);
- if (UserPreferences.showSkipOnCompactNotification()) {
- compactActionList.add(numActions);
- }
- numActions++;
- }
-
- PendingIntent stopButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_STOP, numActions);
- notificationBuilder.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
- .setMediaSession(mediaSession.getSessionToken())
- .setShowActionsInCompactView(compactActionList.toArray())
- .setShowCancelButton(true)
- .setCancelButtonIntent(stopButtonPendingIntent))
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- .setColor(NotificationCompat.COLOR_DEFAULT);
+ PlaybackServiceNotificationBuilder notificationBuilder =
+ new PlaybackServiceNotificationBuilder(PlaybackService.this);
+ notificationBuilder.addMetadata(playable, mediaSession.getSessionToken(), playerStatus, isCasting);
+
+ if (!notificationBuilder.isIconCached(playable)) {
+ // To make sure that the notification is shown instantly
+ notificationBuilder.loadDefaultIcon();
+ startForeground(NOTIFICATION_ID, notificationBuilder.build());
+ }
+ notificationBuilder.loadIcon(playable);
- notification = notificationBuilder.build();
+ if (!Thread.currentThread().isInterrupted() && started) {
+ Notification notification = notificationBuilder.build();
if (playerStatus == PlayerStatus.PLAYING ||
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 {
- leaveNotificationAsBackground(notification);
+ stopForeground(false);
+ NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ mNotificationManager.notify(NOTIFICATION_ID, 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();
}
- private PendingIntent getPendingIntentForMediaAction(int keycodeValue, int requestCode) {
- Intent intent = new Intent(
- PlaybackService.this, PlaybackService.class);
- intent.putExtra(
- MediaButtonReceiver.EXTRA_KEYCODE,
- keycodeValue);
- return PendingIntent
- .getService(PlaybackService.this, requestCode,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
/**
* Persists the current position and last played time of the media file.
*
@@ -1547,7 +1430,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
- serviceManager.stopService();
+ stopService();
}
}
@@ -1845,6 +1728,8 @@ 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);
@@ -1884,6 +1769,24 @@ 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;
}
@@ -1898,116 +1801,4 @@ 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/service/playback/PlaybackServiceNotificationBuilder.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
new file mode 100644
index 000000000..9a1e8e7ef
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
@@ -0,0 +1,184 @@
+package de.danoeh.antennapod.core.service.playback;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.support.annotation.NonNull;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.util.Log;
+import android.view.KeyEvent;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
+import de.danoeh.antennapod.core.util.IntList;
+import de.danoeh.antennapod.core.util.gui.NotificationUtils;
+import de.danoeh.antennapod.core.util.playback.Playable;
+
+public class PlaybackServiceNotificationBuilder extends NotificationCompat.Builder {
+ private static final String TAG = "PlaybackSrvNotification";
+ private static Bitmap defaultIcon = null;
+
+ private Context context;
+
+ public PlaybackServiceNotificationBuilder(@NonNull Context context) {
+ super(context, NotificationUtils.CHANNEL_ID_PLAYING);
+ this.context = context;
+
+ final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(context);
+
+ final PendingIntent pIntent = PendingIntent.getActivity(context, 0,
+ PlaybackService.getPlayerActivityIntent(context),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ setContentTitle(context.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);
+ }
+
+ public void addMetadata(Playable playable, MediaSessionCompat.Token mediaSessionToken, PlayerStatus playerStatus, boolean isCasting) {
+ Log.v(TAG, "notificationSetupTask: playerStatus=" + playerStatus);
+ setContentTitle(playable.getFeedTitle());
+ setContentText(playable.getEpisodeTitle());
+ setPriority(UserPreferences.getNotifyPriority());
+ addActions(mediaSessionToken, playerStatus, isCasting);
+ setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
+ setColor(NotificationCompat.COLOR_DEFAULT);
+ }
+
+ public boolean isIconCached(Playable playable) {
+ int iconSize = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
+ try {
+ Bitmap icon = Glide.with(context)
+ .asBitmap()
+ .load(playable.getImageLocation())
+ .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY))
+ .apply(new RequestOptions()
+ .centerCrop()
+ .onlyRetrieveFromCache(true))
+ .submit(iconSize, iconSize)
+ .get();
+ return icon != null;
+ } catch (Throwable tr) {
+ return false;
+ }
+ }
+
+ public void loadIcon(Playable playable) {
+ Bitmap icon = null;
+ int iconSize = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
+ try {
+ icon = Glide.with(context)
+ .asBitmap()
+ .load(playable.getImageLocation())
+ .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY))
+ .apply(new RequestOptions().centerCrop())
+ .submit(iconSize, iconSize)
+ .get();
+ } catch (Throwable tr) {
+ Log.e(TAG, "Error loading the media icon for the notification", tr);
+ }
+
+ if (icon == null) {
+ loadDefaultIcon();
+ } else {
+ setLargeIcon(icon);
+ }
+ }
+
+ public void loadDefaultIcon() {
+ if (defaultIcon == null) {
+ defaultIcon = BitmapFactory.decodeResource(context.getResources(),
+ ClientConfig.playbackServiceCallbacks.getNotificationIconResource(context));
+ }
+ setLargeIcon(defaultIcon);
+ }
+
+ private void addActions(MediaSessionCompat.Token mediaSessionToken, PlayerStatus playerStatus, boolean isCasting) {
+ IntList compactActionList = new IntList();
+
+ int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction
+
+ if (isCasting) {
+ Intent stopCastingIntent = new Intent(context, PlaybackService.class);
+ stopCastingIntent.putExtra(PlaybackService.EXTRA_CAST_DISCONNECT, true);
+ PendingIntent stopCastingPendingIntent = PendingIntent.getService(context,
+ numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ addAction(R.drawable.ic_media_cast_disconnect,
+ context.getString(R.string.cast_disconnect_label),
+ stopCastingPendingIntent);
+ numActions++;
+ }
+
+ // always let them rewind
+ PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction(
+ KeyEvent.KEYCODE_MEDIA_REWIND, numActions);
+ addAction(android.R.drawable.ic_media_rew, context.getString(R.string.rewind_label), rewindButtonPendingIntent);
+ if (UserPreferences.showRewindOnCompactNotification()) {
+ compactActionList.add(numActions);
+ }
+ numActions++;
+
+ if (playerStatus == PlayerStatus.PLAYING) {
+ PendingIntent pauseButtonPendingIntent = getPendingIntentForMediaAction(
+ KeyEvent.KEYCODE_MEDIA_PAUSE, numActions);
+ addAction(android.R.drawable.ic_media_pause, //pause action
+ context.getString(R.string.pause_label),
+ pauseButtonPendingIntent);
+ compactActionList.add(numActions++);
+ } else {
+ PendingIntent playButtonPendingIntent = getPendingIntentForMediaAction(
+ KeyEvent.KEYCODE_MEDIA_PLAY, numActions);
+ addAction(android.R.drawable.ic_media_play, //play action
+ context.getString(R.string.play_label),
+ playButtonPendingIntent);
+ compactActionList.add(numActions++);
+ }
+
+ // ff follows play, then we have skip (if it's present)
+ PendingIntent ffButtonPendingIntent = getPendingIntentForMediaAction(
+ KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions);
+ addAction(android.R.drawable.ic_media_ff, context.getString(R.string.fast_forward_label), ffButtonPendingIntent);
+ if (UserPreferences.showFastForwardOnCompactNotification()) {
+ compactActionList.add(numActions);
+ }
+ numActions++;
+
+ if (UserPreferences.isFollowQueue()) {
+ PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction(
+ KeyEvent.KEYCODE_MEDIA_NEXT, numActions);
+ addAction(android.R.drawable.ic_media_next,
+ context.getString(R.string.skip_episode_label),
+ skipButtonPendingIntent);
+ if (UserPreferences.showSkipOnCompactNotification()) {
+ compactActionList.add(numActions);
+ }
+ numActions++;
+ }
+
+ PendingIntent stopButtonPendingIntent = getPendingIntentForMediaAction(
+ KeyEvent.KEYCODE_MEDIA_STOP, numActions);
+ setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
+ .setMediaSession(mediaSessionToken)
+ .setShowActionsInCompactView(compactActionList.toArray())
+ .setShowCancelButton(true)
+ .setCancelButtonIntent(stopButtonPendingIntent));
+ }
+
+ private PendingIntent getPendingIntentForMediaAction(int keycodeValue, int requestCode) {
+ Intent intent = new Intent(context, PlaybackService.class);
+ intent.putExtra(MediaButtonReceiver.EXTRA_KEYCODE, keycodeValue);
+ return PendingIntent .getService(context, requestCode,
+ intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+} \ No newline at end of file
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 9bdd375ce..d9431bc5d 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
@@ -68,6 +68,11 @@ public class NetworkUtils {
}
}
}
+ } else if (networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET) {
+ Log.d(TAG, "Device is connected to Ethernet");
+ if (networkInfo.isConnected()) {
+ return true;
+ }
} else {
if (!UserPreferences.isEnableAutodownloadOnMobile()) {
Log.d(TAG, "Auto Download not enabled on Mobile");
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 4d5ed449b..a77086f2a 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,6 +12,7 @@ import android.media.MediaPlayer;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -104,7 +105,6 @@ public abstract class PlaybackController {
}
private synchronized void initServiceRunning() {
- Log.v(TAG, "initServiceRunning()");
if (initialized) {
return;
}
@@ -189,13 +189,20 @@ public abstract class PlaybackController {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(optionalIntent -> {
boolean bound = false;
- if (optionalIntent.isPresent()) {
- Log.d(TAG, "Calling bind service");
- bound = activity.bindService(optionalIntent.get(), mConnection, 0);
+ if (!PlaybackService.started) {
+ if (optionalIntent.isPresent()) {
+ Log.d(TAG, "Calling start service");
+ ContextCompat.startForegroundService(activity, optionalIntent.get());
+ bound = activity.bindService(optionalIntent.get(), mConnection, 0);
+ } else {
+ status = PlayerStatus.STOPPED;
+ setupGUI();
+ handleStatus();
+ }
} else {
- status = PlayerStatus.STOPPED;
- setupGUI();
- handleStatus();
+ Log.d(TAG, "PlaybackService is running, trying to connect without start command.");
+ bound = activity.bindService(new Intent(activity, PlaybackService.class),
+ mConnection, 0);
}
Log.d(TAG, "Result for service binding: " + bound);
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
@@ -214,7 +221,6 @@ public abstract class PlaybackController {
return Optional.empty();
}
-
boolean fileExists = media.localFileAvailable();
boolean lastIsStream = PlaybackPreferences.getCurrentEpisodeIsStream();
if (!fileExists && !lastIsStream && media instanceof FeedMedia) {
@@ -582,8 +588,7 @@ public abstract class PlaybackController {
.startWhenPrepared(true)
.streamIfLastWasStream()
.start();
- Log.d(TAG, "Play/Pause button was pressed, but playbackservice was null - " +
- "it is likely to have been released by Android system. Restarting it.");
+ Log.w(TAG, "Play/Pause button was pressed, but playbackservice was null!");
return;
}
switch (status) {
@@ -760,7 +765,6 @@ public abstract class PlaybackController {
}
public void notifyVideoSurfaceAbandoned() {
- Log.v(TAG, "notifyVideoSurfaceAbandoned() - hasPlaybackService=" + (playbackService != null));
if (playbackService != null) {
playbackService.notifyVideoSurfaceAbandoned();
}
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 64cf61457..f7d2ee409 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,15 +2,14 @@ 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;
@@ -67,10 +66,6 @@ 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/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
index 15675c966..7ab1be380 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
@@ -153,6 +153,7 @@ public class PlaybackServiceFlavorHelper {
// hardware volume buttons control the local device volume
mediaRouter.setMediaSessionCompat(null);
unregisterWifiBroadcastReceiver();
+ callback.setupNotification(false, info);
}
};
}
@@ -182,6 +183,7 @@ 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,