diff options
19 files changed, 550 insertions, 448 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 b2cd1f3be..711048239 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/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java index 5dd4a357b..1bcdada44 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java @@ -1,7 +1,9 @@ package de.danoeh.antennapod.activity; +import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; @@ -54,12 +56,15 @@ public class AboutActivity extends AppCompatActivity { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - if (!url.startsWith("http")) { + if (url.startsWith("http")) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + startActivity(browserIntent); + return true; + } else { url = url.replace("file:///android_asset/", ""); loadAsset(url); return true; } - return false; } }); 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, |