summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java52
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java127
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java51
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java46
-rw-r--r--app/src/main/AndroidManifest.xml7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java78
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java (renamed from app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java)76
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java7
-rw-r--r--app/src/main/res/layout/statistics_activity.xml81
-rw-r--r--app/src/main/res/layout/statistics_listitem.xml95
-rw-r--r--app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java54
-rw-r--r--app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java15
-rw-r--r--config/checkstyle/checkstyle.xml54
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/PlaybackPositionEvent.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java36
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java69
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java87
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java119
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java34
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Optional.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java50
-rw-r--r--core/src/main/res/drawable/ic_warning_red.xml5
-rw-r--r--core/src/main/res/values/strings.xml2
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java13
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java4
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java31
51 files changed, 1033 insertions, 395 deletions
diff --git a/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java b/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java
new file mode 100644
index 000000000..9bb2c58db
--- /dev/null
+++ b/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java
@@ -0,0 +1,52 @@
+package de.danoeh.antennapod.core.service.download;
+
+import android.support.annotation.NonNull;
+
+import de.danoeh.antennapod.core.util.Consumer;
+
+public class StubDownloader extends Downloader {
+
+ private final long downloadTime;
+
+ @NonNull
+ private final Consumer<DownloadStatus> onDownloadComplete;
+
+ public StubDownloader(@NonNull DownloadRequest request, long downloadTime, @NonNull Consumer<DownloadStatus> onDownloadComplete) {
+ super(request);
+ this.downloadTime = downloadTime;
+ this.onDownloadComplete = onDownloadComplete;
+ }
+
+ @Override
+ protected void download() {
+ try {
+ Thread.sleep(downloadTime);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ onDownloadComplete.accept(result);
+ }
+
+ @NonNull
+ @Override
+ public DownloadRequest getDownloadRequest() {
+ return super.getDownloadRequest();
+ }
+
+ @NonNull
+ @Override
+ public DownloadStatus getResult() {
+ return super.getResult();
+ }
+
+ @Override
+ public boolean isFinished() {
+ return super.isFinished();
+ }
+
+ @Override
+ public void cancel() {
+ super.cancel();
+ result.setCancelled();
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
new file mode 100644
index 000000000..81edb75ba
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
@@ -0,0 +1,127 @@
+package de.test.antennapod.service.download;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.awaitility.Awaitility;
+import org.awaitility.core.ConditionTimeoutException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.service.download.DownloadRequest;
+import de.danoeh.antennapod.core.service.download.DownloadService;
+import de.danoeh.antennapod.core.service.download.DownloadStatus;
+import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.core.service.download.StubDownloader;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.Consumer;
+
+import static de.test.antennapod.util.event.FeedItemEventListener.withFeedItemEventListener;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @see HttpDownloaderTest for the test of actual download (and saving the file)
+ */
+@RunWith(AndroidJUnit4.class)
+public class DownloadServiceTest {
+
+ private CountDownLatch latch = null;
+ private Feed testFeed = null;
+ private FeedMedia testMedia11 = null;
+
+ private DownloadService.DownloaderFactory origFactory = null;
+
+ @Before
+ public void setUp() throws Exception {
+ origFactory = DownloadService.getDownloaderFactory();
+ testFeed = setUpTestFeeds();
+ testMedia11 = testFeed.getItemAtIndex(0).getMedia();
+ }
+
+ private Feed setUpTestFeeds() throws Exception {
+ Feed feed = new Feed("url", null, "Test Feed title 1");
+ List<FeedItem> items = new ArrayList<>();
+ feed.setItems(items);
+ FeedItem item1 = new FeedItem(0, "Item 1-1", "Item 1-1", "url", new Date(), FeedItem.NEW, feed);
+ items.add(item1);
+ FeedMedia media1 = new FeedMedia(0, item1, 123, 1, 1, "audio/mp3", null, "http://example.com/episode.mp3", false, null, 0, 0);
+ item1.setMedia(media1);
+
+ DBWriter.setFeedItem(item1).get();
+ return feed;
+ }
+
+
+ @After
+ public void tearDown() throws Exception {
+ DownloadService.setDownloaderFactory(origFactory);
+ }
+
+ @Test
+ public void testEventsGeneratedCaseMediaDownloadSuccess() throws Exception {
+ // create a stub download that returns successful
+ //
+ // OPEN: Ideally, I'd like the download time long enough so that multiple in-progress DownloadEvents
+ // are generated (to simulate typical download), but it'll make download time quite long (1-2 seconds)
+ // to do so
+ DownloadService.setDownloaderFactory(new StubDownloaderFactory(50, downloadStatus -> {
+ downloadStatus.setSuccessful();
+ }));
+
+ withFeedItemEventListener(feedItemEventListener -> {
+ try {
+ assertEquals(0, feedItemEventListener.getEvents().size());
+ assertFalse("The media in test should not yet been downloaded",
+ DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
+
+ DownloadRequester.getInstance().downloadMedia(InstrumentationRegistry.getTargetContext(),
+ testMedia11);
+ Awaitility.await()
+ .atMost(1000, TimeUnit.MILLISECONDS)
+ .until(() -> feedItemEventListener.getEvents().size() > 0);
+ assertTrue("After media download has completed, FeedMedia object in db should indicate so.",
+ DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
+ } catch (ConditionTimeoutException cte) {
+ fail("The expected FeedItemEvent (for media download complete) has not been posted. "
+ + cte.getMessage());
+ }
+ });
+ }
+
+ private static class StubDownloaderFactory implements DownloadService.DownloaderFactory {
+ private final long downloadTime;
+
+ @NonNull
+ private final Consumer<DownloadStatus> onDownloadComplete;
+
+ StubDownloaderFactory(long downloadTime, @NonNull Consumer<DownloadStatus> onDownloadComplete) {
+ this.downloadTime = downloadTime;
+ this.onDownloadComplete = onDownloadComplete;
+ }
+
+ @Nullable
+ @Override
+ public Downloader create(@NonNull DownloadRequest request) {
+ return new StubDownloader(request, downloadTime, onDownloadComplete);
+ }
+ }
+
+}
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 9c0e90929..35f178a44 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
@@ -5,6 +5,12 @@ import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.LargeTest;
+import org.awaitility.Awaitility;
+import org.greenrobot.eventbus.EventBus;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -15,14 +21,14 @@ import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.Playable;
-import org.greenrobot.eventbus.EventBus;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import static de.test.antennapod.util.event.FeedItemEventListener.withFeedItemEventListener;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -124,6 +130,43 @@ public class PlaybackServiceTaskManagerTest {
}
@Test
+ public void testQueueUpdatedUponDownloadComplete() throws Exception {
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ { // Setup test data
+ List<FeedItem> queue = writeTestQueue("a");
+ FeedItem item = DBReader.getFeedItem(queue.get(0).getId());
+ FeedMedia media = new FeedMedia(item, "http://example.com/episode.mp3", 12345, "audio/mp3");
+ item.setMedia(media);
+ DBWriter.setFeedMedia(media).get();
+ DBWriter.setFeedItem(item).get();
+ }
+
+ PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
+ final FeedItem testItem = pstm.getQueue().get(0);
+ assertFalse("The item should not yet be downloaded", testItem.getMedia().isDownloaded());
+
+ withFeedItemEventListener( feedItemEventListener -> {
+ // simulate download complete (in DownloadService.MediaHandlerThread)
+ FeedItem item = DBReader.getFeedItem(testItem.getId());
+ item.getMedia().setDownloaded(true);
+ item.getMedia().setFile_url("file://123");
+ item.setAutoDownload(false);
+ DBWriter.setFeedMedia(item.getMedia()).get();
+ DBWriter.setFeedItem(item).get();
+
+ Awaitility.await()
+ .atMost(1000, TimeUnit.MILLISECONDS)
+ .until(() -> feedItemEventListener.getEvents().size() > 0);
+
+ final FeedItem itemUpdated = pstm.getQueue().get(0);
+ assertTrue("media.isDownloaded() should be true - The queue in PlaybackService should be updated after download is completed",
+ itemUpdated.getMedia().isDownloaded());
+ });
+
+ pstm.shutdown();
+ }
+
+ @Test
public void testStartPositionSaver() throws InterruptedException {
final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
final int NUM_COUNTDOWNS = 2;
diff --git a/app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java b/app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java
new file mode 100644
index 000000000..3088a3288
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java
@@ -0,0 +1,46 @@
+package de.test.antennapod.util.event;
+
+import android.support.annotation.NonNull;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.danoeh.antennapod.core.event.FeedItemEvent;
+import io.reactivex.functions.Consumer;
+
+/**
+ * Test helpers to listen {@link FeedItemEvent} and handle them accordingly
+ *
+ */
+public class FeedItemEventListener {
+ private final List<FeedItemEvent> events = new ArrayList<>();
+
+ /**
+ * Provides an listener subscribing to {@link FeedItemEvent} that the callers can use
+ *
+ * Note: it uses RxJava's version of {@link Consumer} because it allows exceptions to be thrown.
+ */
+ public static void withFeedItemEventListener(@NonNull Consumer<FeedItemEventListener> consumer)
+ throws Exception {
+ FeedItemEventListener feedItemEventListener = new FeedItemEventListener();
+ try {
+ EventBus.getDefault().register(feedItemEventListener);
+ consumer.accept(feedItemEventListener);
+ } finally {
+ EventBus.getDefault().unregister(feedItemEventListener);
+ }
+ }
+
+ @Subscribe
+ public void onEvent(FeedItemEvent event) {
+ events.add(event);
+ }
+
+ @NonNull
+ public List<? extends FeedItemEvent> getEvents() {
+ return events;
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2d1d6c671..bfef37f71 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -140,13 +140,6 @@
android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
</activity>
<activity
- android:name=".activity.StatisticsActivity"
- android:label="@string/statistics_label">
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
- </activity>
- <activity
android:name=".activity.ImportExportActivity"
android:label="@string/import_export">
<meta-data
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index cb2f597d6..94d281a45 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -63,6 +63,8 @@ public class PodcastApp extends Application {
EventBus.builder()
.addIndex(new ApEventBusIndex())
.addIndex(new ApCoreEventBusIndex())
+ .logNoSubscriberMessages(false)
+ .sendNoSubscriberEvent(false)
.installDefaultEventBus();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 7ee8ef8d5..b778fe77e 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -47,7 +47,6 @@ import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.MessageEvent;
-import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
@@ -808,25 +807,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
}
@Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(ProgressEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
- switch(event.action) {
- case START:
- pd = new ProgressDialog(this);
- pd.setMessage(event.message);
- pd.setIndeterminate(true);
- pd.setCancelable(false);
- pd.show();
- break;
- case END:
- if(pd != null) {
- pd.dismiss();
- }
- break;
- }
- }
-
- @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(MessageEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
View parentLayout = findViewById(R.id.drawer_layout);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index 91c3796af..b1269b833 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -34,6 +34,7 @@ import com.joanzapata.iconify.IconDrawable;
import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
@@ -63,6 +64,9 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
@@ -194,6 +198,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
};
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ onPositionObserverUpdate();
+ }
+
private static TextView getTxtvFFFromActivity(MediaplayerActivity activity) {
return activity.txtvFF;
}
@@ -274,6 +283,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.init();
loadMediaInfo();
onPositionObserverUpdate();
+ EventBus.getDefault().register(this);
}
@Override
@@ -286,6 +296,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
if (disposable != null) {
disposable.dispose();
}
+ EventBus.getDefault().unregister(this);
super.onStop();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
index 5210bdc06..234962a8d 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
@@ -122,7 +122,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
disposable.dispose();
}
EventDistributor.getInstance().unregister(contentUpdate);
- EventBus.getDefault().unregister(this);
saveCurrentFragment();
}
@@ -175,7 +174,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
protected void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
loadData();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
index 85b5e3985..9eb181edc 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.os.Build;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
@@ -23,10 +24,12 @@ import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
import java.lang.ref.WeakReference;
+import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -50,6 +53,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
private final boolean showOnlyNewEpisodes;
private FeedItem selectedItem;
+ private Holder currentlyPlayingItem = null;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
@@ -165,8 +169,9 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.progress.setVisibility(View.INVISIBLE);
}
- if(media.isCurrentlyPlaying()) {
+ if (media.isCurrentlyPlaying()) {
holder.container.setBackgroundColor(playingBackGroundColor);
+ currentlyPlayingItem = holder;
} else {
holder.container.setBackgroundColor(normalBackGroundColor);
}
@@ -196,6 +201,22 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
.load();
}
+ @Override
+ public void onBindViewHolder(@NonNull Holder holder, int pos, List<Object> payload) {
+ onBindViewHolder(holder, pos);
+
+ if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) {
+ PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0);
+ holder.progress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ }
+ }
+
+ public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) {
+ if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) {
+ notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event);
+ }
+ }
+
@Nullable
public FeedItem getSelectedItem() {
return selectedItem;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index d090bc4b1..463ad10a0 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
@@ -13,15 +13,18 @@ import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.ThemeUtils;
@@ -39,6 +42,8 @@ public class FeedItemlistAdapter extends BaseAdapter {
private final int playingBackGroundColor;
private final int normalBackGroundColor;
+ private int currentlyPlayingItem = -1;
+
public FeedItemlistAdapter(Context context,
ItemAccess itemAccess,
boolean showFeedtitle,
@@ -176,8 +181,9 @@ public class FeedItemlistAdapter extends BaseAdapter {
}
typeDrawables.recycle();
- if(media.isCurrentlyPlaying()) {
+ if (media.isCurrentlyPlaying()) {
holder.container.setBackgroundColor(playingBackGroundColor);
+ currentlyPlayingItem = position;
} else {
holder.container.setBackgroundColor(normalBackGroundColor);
}
@@ -195,6 +201,19 @@ public class FeedItemlistAdapter extends BaseAdapter {
return convertView;
}
+ public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event, ListView listView) {
+ if (currentlyPlayingItem != -1 && currentlyPlayingItem < getCount()) {
+ View view = listView.getChildAt(currentlyPlayingItem
+ - listView.getFirstVisiblePosition() + listView.getHeaderViewsCount());
+ if (view == null) {
+ return;
+ }
+ Holder holder = (Holder) view.getTag();
+ holder.episodeProgress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ holder.lenSize.setText(Converter.getDurationStringLong(event.getDuration() - event.getPosition()));
+ }
+ }
+
static class Holder {
LinearLayout container;
TextView title;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
index 30057dde3..148b36d21 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.os.Build;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.MotionEventCompat;
@@ -24,9 +25,11 @@ import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
+import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
@@ -57,6 +60,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
private boolean locked;
private FeedItem selectedItem;
+ private ViewHolder currentlyPlayingItem = null;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
@@ -93,6 +97,18 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
});
}
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int pos, List<Object> payload) {
+ onBindViewHolder(holder, pos);
+
+ if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) {
+ PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0);
+ holder.progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ holder.progressLeft.setText(Converter.getDurationStringLong(event.getPosition()));
+ holder.progressRight.setText(Converter.getDurationStringLong(event.getDuration()));
+ }
+ }
+
@Nullable
public FeedItem getSelectedItem() {
return selectedItem;
@@ -108,6 +124,12 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
return itemAccess.getCount();
}
+ public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) {
+ if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) {
+ notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event);
+ }
+ }
+
public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener,
View.OnCreateContextMenuListener,
@@ -287,6 +309,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
if(media.isCurrentlyPlaying()) {
container.setBackgroundColor(playingBackGroundColor);
+ currentlyPlayingItem = this;
} else {
container.setBackgroundColor(normalBackGroundColor);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
index c0c23685a..b6e3d14dd 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -24,6 +24,7 @@ import android.widget.Toast;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -384,6 +385,13 @@ public abstract class EpisodesListFragment extends Fragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ if (listAdapter != null) {
+ listAdapter.notifyCurrentlyPlayingItemChanged(event);
+ }
+ }
+
protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
return true;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index 348c73b92..9f136490a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -18,7 +18,7 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.ServiceEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
@@ -28,6 +28,9 @@ import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Fragment which is supposed to be displayed outside of the MediaplayerActivity
@@ -138,6 +141,7 @@ public class ExternalPlayerFragment extends Fragment {
controller = setupPlaybackController();
controller.init();
loadMediaInfo();
+ EventBus.getDefault().register(this);
}
@Override
@@ -147,6 +151,12 @@ public class ExternalPlayerFragment extends Fragment {
controller.release();
controller = null;
}
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ onPositionObserverUpdate();
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
index c7ac8286c..745404aeb 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -29,6 +29,7 @@ import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconTextView;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import org.apache.commons.lang3.Validate;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -385,6 +386,13 @@ public class FeedItemlistFragment extends ListFragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ if (adapter != null) {
+ adapter.notifyCurrentlyPlayingItemChanged(event, getListView());
+ }
+ }
+
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index a1f5dca18..ff1e9a28e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -26,6 +26,7 @@ import android.widget.TextView;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -213,6 +214,13 @@ public class QueueFragment extends Fragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ if (recyclerAdapter != null) {
+ recyclerAdapter.notifyCurrentlyPlayingItemChanged(event);
+ }
+ }
+
private void saveScrollPosition() {
int firstItem = layoutManager.findFirstVisibleItemPosition();
View firstItemView = layoutManager.findViewByPosition(firstItem);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
index a04615a00..3e5e75a08 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
@@ -1,29 +1,42 @@
package de.danoeh.antennapod.fragment.preferences;
+import android.Manifest;
import android.app.Activity;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.preference.PreferenceScreen;
import android.util.Log;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
private static final String TAG = "AutoDnldPrefFragment";
+
+ private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
+ private static final String PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT = "prefAutoDownloadWifiFilterAndroid10PermissionPrompt";
+
private CheckBoxPreference[] selectedNetworks;
+ private Preference prefPermissionRequestPromptOnAndroid10 = null;
+
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences_autodownload);
@@ -35,6 +48,12 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
}
@Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.auto_download_label);
+ }
+
+ @Override
public void onResume() {
super.onResume();
checkAutodownloadItemVisibility(UserPreferences.isEnableAutodownload());
@@ -175,10 +194,65 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
}
private void setSelectedNetworksEnabled(boolean b) {
+ if (showPermissionRequestPromptOnAndroid10IfNeeded(b)) {
+ return;
+ }
+
if (selectedNetworks != null) {
for (Preference p : selectedNetworks) {
p.setEnabled(b);
}
}
}
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
+ return;
+ }
+ if (permissions.length > 0 && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION) &&
+ grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ buildAutodownloadSelectedNetworksPreference();
+ }
+ }
+
+ private boolean showPermissionRequestPromptOnAndroid10IfNeeded(boolean wifiFilterEnabled) {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
+ return false;
+ }
+
+ // Cases Android 10(Q) or later
+ if (prefPermissionRequestPromptOnAndroid10 != null) {
+ getPreferenceScreen().removePreference(prefPermissionRequestPromptOnAndroid10);
+ prefPermissionRequestPromptOnAndroid10 = null;
+ }
+
+ if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+
+ // Case location permission not yet granted, permission-specific UI is needed
+ if (!wifiFilterEnabled) {
+ // Don't show the UI when WiFi filter disabled.
+ // it still return true, so that the caller knows
+ // it does not have required permission, and will not invoke codes that require so.
+ return true;
+ }
+
+ Preference pref = new Preference(requireActivity());
+ pref.setKey(PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT);
+ pref.setTitle(R.string.autodl_wifi_filter_permission_title);
+ pref.setSummary(R.string.autodl_wifi_filter_permission_message);
+ pref.setIcon(R.drawable.ic_warning_red);
+ pref.setOnPreferenceClickListener(preference -> {
+ requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
+ return true;
+ });
+ pref.setPersistent(false);
+ getPreferenceScreen().addPreference(pref);
+ prefPermissionRequestPromptOnAndroid10 = pref;
+ return true;
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
index 491922056..ca902b0e4 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
@@ -9,6 +9,7 @@ import android.text.Html;
import android.text.format.DateUtils;
import android.widget.Toast;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.service.GpodnetSyncService;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
@@ -30,6 +31,12 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
}
@Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.gpodnet_main_label);
+ }
+
+ @Override
public void onResume() {
super.onResume();
GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java
index 229274b76..d0c86ca34 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java
@@ -14,6 +14,12 @@ public class IntegrationsPreferencesFragment extends PreferenceFragmentCompat {
setupIntegrationsScreen();
}
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.integrations_label);
+ }
+
private void setupIntegrationsScreen() {
findPreference(PREF_SCREEN_GPODDER).setOnPreferenceClickListener(preference -> {
((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_gpodder);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
index 9f36e1355..2b385851e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
@@ -1,20 +1,15 @@
package de.danoeh.antennapod.fragment.preferences;
-import android.content.ActivityNotFoundException;
import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.preference.PreferenceFragmentCompat;
-import android.util.Log;
-import android.widget.Toast;
import com.bytehamster.lib.preferencesearch.SearchConfiguration;
import com.bytehamster.lib.preferencesearch.SearchPreference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AboutActivity;
import de.danoeh.antennapod.activity.BugReportActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
-import de.danoeh.antennapod.activity.StatisticsActivity;
import de.danoeh.antennapod.core.util.IntentUtils;
public class MainPreferencesFragment extends PreferenceFragmentCompat {
@@ -38,6 +33,12 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
setupSearch();
}
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label);
+ }
+
private void setupMainScreen() {
findPreference(PREF_SCREEN_USER_INTERFACE).setOnPreferenceClickListener(preference -> {
((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_user_interface);
@@ -68,7 +69,8 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
);
findPreference(STATISTICS).setOnPreferenceClickListener(
preference -> {
- startActivity(new Intent(getActivity(), StatisticsActivity.class));
+ getFragmentManager().beginTransaction().replace(R.id.content, new StatisticsFragment())
+ .addToBackStack(getString(R.string.statistics_label)).commit();
return true;
}
);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
index ac2436e25..1d4310869 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
@@ -31,6 +31,12 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
}
@Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.network_pref);
+ }
+
+ @Override
public void onResume() {
super.onResume();
setUpdateIntervalText();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
index e1714d4bd..9a0eec744 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
@@ -8,6 +8,7 @@ import android.support.v7.preference.ListPreference;
import android.support.v7.preference.PreferenceFragmentCompat;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MediaplayerActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
@@ -28,6 +29,12 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
}
@Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.playback_pref);
+ }
+
+ @Override
public void onResume() {
super.onResume();
checkSonicItemVisibility();
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java
index 37199ccf7..6129387c0 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java
@@ -1,23 +1,27 @@
-package de.danoeh.antennapod.activity;
+package de.danoeh.antennapod.fragment.preferences;
+import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatActivity;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RadioButton;
import android.widget.TextView;
-
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.adapter.StatisticsListAdapter;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.Converter;
import io.reactivex.Observable;
@@ -28,10 +32,8 @@ import io.reactivex.schedulers.Schedulers;
/**
* Displays the 'statistics' screen
*/
-public class StatisticsActivity extends AppCompatActivity
- implements AdapterView.OnItemClickListener {
-
- private static final String TAG = StatisticsActivity.class.getSimpleName();
+public class StatisticsFragment extends Fragment implements AdapterView.OnItemClickListener {
+ private static final String TAG = StatisticsFragment.class.getSimpleName();
private static final String PREF_NAME = "StatisticsActivityPrefs";
private static final String PREF_COUNT_ALL = "countAll";
@@ -44,54 +46,51 @@ public class StatisticsActivity extends AppCompatActivity
private SharedPreferences prefs;
@Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- getSupportActionBar().setDisplayShowHomeEnabled(true);
- setContentView(R.layout.statistics_activity);
-
- prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+ prefs = getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
countAll = prefs.getBoolean(PREF_COUNT_ALL, false);
+ setHasOptionsMenu(true);
+ }
- totalTimeTextView = findViewById(R.id.total_time);
- feedStatisticsList = findViewById(R.id.statistics_list);
- progressBar = findViewById(R.id.progressBar);
- listAdapter = new StatisticsListAdapter(this);
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View root = inflater.inflate(R.layout.statistics_activity, container, false);
+ totalTimeTextView = root.findViewById(R.id.total_time);
+ feedStatisticsList = root.findViewById(R.id.statistics_list);
+ progressBar = root.findViewById(R.id.progressBar);
+ listAdapter = new StatisticsListAdapter(getContext());
listAdapter.setCountAll(countAll);
feedStatisticsList.setAdapter(listAdapter);
feedStatisticsList.setOnItemClickListener(this);
+ return root;
}
@Override
- public void onResume() {
- super.onResume();
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.statistics_label);
refreshStatistics();
}
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- MenuInflater inflater = getMenuInflater();
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.statistics, menu);
- return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- finish();
- return true;
- } else if (item.getItemId() == R.id.statistics_mode) {
+ if (item.getItemId() == R.id.statistics_mode) {
selectStatisticsMode();
return true;
- } else {
- return super.onOptionsItemSelected(item);
}
+ return super.onOptionsItemSelected(item);
}
private void selectStatisticsMode() {
- View contentView = View.inflate(this, R.layout.statistics_mode_select_dialog, null);
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ View contentView = View.inflate(getContext(), R.layout.statistics_mode_select_dialog, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setView(contentView);
builder.setTitle(R.string.statistics_mode);
@@ -126,8 +125,8 @@ public class StatisticsActivity extends AppCompatActivity
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
- totalTimeTextView.setText(Converter
- .shortLocalizedDuration(this, countAll ? result.totalTimeCountAll : result.totalTime));
+ totalTimeTextView.setText(Converter.shortLocalizedDuration(getContext(),
+ countAll ? result.totalTimeCountAll : result.totalTime));
listAdapter.update(result.feedTime);
progressBar.setVisibility(View.GONE);
totalTimeTextView.setVisibility(View.VISIBLE);
@@ -139,14 +138,13 @@ public class StatisticsActivity extends AppCompatActivity
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
DBReader.StatisticsItem stats = listAdapter.getItem(position);
- AlertDialog.Builder dialog = new AlertDialog.Builder(this);
+ AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
dialog.setTitle(stats.feed.getTitle());
dialog.setMessage(getString(R.string.statistics_details_dialog,
countAll ? stats.episodesStartedIncludingMarked : stats.episodesStarted,
- stats.episodes,
- Converter.shortLocalizedDuration(this, countAll ?
- stats.timePlayedCountAll : stats.timePlayed),
- Converter.shortLocalizedDuration(this, stats.time)));
+ stats.episodes, Converter.shortLocalizedDuration(getContext(),
+ countAll ? stats.timePlayedCountAll : stats.timePlayed),
+ Converter.shortLocalizedDuration(getContext(), stats.time)));
dialog.setPositiveButton(android.R.string.ok, null);
dialog.show();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
index e36476c6f..1cbb5cde2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
@@ -23,6 +23,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
import de.danoeh.antennapod.activity.ImportExportActivity;
import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.asynctask.DocumentFileExportWorker;
import de.danoeh.antennapod.asynctask.ExportWorker;
import de.danoeh.antennapod.core.export.ExportWriter;
@@ -64,6 +65,12 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
}
@Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.storage_pref);
+ }
+
+ @Override
public void onResume() {
super.onResume();
setDataFolderText();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
index e1d44f7d3..7b5eeb125 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
@@ -11,6 +11,7 @@ import android.widget.ListView;
import android.widget.Toast;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import org.apache.commons.lang3.ArrayUtils;
@@ -25,6 +26,12 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat {
setupInterfaceScreen();
}
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.user_interface_label);
+ }
+
private void setupInterfaceScreen() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
diff --git a/app/src/main/res/layout/statistics_activity.xml b/app/src/main/res/layout/statistics_activity.xml
index 4a72dc7de..fe42ce32e 100644
--- a/app/src/main/res/layout/statistics_activity.xml
+++ b/app/src/main/res/layout/statistics_activity.xml
@@ -1,41 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingTop="8dp"
- android:paddingBottom="8dp">
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/total_time_listened_to_podcasts"
- android:gravity="center_horizontal"/>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="16dp">
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/progressBar"
- android:layout_gravity="center_horizontal"
- style="?android:attr/progressBarStyleLarge"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:text="@string/total_time_listened_to_podcasts"
+ android:gravity="center_horizontal"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/total_time"
- android:gravity="center_horizontal"
- android:textSize="45sp"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/total_time"
+ android:textColor="?android:attr/textColorPrimary"
+ android:gravity="center_horizontal"
+ android:textSize="28sp"
+ tools:text="10.0 hours"/>
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/progressBar"
+ android:layout_gravity="center_horizontal"
+ style="?android:attr/progressBarStyleSmall"/>
+ </LinearLayout>
+
+ <View
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/dividerVertical"/>
<ListView
- android:id="@+id/statistics_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:choiceMode="singleChoice"
- android:clipToPadding="false"
- android:divider="@android:color/transparent"
- android:dividerHeight="0dp"
- android:paddingBottom="@dimen/list_vertical_padding"
- android:paddingTop="@dimen/list_vertical_padding"
- android:scrollbarStyle="outsideOverlay" />
+ android:id="@+id/statistics_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:choiceMode="singleChoice"
+ android:clipToPadding="false"
+ android:divider="@android:color/transparent"
+ android:dividerHeight="0dp"
+ android:paddingBottom="@dimen/list_vertical_padding"
+ android:paddingTop="@dimen/list_vertical_padding"
+ android:scrollbarStyle="outsideOverlay"
+ tools:listitem="@layout/statistics_listitem"/>
</LinearLayout>
diff --git a/app/src/main/res/layout/statistics_listitem.xml b/app/src/main/res/layout/statistics_listitem.xml
index f52aa73e0..b85cdc74e 100644
--- a/app/src/main/res/layout/statistics_listitem.xml
+++ b/app/src/main/res/layout/statistics_listitem.xml
@@ -3,63 +3,54 @@
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="@dimen/listitem_iconwithtext_height"
- android:paddingLeft="@dimen/listitem_threeline_verticalpadding"
- android:paddingStart="@dimen/listitem_threeline_verticalpadding"
- android:paddingRight="@dimen/listitem_threeline_verticalpadding"
- android:paddingEnd="@dimen/listitem_threeline_verticalpadding"
- tools:background="@android:color/darker_gray">
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp">
<ImageView
- android:id="@+id/imgvCover"
- android:contentDescription="@string/cover_label"
- android:layout_width="@dimen/thumbnail_length_navlist"
- android:layout_height="@dimen/thumbnail_length_navlist"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:adjustViewBounds="true"
- android:cropToPadding="true"
- android:scaleType="centerCrop"
- android:layout_marginTop="4dp"
- android:layout_marginBottom="4dp"
- tools:src="@drawable/ic_antenna"
- tools:background="@android:color/holo_green_dark"/>
+ android:id="@+id/imgvCover"
+ android:contentDescription="@string/cover_label"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:adjustViewBounds="true"
+ android:cropToPadding="true"
+ android:scaleType="centerCrop"
+ tools:src="@drawable/ic_antenna"
+ tools:background="@android:color/holo_green_dark"/>
<TextView
- android:id="@+id/txtvTime"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/list_vertical_padding"
- android:layout_marginStart="@dimen/list_vertical_padding"
- android:lines="1"
- android:textColor="?android:attr/textColorTertiary"
- android:textSize="@dimen/text_size_navdrawer"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- tools:text="23"
- tools:background="@android:color/holo_green_dark"/>
+ android:id="@+id/txtvTitle"
+ android:lines="1"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_alignTop="@id/imgvCover"
+ android:layout_alignWithParentIfMissing="true"
+ tools:text="Feed title"/>
<TextView
- android:id="@+id/txtvTitle"
- android:lines="1"
- android:ellipsize="end"
- android:singleLine="true"
- android:layout_centerVertical="true"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_navdrawer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/listitem_iconwithtext_textleftpadding"
- android:layout_marginStart="@dimen/listitem_iconwithtext_textleftpadding"
- android:layout_toRightOf="@id/imgvCover"
- android:layout_toEndOf="@id/imgvCover"
- android:layout_toLeftOf="@id/txtvTime"
- android:layout_toStartOf="@id/txtvTime"
- android:layout_alignWithParentIfMissing="true"
- tools:text="Navigation feed item title"
- tools:background="@android:color/holo_green_dark"/>
-
+ android:id="@+id/txtvTime"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:lines="1"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textSize="14sp"
+ android:layout_toEndOf="@+id/imgvCover"
+ android:layout_toRightOf="@+id/imgvCover"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_below="@+id/txtvTitle"
+ tools:text="23 hours"/>
</RelativeLayout>
diff --git a/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java b/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
index 87304b3d6..caca8a6e3 100644
--- a/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ b/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
@@ -29,17 +29,34 @@ public abstract class CastEnabledActivity extends AppCompatActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String TAG = "CastEnabledActivity";
- protected CastManager castManager;
- protected SwitchableMediaRouteActionProvider mediaRouteActionProvider;
+ private CastConsumer castConsumer;
+ private CastManager castManager;
+
+ private SwitchableMediaRouteActionProvider mediaRouteActionProvider;
private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (!CastManager.isInitialized()) {
+ return;
+ }
+
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).
registerOnSharedPreferenceChangeListener(this);
+ castConsumer = new DefaultCastConsumer() {
+ @Override
+ public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
+ onCastConnectionChanged(true);
+ }
+
+ @Override
+ public void onDisconnected() {
+ onCastConnectionChanged(false);
+ }
+ };
castManager = CastManager.getInstance();
castManager.addCastConsumer(castConsumer);
castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
@@ -48,6 +65,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@Override
protected void onDestroy() {
+ if (!CastManager.isInitialized()) {
+ super.onDestroy();
+ return;
+ }
PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
.unregisterOnSharedPreferenceChangeListener(this);
castManager.removeCastConsumer(castConsumer);
@@ -58,6 +79,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@CallSuper
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
+ if (!CastManager.isInitialized()) {
+ return true;
+ }
getMenuInflater().inflate(R.menu.cast_enabled, menu);
castButtonVisibilityManager.setMenu(menu);
return true;
@@ -67,6 +91,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@CallSuper
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
+ if (!CastManager.isInitialized()) {
+ return true;
+ }
+
MenuItem mediaRouteButton = menu.findItem(R.id.media_route_menu_item);
if (mediaRouteButton == null) {
Log.wtf(TAG, "MediaRoute item could not be found on the menu!", new Exception());
@@ -83,15 +111,22 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@Override
protected void onResume() {
super.onResume();
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.setResumed(true);
}
@Override
protected void onPause() {
super.onPause();
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.setResumed(false);
}
+
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
@@ -105,18 +140,6 @@ public abstract class CastEnabledActivity extends AppCompatActivity
}
}
- CastConsumer castConsumer = new DefaultCastConsumer() {
- @Override
- public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
- onCastConnectionChanged(true);
- }
-
- @Override
- public void onDisconnected() {
- onCastConnectionChanged(false);
- }
- };
-
private void onCastConnectionChanged(boolean connected) {
if (connected) {
castButtonVisibilityManager.onConnected();
@@ -133,6 +156,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
* @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
*/
public final void requestCastButton(int showAsAction) {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.requestCastButton(showAsAction);
}
diff --git a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
index c9d52df0c..0e69da61e 100644
--- a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
+++ b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
@@ -1,8 +1,13 @@
package de.danoeh.antennapod.preferences;
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AlertDialog;
+
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment;
@@ -18,6 +23,7 @@ public class PreferenceControllerFlavorHelper {
final int googlePlayServicesCheck = GoogleApiAvailability.getInstance()
.isGooglePlayServicesAvailable(ui.getActivity());
if (googlePlayServicesCheck == ConnectionResult.SUCCESS) {
+ displayRestartRequiredDialog(ui.requireContext());
return true;
} else {
GoogleApiAvailability.getInstance()
@@ -29,4 +35,13 @@ public class PreferenceControllerFlavorHelper {
return true;
});
}
+
+ private static void displayRestartRequiredDialog(@NonNull Context context) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(context);
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.setCancelable(false);
+ dialog.show();
+ }
}
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index 4bc3d0b51..0a5b47eb8 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -10,6 +10,7 @@
<property name="fileExtensions" value="java, xml"/>
<module name="TreeWalker">
+ <module name="OuterTypeFilename"/>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
@@ -17,12 +18,65 @@
</module>
<module name="AvoidStarImport"/>
<module name="OneTopLevelClass"/>
+ <module name="NoLineWrap"/>
+ <module name="EmptyBlock">
+ <property name="option" value="TEXT"/>
+ <property name="tokens"
+ value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
+ </module>
<module name="OneStatementPerLine"/>
+ <module name="FallThrough"/>
+ <module name="UpperEll"/>
<module name="ModifierOrder"/>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
+ <module name="TypeName">
+ <message key="name.invalidPattern"
+ value="Type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="CatchParameterName">
+ <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
+ <message key="name.invalidPattern"
+ value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="ClassTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern"
+ value="Class type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="MethodTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern"
+ value="Method type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="InterfaceTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern"
+ value="Interface type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="NoFinalizer"/>
+ <module name="GenericWhitespace">
+ <message key="ws.followed"
+ value="GenericWhitespace ''{0}'' is followed by whitespace."/>
+ <message key="ws.preceded"
+ value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
+ <message key="ws.illegalFollow"
+ value="GenericWhitespace ''{0}'' should followed by whitespace."/>
+ <message key="ws.notPreceded"
+ value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
+ </module>
+ <module name="AnnotationLocation">
+ <property name="id" value="AnnotationLocationMostCases"/>
+ <property name="tokens"
+ value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
+ </module>
+ <module name="AnnotationLocation">
+ <property name="id" value="AnnotationLocationVariables"/>
+ <property name="tokens" value="VARIABLE_DEF"/>
+ <property name="allowSamelineMultipleAnnotations" value="true"/>
+ </module>
</module>
</module>
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackPositionEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackPositionEvent.java
new file mode 100644
index 000000000..3327d8a02
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackPositionEvent.java
@@ -0,0 +1,19 @@
+package de.danoeh.antennapod.core.event;
+
+public class PlaybackPositionEvent {
+ private final int position;
+ private final int duration;
+
+ public PlaybackPositionEvent(int position, int duration) {
+ this.position = position;
+ this.duration = duration;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public int getDuration() {
+ return duration;
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java
deleted file mode 100644
index 3769d6bb1..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-
-public class ProgressEvent {
-
- public enum Action {
- START, END
- }
-
- public final Action action;
- public final String message;
-
- private ProgressEvent(Action action, String message) {
- this.action = action;
- this.message = message;
- }
-
- public static ProgressEvent start(String message) {
- return new ProgressEvent(Action.START, message);
- }
-
- public static ProgressEvent end() {
- return new ProgressEvent(Action.END, null);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
- .append("action", action)
- .append("message", message)
- .toString();
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
index 3495164a6..744e9b924 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
@@ -218,6 +218,8 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, ImageR
return itemIdentifier;
} else if (title != null && !title.isEmpty()) {
return title;
+ } else if (hasMedia() && media.getDownload_url() != null) {
+ return media.getDownload_url();
} else {
return link;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
index bb34e2c0f..3dd87cc0b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
@@ -92,11 +92,9 @@ class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
@Nullable
@Override
public LoadData<InputStream> buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) {
- Log.d(TAG, "buildLoadData() called with: " + "model = [" + model + "], width = ["
- + width + "], height = [" + height + "]");
- if(TextUtils.isEmpty(model)) {
+ if (TextUtils.isEmpty(model)) {
return null;
- } else if(model.startsWith("/")) {
+ } else if (model.startsWith("/")) {
return new LoadData<>(new ObjectKey(model), new AudioCoverFetcher(model));
} else {
GlideUrl url = new GlideUrl(model);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
index a4cc22d8b..f2c0c8fe3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
@@ -4,7 +4,12 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
+import android.util.Log;
import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.service.playback.PlayerStatus;
+import de.danoeh.antennapod.core.util.playback.Playable;
/**
* Provides access to preferences set by the playback service. A private
@@ -19,35 +24,35 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference
* Contains the feed id of the currently playing item if it is a FeedMedia
* object.
*/
- public static final String PREF_CURRENTLY_PLAYING_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId";
+ private static final String PREF_CURRENTLY_PLAYING_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId";
/**
* Contains the id of the currently playing FeedMedia object or
* NO_MEDIA_PLAYING if the currently playing media is no FeedMedia object.
*/
- public static final String PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedMediaId";
+ private static final String PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedMediaId";
/**
* Type of the media object that is currently being played. This preference
* is set to NO_MEDIA_PLAYING after playback has been completed and is set
* as soon as the 'play' button is pressed.
*/
- public static final String PREF_CURRENTLY_PLAYING_MEDIA = "de.danoeh.antennapod.preferences.currentlyPlayingMedia";
+ private static final String PREF_CURRENTLY_PLAYING_MEDIA = "de.danoeh.antennapod.preferences.currentlyPlayingMedia";
/**
* True if last played media was streamed.
*/
- public static final String PREF_CURRENT_EPISODE_IS_STREAM = "de.danoeh.antennapod.preferences.lastIsStream";
+ private static final String PREF_CURRENT_EPISODE_IS_STREAM = "de.danoeh.antennapod.preferences.lastIsStream";
/**
* True if last played media was a video.
*/
- public static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo";
+ private static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo";
/**
* The current player status as int.
*/
- public static final String PREF_CURRENT_PLAYER_STATUS = "de.danoeh.antennapod.preferences.currentPlayerStatus";
+ private static final String PREF_CURRENT_PLAYER_STATUS = "de.danoeh.antennapod.preferences.currentPlayerStatus";
/**
* Value of PREF_CURRENTLY_PLAYING_MEDIA if no media is playing.
@@ -87,10 +92,6 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference
}
}
- public static long getLastPlayedFeedId() {
- return prefs.getLong(PREF_CURRENTLY_PLAYING_FEED_ID, -1);
- }
-
public static long getCurrentlyPlayingMedia() {
return prefs.getLong(PREF_CURRENTLY_PLAYING_MEDIA, NO_MEDIA_PLAYING);
}
@@ -119,4 +120,52 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference
editor.putInt(PREF_CURRENT_PLAYER_STATUS, PLAYER_STATUS_OTHER);
editor.apply();
}
+
+ public static void writeMediaPlaying(Playable playable, PlayerStatus playerStatus, boolean stream) {
+ Log.d(TAG, "Writing playback preferences");
+ SharedPreferences.Editor editor = prefs.edit();
+
+ if (playable == null) {
+ writeNoMediaPlaying();
+ } else {
+ editor.putLong(PREF_CURRENTLY_PLAYING_MEDIA, playable.getPlayableType());
+ editor.putBoolean(PREF_CURRENT_EPISODE_IS_STREAM, stream);
+ editor.putBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, playable.getMediaType() == MediaType.VIDEO);
+ if (playable instanceof FeedMedia) {
+ FeedMedia fMedia = (FeedMedia) playable;
+ editor.putLong(PREF_CURRENTLY_PLAYING_FEED_ID, fMedia.getItem().getFeed().getId());
+ editor.putLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, fMedia.getId());
+ } else {
+ editor.putLong(PREF_CURRENTLY_PLAYING_FEED_ID, NO_MEDIA_PLAYING);
+ editor.putLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING);
+ }
+ playable.writeToPreferences(editor);
+ }
+ editor.putInt(PREF_CURRENT_PLAYER_STATUS, getCurrentPlayerStatusAsInt(playerStatus));
+
+ editor.apply();
+ }
+
+ public static void writePlayerStatus(PlayerStatus playerStatus) {
+ Log.d(TAG, "Writing player status playback preferences");
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(PREF_CURRENT_PLAYER_STATUS, getCurrentPlayerStatusAsInt(playerStatus));
+ editor.apply();
+ }
+
+ private static int getCurrentPlayerStatusAsInt(PlayerStatus playerStatus) {
+ int playerStatusAsInt;
+ switch (playerStatus) {
+ case PLAYING:
+ playerStatusAsInt = PLAYER_STATUS_PLAYING;
+ break;
+ case PAUSED:
+ playerStatusAsInt = PLAYER_STATUS_PAUSED;
+ break;
+ default:
+ playerStatusAsInt = PLAYER_STATUS_OTHER;
+ }
+ return playerStatusAsInt;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
index 0f346893e..b429740bf 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
@@ -13,6 +13,8 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
@@ -190,10 +192,8 @@ public class DownloadService extends Service {
handleFailedDownload(status, downloader.getDownloadRequest());
if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- long id = status.getFeedfileId();
- FeedMedia media = DBReader.getFeedMedia(id);
- FeedItem item;
- if (media == null || (item = media.getItem()) == null) {
+ FeedItem item = getFeedItemFromId(status.getFeedfileId());
+ if (item == null) {
return;
}
boolean httpNotFound = status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
@@ -213,9 +213,8 @@ public class DownloadService extends Service {
// if FeedMedia download has been canceled, fake FeedItem update
// so that lists reload that it
if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
- FeedItem item;
- if (media == null || (item = media.getItem()) == null) {
+ FeedItem item = getFeedItemFromId(status.getFeedfileId());
+ if (item == null) {
return;
}
EventBus.getDefault().post(FeedItemEvent.updated(item));
@@ -386,6 +385,12 @@ public class DownloadService extends Service {
Downloader d = getDownloader(url);
if (d != null) {
d.cancel();
+ DownloadRequester.getInstance().removeDownload(d.getDownloadRequest());
+
+ FeedItem item = getFeedItemFromId(d.getDownloadRequest().getFeedfileId());
+ if (item != null) {
+ EventBus.getDefault().post(FeedItemEvent.updated(item));
+ }
} else {
Log.e(TAG, "Could not cancel download with url " + url);
}
@@ -430,12 +435,40 @@ public class DownloadService extends Service {
queryDownloads();
}
- private Downloader getDownloader(DownloadRequest request) {
- if (!URLUtil.isHttpUrl(request.getSource()) && !URLUtil.isHttpsUrl(request.getSource())) {
- Log.e(TAG, "Could not find appropriate downloader for " + request.getSource());
- return null;
+ @VisibleForTesting
+ public interface DownloaderFactory {
+ @Nullable
+ Downloader create(@NonNull DownloadRequest request);
+ }
+
+ private static class DefaultDownloaderFactory implements DownloaderFactory {
+ @Nullable
+ @Override
+ public Downloader create(@NonNull DownloadRequest request) {
+ if (!URLUtil.isHttpUrl(request.getSource()) && !URLUtil.isHttpsUrl(request.getSource())) {
+ Log.e(TAG, "Could not find appropriate downloader for " + request.getSource());
+ return null;
+ }
+ return new HttpDownloader(request);
}
- return new HttpDownloader(request);
+ }
+
+ private static DownloaderFactory downloaderFactory = new DefaultDownloaderFactory();
+
+ @VisibleForTesting
+ public static DownloaderFactory getDownloaderFactory() {
+ return downloaderFactory;
+ }
+
+ // public scope rather than package private,
+ // because androidTest put classes in the non-standard de.test.antennapod hierarchy
+ @VisibleForTesting
+ public static void setDownloaderFactory(DownloaderFactory downloaderFactory) {
+ DownloadService.downloaderFactory = downloaderFactory;
+ }
+
+ private Downloader getDownloader(@NonNull DownloadRequest request) {
+ return downloaderFactory.create(request);
}
/**
@@ -578,6 +611,16 @@ public class DownloadService extends Service {
syncExecutor.execute(new FailedDownloadHandler(status, request));
}
+ @Nullable
+ private FeedItem getFeedItemFromId(long id) {
+ FeedMedia media = DBReader.getFeedMedia(id);
+ if (media != null) {
+ return media.getItem();
+ } else {
+ return null;
+ }
+ }
+
/**
* Takes a single Feed, parses the corresponding file and refreshes
* information in the manager
@@ -654,6 +697,7 @@ public class DownloadService extends Service {
Log.e(TAG, "FeedSyncThread was interrupted");
} catch (ExecutionException e) {
Log.e(TAG, "ExecutionException in FeedSyncThread: " + e.getMessage());
+ e.printStackTrace();
}
}
@@ -688,6 +732,7 @@ public class DownloadService extends Service {
Log.e(TAG, "FeedSyncThread was interrupted");
} catch (ExecutionException e) {
Log.e(TAG, "ExecutionException in FeedSyncThread: " + e.getMessage());
+ e.printStackTrace();
}
}
@@ -983,14 +1028,17 @@ public class DownloadService extends Service {
final FeedItem item = media.getItem();
try {
+ DBWriter.setFeedMedia(media).get();
+
// we've received the media, we don't want to autodownload it again
if (item != null) {
item.setAutoDownload(false);
+ // setFeedItem() signals (via EventBus) that the item has been updated,
+ // so we do it after the enclosing media has been updated above,
+ // to ensure subscribers will get the updated FeedMedia as well
DBWriter.setFeedItem(item).get();
}
- DBWriter.setFeedMedia(media).get();
-
if (item != null && UserPreferences.enqueueDownloadedEpisodes() &&
!DBTasks.isInQueue(DownloadService.this, item.getId())) {
DBWriter.addQueueItem(DownloadService.this, item).get();
@@ -1058,7 +1106,13 @@ public class DownloadService extends Service {
private final Runnable postDownloaderTask = new Runnable() {
@Override
public void run() {
- List<Downloader> list = Collections.unmodifiableList(downloads);
+ List<Downloader> runningDownloads = new ArrayList<>();
+ for (Downloader downloader : downloads) {
+ if (!downloader.cancelled) {
+ runningDownloads.add(downloader);
+ }
+ }
+ List<Downloader> list = Collections.unmodifiableList(runningDownloads);
EventBus.getDefault().postSticky(DownloadEvent.refresh(list));
postHandler.postDelayed(postDownloaderTask, 1500);
}
@@ -1076,6 +1130,9 @@ public class DownloadService extends Service {
private static String compileNotificationString(List<Downloader> downloads) {
List<String> lines = new ArrayList<>(downloads.size());
for (Downloader downloader : downloads) {
+ if (downloader.cancelled) {
+ continue;
+ }
StringBuilder line = new StringBuilder("• ");
DownloadRequest request = downloader.getDownloadRequest();
switch (request.getFeedfileType()) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
index c3b412012..556aad573 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
@@ -26,6 +26,7 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -63,6 +64,7 @@ public class ExoPlayerWrapper implements IPlayer {
loadControl.setBufferDurationsMs(30000, 120000,
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS);
+ loadControl.setBackBuffer(UserPreferences.getRewindSecs() * 1000 + 500, true);
SimpleExoPlayer p = ExoPlayerFactory.newSimpleInstance(mContext, new DefaultRenderersFactory(mContext),
new DefaultTrackSelector(), loadControl.createDefaultLoadControl());
p.setSeekParameters(SeekParameters.PREVIOUS_SYNC);
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 9164f561f..f8f095ea8 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
@@ -584,7 +584,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
}
playerLock.unlock();
- Log.d(TAG, "getPosition() -> " + retVal);
return retVal;
}
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 a3535616e..e58d5cb31 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
@@ -12,7 +12,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
@@ -45,10 +44,12 @@ import com.bumptech.glide.request.target.Target;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.MessageEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed;
@@ -66,7 +67,6 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.FeedSearcher;
-import de.danoeh.antennapod.core.util.IntList;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.QueueAccess;
@@ -74,6 +74,9 @@ import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
import org.greenrobot.eventbus.EventBus;
/**
@@ -212,6 +215,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
private PlaybackServiceTaskManager taskManager;
private PlaybackServiceFlavorHelper flavorHelper;
private PlaybackServiceStateManager stateManager;
+ private Disposable positionEventTimer;
/**
* Used for Lollipop notifications, Android Wear, and Android Auto.
@@ -331,8 +335,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
isRunning = false;
currentMediaType = MediaType.UNKNOWN;
- PreferenceManager.getDefaultSharedPreferences(this)
- .unregisterOnSharedPreferenceChangeListener(prefListener);
+ cancelPositionObserver();
+ PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(prefListener);
if (mediaSession != null) {
mediaSession.release();
}
@@ -566,7 +570,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
if (status == PlayerStatus.PLAYING) {
- mediaPlayer.pause(!UserPreferences.isPersistNotify(), true);
+ mediaPlayer.pause(!UserPreferences.isPersistNotify(), false);
} else if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED) {
mediaPlayer.resume();
} else if (status == PlayerStatus.PREPARING) {
@@ -590,7 +594,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
return true;
case KeyEvent.KEYCODE_MEDIA_PAUSE:
if (status == PlayerStatus.PLAYING) {
- mediaPlayer.pause(!UserPreferences.isPersistNotify(), true);
+ mediaPlayer.pause(!UserPreferences.isPersistNotify(), false);
}
return true;
@@ -717,7 +721,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
updateMediaSession(newInfo.playerStatus);
switch (newInfo.playerStatus) {
case INITIALIZED:
- writePlaybackPreferences();
+ PlaybackPreferences.writeMediaPlaying(mediaPlayer.getPSMPInfo().playable,
+ mediaPlayer.getPSMPInfo().playerStatus, mediaPlayer.isStreaming());
break;
case PREPARED:
@@ -734,7 +739,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
// remove notification on pause
stateManager.stopForeground(true);
}
- writePlayerStatusPlaybackPreferences();
+ cancelPositionObserver();
+ PlaybackPreferences.writePlayerStatus(mediaPlayer.getPlayerStatus());
break;
case STOPPED:
@@ -743,8 +749,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
break;
case PLAYING:
- writePlayerStatusPlaybackPreferences();
+ PlaybackPreferences.writePlayerStatus(mediaPlayer.getPlayerStatus());
setupNotification(newInfo);
+ setupPositionUpdater();
stateManager.validStartCommandWasReceived();
// set sleep timer if auto-enabled
if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING &&
@@ -1023,82 +1030,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_disabled_label)));
}
- private int getCurrentPlayerStatusAsInt(PlayerStatus playerStatus) {
- int playerStatusAsInt;
- switch (playerStatus) {
- case PLAYING:
- playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_PLAYING;
- break;
- case PAUSED:
- playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_PAUSED;
- break;
- default:
- playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_OTHER;
- }
- return playerStatusAsInt;
- }
-
- private void writePlaybackPreferences() {
- Log.d(TAG, "Writing playback preferences");
-
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()).edit();
- PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
- MediaType mediaType = mediaPlayer.getCurrentMediaType();
- boolean stream = mediaPlayer.isStreaming();
- int playerStatus = getCurrentPlayerStatusAsInt(info.playerStatus);
-
- if (info.playable != null) {
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
- info.playable.getPlayableType());
- editor.putBoolean(
- PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM,
- stream);
- editor.putBoolean(
- PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO,
- mediaType == MediaType.VIDEO);
- if (info.playable instanceof FeedMedia) {
- FeedMedia fMedia = (FeedMedia) info.playable;
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
- fMedia.getItem().getFeed().getId());
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
- fMedia.getId());
- } else {
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- }
- info.playable.writeToPreferences(editor);
- } else {
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- }
- editor.putInt(
- PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
-
- editor.apply();
- }
-
- private void writePlayerStatusPlaybackPreferences() {
- Log.d(TAG, "Writing player status playback preferences");
-
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()).edit();
- int playerStatus = getCurrentPlayerStatusAsInt(mediaPlayer.getPlayerStatus());
- editor.putInt(PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
- editor.apply();
- }
-
private void sendNotificationBroadcast(int type, int code) {
Intent intent = new Intent(ACTION_PLAYER_NOTIFICATION);
intent.putExtra(EXTRA_NOTIFICATION_TYPE, type);
@@ -1653,6 +1584,24 @@ public class PlaybackService extends MediaBrowserServiceCompat {
return mediaPlayer.getVideoSize();
}
+ private void setupPositionUpdater() {
+ if (positionEventTimer != null) {
+ positionEventTimer.dispose();
+ }
+
+ Log.d(TAG, "Setting up position observer");
+ positionEventTimer = Observable.interval(1, TimeUnit.SECONDS)
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(aLong ->
+ EventBus.getDefault().post(new PlaybackPositionEvent(getCurrentPosition(), getDuration())));
+ }
+
+ private void cancelPositionObserver() {
+ if (positionEventTimer != null) {
+ positionEventTimer.dispose();
+ }
+ }
+
private final MediaSessionCompat.Callback sessionCallback = new MediaSessionCompat.Callback() {
private static final String TAG = "MediaSessionCompat";
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
index 68839023e..1a13f9e9f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
@@ -7,6 +7,9 @@ import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.util.Log;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -14,13 +17,12 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.playback.Playable;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
import io.reactivex.Completable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
@@ -102,6 +104,34 @@ public class PlaybackServiceTaskManager {
}
}
+ @Subscribe
+ public void onEvent(FeedItemEvent event) {
+ // Use case: when an item in the queue has been downloaded,
+ // listening to the event to ensure the downloaded item will be used.
+ Log.d(TAG, "onEvent(FeedItemEvent " + event + ")");
+
+ for (FeedItem item : event.items) {
+ if (isItemInQueue(item.getId())) {
+ Log.d(TAG, "onEvent(FeedItemEvent) - some item (" + item.getId() + ") in the queue has been updated (usually downloaded). Refresh the queue.");
+ cancelQueueLoader();
+ loadQueue();
+ return;
+ }
+ }
+ }
+
+ private boolean isItemInQueue(long itemId) {
+ List<FeedItem> queue = getQueueIfLoaded();
+ if (queue != null) {
+ for (FeedItem item : queue) {
+ if (item.getId() == itemId) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Returns the queue if it is already loaded or null if it hasn't been loaded yet.
* In order to wait until the queue has been loaded, use getQueue()
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
index 4c15f5f00..46fa4b99c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
@@ -4,6 +4,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Looper;
+import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
@@ -440,10 +441,9 @@ public final class DBTasks {
/**
* Get a FeedItem by its identifying value.
*/
- private static FeedItem searchFeedItemByIdentifyingValue(Feed feed,
- String identifier) {
+ private static FeedItem searchFeedItemByIdentifyingValue(Feed feed, String identifier) {
for (FeedItem item : feed.getItems()) {
- if (item.getIdentifyingValue().equals(identifier)) {
+ if (TextUtils.equals(item.getIdentifyingValue(), identifier)) {
return item;
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
index a3271bcf9..a1732e08f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
@@ -28,8 +28,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -38,7 +36,6 @@ import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.util.LongIntMap;
-import org.greenrobot.eventbus.EventBus;
// TODO Remove media column from feeditem table
@@ -1478,11 +1475,9 @@ public class PodDBAdapter {
@Override
public void onUpgrade(final SQLiteDatabase db, final int oldVersion,
final int newVersion) {
- EventBus.getDefault().post(ProgressEvent.start(context.getString(R.string.progress_upgrading_database)));
Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to "
+ newVersion + ".");
DBUpgrader.upgrade(db, oldVersion, newVersion);
- EventBus.getDefault().post(ProgressEvent.end());
}
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java b/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
index 0fe11ec53..37f12c01c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
@@ -77,7 +77,7 @@ public final class Optional<T> {
* @param <T> Type of the non-existent value
* @return an empty {@code Optional}
*/
- public static<T> Optional<T> empty() {
+ public static <T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java b/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java
index 37172d042..0c21ca393 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java
@@ -164,7 +164,7 @@ public class QueueSorter {
Collections.sort(feedItems, itemComparator);
if (spread == 0) {
spread = feedItems.size();
- } else if (feedItems.size() % spread != 0){
+ } else if (spread % feedItems.size() != 0){
spread *= feedItems.size();
}
}
@@ -180,6 +180,9 @@ public class QueueSorter {
Map<Long, List<FeedItem>> spreadItems = new HashMap<>();
for (List<FeedItem> feedItems : feeds) {
long thisSpread = spread / feedItems.size();
+ if (thisSpread == 0) {
+ thisSpread = 1;
+ }
// Starting from 0 ensures we front-load, so the queue starts with one episode from
// each feed in the queue
long itemSpread = 0;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
index d22d08e09..a3f747e09 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
@@ -72,7 +72,7 @@ public class ChapterReader extends ID3Reader {
String decodedLink = URLDecoder.decode(link.toString(), "UTF-8");
currentChapter.setLink(decodedLink);
Log.d(TAG, "Found link: " + currentChapter.getLink());
- } catch (IllegalArgumentException _iae) {
+ } catch (IllegalArgumentException iae) {
Log.w(TAG, "Bad URL found in ID3 data");
}
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 ac5418dd0..1456ebd8d 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
@@ -21,11 +21,10 @@ import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TextView;
-import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -69,9 +68,6 @@ public class PlaybackController {
private final ScheduledThreadPoolExecutor schedExecutor;
private static final int SCHED_EX_POOLSIZE = 1;
- private MediaPositionObserver positionObserver;
- private ScheduledFuture<?> positionObserverFuture;
-
private boolean mediaInfoLoaded = false;
private boolean released = false;
private boolean initialized = false;
@@ -177,7 +173,6 @@ public class PlaybackController {
} catch (IllegalArgumentException e) {
// ignore
}
- cancelPositionObserver();
schedExecutor.shutdownNow();
media = null;
released = true;
@@ -254,29 +249,6 @@ public class PlaybackController {
.getIntent());
}
-
-
- private void setupPositionObserver() {
- if (positionObserverFuture == null ||
- positionObserverFuture.isCancelled() ||
- positionObserverFuture.isDone()) {
-
- Log.d(TAG, "Setting up position observer");
- positionObserver = new MediaPositionObserver();
- positionObserverFuture = schedExecutor.scheduleWithFixedDelay(
- positionObserver, MediaPositionObserver.WAITING_INTERVALL,
- MediaPositionObserver.WAITING_INTERVALL,
- TimeUnit.MILLISECONDS);
- }
- }
-
- private void cancelPositionObserver() {
- if (positionObserverFuture != null) {
- boolean result = positionObserverFuture.cancel(true);
- Log.d(TAG, "PositionObserver cancelled. Result: " + result);
- }
- }
-
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if(service instanceof PlaybackService.LocalBinder) {
@@ -337,7 +309,6 @@ public class PlaybackController {
onBufferUpdate(progress);
break;
case PlaybackService.NOTIFICATION_TYPE_RELOAD:
- cancelPositionObserver();
mediaInfoLoaded = false;
queryService();
onReloadNotification(intent.getIntExtra(
@@ -447,7 +418,6 @@ public class PlaybackController {
case PAUSED:
clearStatusMsg();
checkMediaInfoLoaded();
- cancelPositionObserver();
onPositionObserverUpdate();
updatePlayButtonAppearance(playResource, playText);
if (!PlaybackService.isCasting() &&
@@ -463,7 +433,6 @@ public class PlaybackController {
onAwaitingVideoSurface();
setScreenOn(true);
}
- setupPositionObserver();
updatePlayButtonAppearance(pauseResource, pauseText);
break;
case PREPARING:
@@ -581,7 +550,6 @@ public class PlaybackController {
*/
public void onSeekBarStartTrackingTouch(SeekBar seekBar) {
// interrupt position Observer, restart later
- cancelPositionObserver();
}
/**
@@ -590,7 +558,6 @@ public class PlaybackController {
public void onSeekBarStopTrackingTouch(SeekBar seekBar, float prog) {
if (playbackService != null && media != null) {
playbackService.seekTo((int) (prog * media.getDuration()));
- setupPositionObserver();
}
}
@@ -836,19 +803,4 @@ public class PlaybackController {
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
-
- /**
- * Refreshes the current position of the media file that is playing.
- */
- public class MediaPositionObserver implements Runnable {
-
- static final int WAITING_INTERVALL = 1000;
-
- @Override
- public void run() {
- if (playbackService != null && playbackService.getStatus() == PlayerStatus.PLAYING) {
- activity.runOnUiThread(PlaybackController.this::onPositionObserverUpdate);
- }
- }
- }
}
diff --git a/core/src/main/res/drawable/ic_warning_red.xml b/core/src/main/res/drawable/ic_warning_red.xml
new file mode 100644
index 000000000..475a41bbb
--- /dev/null
+++ b/core/src/main/res/drawable/ic_warning_red.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#FF0000"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FF000000" android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
+</vector>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index c7bd2d2a2..adf938d75 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -394,6 +394,8 @@
<string name="pref_automatic_download_sum">Configure the automatic download of episodes.</string>
<string name="pref_autodl_wifi_filter_title">Enable Wi-Fi filter</string>
<string name="pref_autodl_wifi_filter_sum">Allow automatic download only for selected Wi-Fi networks.</string>
+ <string name="autodl_wifi_filter_permission_title">Permission required</string>
+ <string name="autodl_wifi_filter_permission_message">Location permission is required for Wi-Fi filter. Tap to grant the permission.</string>
<string name="pref_automatic_download_on_battery_title">Download when not charging</string>
<string name="pref_automatic_download_on_battery_sum">Allow automatic download when the battery is not charging</string>
<string name="pref_parallel_downloads_title">Parallel Downloads</string>
diff --git a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
index 244fb0254..800222ada 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.core;
import android.content.Context;
+import android.util.Log;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
@@ -15,6 +16,8 @@ import de.danoeh.antennapod.core.util.exception.RxJavaErrorHandlerSetup;
* Apps using the core module of AntennaPod should register implementations of all interfaces here.
*/
public class ClientConfig {
+ private static final String TAG = "ClientConfig";
+
private ClientConfig(){}
/**
@@ -44,7 +47,15 @@ public class ClientConfig {
UserPreferences.init(context);
PlaybackPreferences.init(context);
NetworkUtils.init(context);
- CastManager.init(context);
+ // Don't initialize Cast-related logic unless it is enabled, to avoid the unnecessary
+ // Google Play Service usage.
+ // Down side: when the user decides to enable casting, AntennaPod needs to be restarted
+ // for it to take effect.
+ if (UserPreferences.isCastEnabled()) {
+ CastManager.init(context);
+ } else {
+ Log.v(TAG, "Cast is disabled. All Cast-related initialization will be skipped.");
+ }
SleepTimerPreferences.init(context);
RxJavaErrorHandlerSetup.setupRxJavaErrorHandler();
initialized = true;
diff --git a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
index 5198a76bd..414a7840c 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
@@ -163,6 +163,10 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
return INSTANCE;
}
+ public static boolean isInitialized() {
+ return INSTANCE != null;
+ }
+
/**
* Returns the active {@link RemoteMediaPlayer} instance. Since there are a number of media
* control APIs that this library do not provide a wrapper for, client applications can call
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 7ab1be380..79c71f164 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
@@ -56,11 +56,18 @@ public class PlaybackServiceFlavorHelper {
PlaybackServiceFlavorHelper(Context context, PlaybackService.FlavorHelperCallback callback) {
this.callback = callback;
+ if (!CastManager.isInitialized()) {
+ return;
+ }
mediaRouter = MediaRouter.getInstance(context.getApplicationContext());
setCastConsumer(context);
}
void initializeMediaPlayer(Context context) {
+ if (!CastManager.isInitialized()) {
+ callback.setMediaPlayer(new LocalPSMP(context, callback.getMediaPlayerCallback()));
+ return;
+ }
castManager = CastManager.getInstance();
castManager.addCastConsumer(castConsumer);
boolean isCasting = castManager.isConnected();
@@ -77,10 +84,16 @@ public class PlaybackServiceFlavorHelper {
}
void removeCastConsumer() {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castManager.removeCastConsumer(castConsumer);
}
boolean castDisconnect(boolean castDisconnect) {
+ if (!CastManager.isInitialized()) {
+ return false;
+ }
if (castDisconnect) {
castManager.disconnect();
}
@@ -88,6 +101,9 @@ public class PlaybackServiceFlavorHelper {
}
boolean onMediaPlayerInfo(Context context, int code, @StringRes int resourceId) {
+ if (!CastManager.isInitialized()) {
+ return false;
+ }
switch (code) {
case RemotePSMP.CAST_ERROR:
callback.sendNotificationBroadcast(PlaybackService.NOTIFICATION_TYPE_SHOW_TOAST, resourceId);
@@ -218,6 +234,9 @@ public class PlaybackServiceFlavorHelper {
}
void registerWifiBroadcastReceiver() {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
if (wifiBroadcastReceiver != null) {
return;
}
@@ -243,6 +262,9 @@ public class PlaybackServiceFlavorHelper {
}
void unregisterWifiBroadcastReceiver() {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
if (wifiBroadcastReceiver != null) {
callback.unregisterReceiver(wifiBroadcastReceiver);
wifiBroadcastReceiver = null;
@@ -250,6 +272,9 @@ public class PlaybackServiceFlavorHelper {
}
boolean onSharedPreference(String key) {
+ if (!CastManager.isInitialized()) {
+ return false;
+ }
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
if (!UserPreferences.isCastEnabled()) {
if (castManager.isConnecting() || castManager.isConnected()) {
@@ -263,6 +288,9 @@ public class PlaybackServiceFlavorHelper {
}
void sessionStateAddActionForWear(PlaybackStateCompat.Builder sessionState, String actionName, CharSequence name, int icon) {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
PlaybackStateCompat.CustomAction.Builder actionBuilder =
new PlaybackStateCompat.CustomAction.Builder(actionName, name, icon);
Bundle actionExtras = new Bundle();
@@ -273,6 +301,9 @@ public class PlaybackServiceFlavorHelper {
}
void mediaSessionSetExtraForWear(MediaSessionCompat mediaSession) {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
Bundle sessionExtras = new Bundle();
sessionExtras.putBoolean(MediaControlConstants.EXTRA_RESERVE_SLOT_SKIP_TO_PREVIOUS, true);
sessionExtras.putBoolean(MediaControlConstants.EXTRA_RESERVE_SLOT_SKIP_TO_NEXT, true);