diff options
Diffstat (limited to 'app')
29 files changed, 693 insertions, 181 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(); + } } |