diff options
Diffstat (limited to 'app/src/main/java/de/danoeh/antennapod')
21 files changed, 355 insertions, 159 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java index ecfdf24b0..1bcdada44 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java @@ -1,7 +1,9 @@ package de.danoeh.antennapod.activity; +import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; @@ -46,22 +48,23 @@ public class AboutActivity extends AppCompatActivity { webViewContainer = findViewById(R.id.webViewContainer); webView = findViewById(R.id.webViewAbout); webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); - if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { - webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); - } - webView.setBackgroundColor(Color.TRANSPARENT); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } + webView.setBackgroundColor(Color.TRANSPARENT); webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - if (!url.startsWith("http")) { + if (url.startsWith("http")) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + startActivity(browserIntent); + return true; + } else { url = url.replace("file:///android_asset/", ""); loadAsset(url); return true; } - return false; } }); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java index aaab579a2..86d4ec642 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -278,9 +278,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements @Override protected void onStart() { super.onStart(); - if (controller != null) { - controller.release(); - } controller = newPlaybackController(); controller.init(); loadMediaInfo(); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java index fa5012b74..78cc15b2c 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java @@ -47,7 +47,6 @@ public class VideoplayerActivity extends MediaplayerActivity { */ private boolean videoControlsShowing = true; private boolean videoSurfaceCreated = false; - private boolean playbackStoppedUponExitVideo = false; private boolean destroyingDueToReload = false; private VideoControlsHider videoControlsHider = new VideoControlsHider(this); @@ -78,7 +77,6 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override protected void onResume() { super.onResume(); - playbackStoppedUponExitVideo = false; if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) { playExternalMedia(getIntent(), MediaType.VIDEO); } else if (PlaybackService.isCasting()) { @@ -93,32 +91,12 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override protected void onStop() { - stopPlaybackIfUserPreferencesSpecified(); // MUST be called before super.onStop(), while it still has member variable controller super.onStop(); if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { videoControlsHider.stop(); } } - void stopPlaybackIfUserPreferencesSpecified() { - // to avoid the method being called twice during leaving Videoplayer - // , which will double-pause the media - // (it is usually first called by surfaceHolderCallback.surfaceDestroyed(), - // then VideoplayerActivity.onStop() , but sometimes VideoplayerActivity.onStop() - // will first be invoked.) - if (playbackStoppedUponExitVideo) { - return; - } - playbackStoppedUponExitVideo = true; - - if (controller != null && !destroyingDueToReload - && UserPreferences.getVideoBackgroundBehavior() - != UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) { - Log.v(TAG, "stop video playback per UserPreference"); - controller.notifyVideoSurfaceAbandoned(); - } - } - @Override public void onUserLeaveHint () { if (!PictureInPictureUtil.isInPictureInPictureMode(this) && UserPreferences.getVideoBackgroundBehavior() @@ -297,12 +275,13 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override public void surfaceDestroyed(SurfaceHolder holder) { - Log.d(TAG, "Videosurface was destroyed." ); - Log.v(TAG, " hasController=" + (controller != null) - + " , destroyingDueToReload=" + destroyingDueToReload - + " , videoBackgroundBehavior=" + UserPreferences.getVideoBackgroundBehavior()); + Log.d(TAG, "Videosurface was destroyed"); videoSurfaceCreated = false; - stopPlaybackIfUserPreferencesSpecified(); + if (controller != null && !destroyingDueToReload + && UserPreferences.getVideoBackgroundBehavior() + != UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) { + controller.notifyVideoSurfaceAbandoned(); + } } }; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java index 54ecdae77..79dc1f96e 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java @@ -66,7 +66,7 @@ public class CoverLoader { options = options.error(errorResource); } - RequestBuilder builder = Glide.with(activity) + RequestBuilder<Drawable> builder = Glide.with(activity) .load(uri) .apply(options); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java index f54b9266e..1286d9dc7 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java @@ -1,6 +1,8 @@ package de.danoeh.antennapod.adapter; import android.content.Context; +import android.support.annotation.NonNull; +import android.content.Intent; import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; @@ -28,7 +30,7 @@ import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter; */ public class DefaultActionButtonCallback implements ActionButtonCallback { - private static final String TAG = "DefaultActionBtnCb"; + private static final String TAG = "DefaultActionButtonCallback"; private final Context context; @@ -82,9 +84,13 @@ public class DefaultActionButtonCallback implements ActionButtonCallback { } } else { // media is downloaded if (media.isCurrentlyPlaying()) { + new PlaybackServiceStarter(context, media) + .startWhenPrepared(true) + .shouldStream(false) + .start(); IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE); } else if (media.isCurrentlyPaused()) { - new PlaybackServiceStarter(context, media) // need to start the service in case it's been stopped by system. + new PlaybackServiceStarter(context, media) .startWhenPrepared(true) .shouldStream(false) .start(); diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java index ece184035..24656ed29 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; import android.util.Log; import com.afollestad.materialdialogs.MaterialDialog; @@ -73,7 +74,8 @@ public class RatingDialog { return mPreferences.getBoolean(KEY_RATED, false); } - private static void saveRated() { + @VisibleForTesting + public static void saveRated() { mPreferences .edit() .putBoolean(KEY_RATED, true) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index 20913d414..f2ae47ac4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -51,6 +51,7 @@ import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; +import de.danoeh.antennapod.view.EmptyViewHandler; import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -76,7 +77,7 @@ public class AllEpisodesFragment extends Fragment { RecyclerView recyclerView; AllEpisodesRecycleAdapter listAdapter; private ProgressBar progLoading; - private View emptyView; + EmptyViewHandler emptyView; List<FeedItem> episodes; private List<Downloader> downloaderList; @@ -279,11 +280,11 @@ public class AllEpisodesFragment extends Fragment { return true; // avoids that the position is reset when we need it in the submenu } - FeedItem selectedItem = listAdapter.getSelectedItem(); - if (selectedItem == null) { - Log.i(TAG, "Selected item was null, ignoring selection"); + if (listAdapter == null || listAdapter.getSelectedItem() == null) { + Log.i(TAG, "Selected item or listAdapter was null, ignoring selection"); return super.onContextItemSelected(item); } + FeedItem selectedItem = listAdapter.getSelectedItem(); // Mark as seen contains UI logic specific to All/New/FavoriteSegments, // e.g., Undo with Snackbar, @@ -334,10 +335,10 @@ public class AllEpisodesFragment extends Fragment { onFragmentLoaded(); } - emptyView = (View) root.findViewById(R.id.emptyView); - emptyView.setVisibility(View.GONE); - ((TextView)emptyView.findViewById(R.id.emptyViewTitle)).setText(R.string.no_all_episodes_head_label); - ((TextView)emptyView.findViewById(R.id.emptyViewMessage)).setText(R.string.no_all_episodes_label); + emptyView = new EmptyViewHandler(getContext()); + emptyView.attachToRecyclerView(recyclerView); + emptyView.setTitle(R.string.no_all_episodes_head_label); + emptyView.setMessage(R.string.no_all_episodes_label); return root; } @@ -350,16 +351,16 @@ public class AllEpisodesFragment extends Fragment { new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes()); listAdapter.setHasStableIds(true); recyclerView.setAdapter(listAdapter); + emptyView.updateAdapter(listAdapter); } - emptyView.setVisibility(View.GONE); recyclerView.setVisibility(View.VISIBLE); + listAdapter.notifyDataSetChanged(); } else { listAdapter = null; recyclerView.setVisibility(View.GONE); - emptyView.setVisibility(View.VISIBLE); + emptyView.updateAdapter(listAdapter); } - listAdapter.notifyDataSetChanged(); restoreScrollPosition(); getActivity().supportInvalidateOptionsMenu(); updateShowOnlyEpisodesListViewState(); @@ -431,7 +432,10 @@ public class AllEpisodesFragment extends Fragment { public void onEventMainThread(FeedItemEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); - if (episodes == null || listAdapter == null) { + if (episodes == null) { + return; + } else if (listAdapter == null) { + loadItems(); return; } for (FeedItem item : event.items) { @@ -459,7 +463,11 @@ public class AllEpisodesFragment extends Fragment { if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) { getActivity().supportInvalidateOptionsMenu(); } - if(listAdapter != null && update.mediaIds.length > 0) { + if (listAdapter == null) { + loadItems(); + return; + } + if (update.mediaIds.length > 0) { for(long mediaId : update.mediaIds) { int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId); if(pos >= 0) { @@ -490,7 +498,7 @@ public class AllEpisodesFragment extends Fragment { } if (viewsCreated && !itemsLoaded) { recyclerView.setVisibility(View.GONE); - emptyView.setVisibility(View.GONE); + emptyView.hide(); progLoading.setVisibility(View.VISIBLE); } disposable = Observable.fromCallable(this::loadData) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java index 8f2073ed0..c4b82459a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; import android.support.v4.app.ListFragment; +import android.util.Log; import android.view.View; import android.widget.ListView; @@ -12,12 +13,17 @@ import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.greenrobot.event.EventBus; +import io.reactivex.Maybe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; public class ChaptersFragment extends ListFragment { private static final String TAG = "ChaptersFragment"; private ChaptersListAdapter adapter; private PlaybackController controller; + private Disposable disposable; @Override @@ -42,10 +48,7 @@ public class ChaptersFragment extends ListFragment { controller = new PlaybackController(getActivity(), false) { @Override public boolean loadMediaInfo() { - if (getMedia() == null) { - return false; - } - onMediaChanged(getMedia()); + ChaptersFragment.this.loadMediaInfo(); return true; } @@ -55,11 +58,20 @@ public class ChaptersFragment extends ListFragment { } }; controller.init(); - onMediaChanged(controller.getMedia()); + loadMediaInfo(); EventBus.getDefault().register(this); } @Override + public void onDestroyView() { + super.onDestroyView(); + + if (disposable != null) { + disposable.dispose(); + } + } + + @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); @@ -73,6 +85,24 @@ public class ChaptersFragment extends ListFragment { } } + private void loadMediaInfo() { + if (disposable != null) { + disposable.dispose(); + } + disposable = Maybe.create(emitter -> { + Playable media = controller.getMedia(); + if (media != null) { + emitter.onSuccess(media); + } else { + emitter.onComplete(); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(media -> onMediaChanged((Playable) media), + error -> Log.e(TAG, Log.getStackTraceString(error))); + } + private void onMediaChanged(Playable media) { if (adapter != null) { adapter.setMedia(media); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java index 88d748c5a..7bb9c60e4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java @@ -1,6 +1,7 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; @@ -18,6 +19,10 @@ import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.greenrobot.event.EventBus; +import io.reactivex.Maybe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Displays the cover and the title of a FeedItem. @@ -31,6 +36,7 @@ public class CoverFragment extends Fragment { private TextView txtvEpisodeTitle; private ImageView imgvCover; private PlaybackController controller; + private Disposable disposable; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -44,11 +50,24 @@ public class CoverFragment extends Fragment { } private void loadMediaInfo() { - Playable media = controller.getMedia(); - if (media == null) { - Log.w(TAG, "loadMediaInfo was called while media was null"); - return; + if (disposable != null) { + disposable.dispose(); } + disposable = Maybe.create(emitter -> { + Playable media = controller.getMedia(); + if (media != null) { + emitter.onSuccess(media); + } else { + emitter.onComplete(); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(media -> displayMediaInfo((Playable) media), + error -> Log.e(TAG, Log.getStackTraceString(error))); + } + + private void displayMediaInfo(@NonNull Playable media) { txtvPodcastTitle.setText(media.getFeedTitle()); txtvEpisodeTitle.setText(media.getEpisodeTitle()); Glide.with(this) @@ -73,9 +92,6 @@ public class CoverFragment extends Fragment { controller = new PlaybackController(getActivity(), false) { @Override public boolean loadMediaInfo() { - if (getMedia() == null) { - return false; - } CoverFragment.this.loadMediaInfo(); return true; } @@ -99,4 +115,13 @@ public class CoverFragment extends Fragment { controller.release(); controller = null; } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + if (disposable != null) { + disposable.dispose(); + } + } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java index 2c35bdba4..dca574984 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -143,7 +143,9 @@ public class ExternalPlayerFragment extends Fragment { @Override public void onStart() { super.onStart(); + controller = setupPlaybackController(); controller.init(); + loadMediaInfo(); EventBus.getDefault().register(this); } @@ -152,6 +154,7 @@ public class ExternalPlayerFragment extends Fragment { super.onStop(); if (controller != null) { controller.release(); + controller = null; } EventBus.getDefault().unregister(this); } @@ -212,7 +215,8 @@ public class ExternalPlayerFragment extends Fragment { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(media -> updateUi((Playable) media), - error -> Log.e(TAG, Log.getStackTraceString(error))); + error -> Log.e(TAG, Log.getStackTraceString(error)), + () -> fragmentLayout.setVisibility(View.GONE)); return true; } @@ -233,7 +237,7 @@ public class ExternalPlayerFragment extends Fragment { .into(imgvCover); fragmentLayout.setVisibility(View.VISIBLE); - if (controller.isPlayingVideoLocally()) { + if (controller != null && controller.isPlayingVideoLocally()) { butPlay.setVisibility(View.GONE); } else { butPlay.setVisibility(View.VISIBLE); @@ -248,7 +252,9 @@ public class ExternalPlayerFragment extends Fragment { } private void onPositionObserverUpdate() { - if (controller.getPosition() == PlaybackService.INVALID_TIME + if (controller == null) { + return; + } else if (controller.getPosition() == PlaybackService.INVALID_TIME || controller.getDuration() == PlaybackService.INVALID_TIME) { return; } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java index cda89bbd3..d4a662d43 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java @@ -51,8 +51,8 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = super.onCreateViewHelper(inflater, container, savedInstanceState, R.layout.all_episodes_fragment); - ((TextView)root.findViewById(R.id.emptyViewTitle)).setText(R.string.no_fav_episodes_head_label); - ((TextView)root.findViewById(R.id.emptyViewMessage)).setText(R.string.no_fav_episodes_label); + emptyView.setTitle(R.string.no_fav_episodes_head_label); + emptyView.setMessage(R.string.no_fav_episodes_label); ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java index 27ffb0a70..9cee59531 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -251,7 +251,7 @@ public class ItemDescriptionFragment extends Fragment { SharedPreferences prefs = getActivity().getSharedPreferences(PREF, Activity.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); - if (controller.getMedia() != null && webvDescription != null) { + if (controller != null && controller.getMedia() != null && webvDescription != null) { Log.d(TAG, "Saving scroll position: " + webvDescription.getScrollY()); editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY()); editor.putString(PREF_PLAYABLE_ID, controller.getMedia().getIdentifier() @@ -272,7 +272,7 @@ public class ItemDescriptionFragment extends Fragment { PREF, Activity.MODE_PRIVATE); String id = prefs.getString(PREF_PLAYABLE_ID, ""); int scrollY = prefs.getInt(PREF_SCROLL_Y, -1); - if (scrollY != -1 && controller.getMedia() != null + if (controller != null && scrollY != -1 && controller.getMedia() != null && id.equals(controller.getMedia().getIdentifier().toString()) && webvDescription != null) { Log.d(TAG, "Restored scroll Position: " + scrollY); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java index e114ef405..4b8f6a16a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java @@ -592,7 +592,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { private FeedItem loadInBackground() { FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]); if (feedItem != null) { - Timeline t = new Timeline(getActivity(), feedItem); + Timeline t = new Timeline(getContext(), feedItem); webviewData = t.processShownotes(false); } return feedItem; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java index dfd9ac924..726de8369 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java @@ -6,7 +6,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.graphics.LightingColorFilter; import android.os.Bundle; -import android.support.annotation.Nullable; +import android.support.annotation.NonNull; import android.support.v4.app.ListFragment; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.SearchView; @@ -61,6 +61,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.LongList; +import de.danoeh.antennapod.core.util.Optional; import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil; import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment; import de.danoeh.antennapod.dialog.RenameFeedDialog; @@ -618,7 +619,7 @@ public class ItemlistFragment extends ListFragment { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { - feed = result; + feed = result.orElse(null); itemsLoaded = true; if (viewsCreated) { onFragmentLoaded(); @@ -626,15 +627,15 @@ public class ItemlistFragment extends ListFragment { }, error -> Log.e(TAG, Log.getStackTraceString(error))); } - @Nullable - private Feed loadData() { + @NonNull + private Optional<Feed> loadData() { Feed feed = DBReader.getFeed(feedID); - DBReader.loadAdditionalFeedItemListData(feed.getItems()); - if(feed != null && feed.getItemFilter() != null) { + if (feed != null && feed.getItemFilter() != null) { + DBReader.loadAdditionalFeedItemListData(feed.getItems()); FeedItemFilter filter = feed.getItemFilter(); feed.setItems(filter.filter(feed.getItems())); } - return feed; + return Optional.ofNullable(feed); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java index 486727313..ba4b55123 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java @@ -238,42 +238,19 @@ public class ItunesSearchFragment extends Fragment { progressBar.setVisibility(View.VISIBLE); disposable = Single.create((SingleOnSubscribe<List<Podcast>>) emitter -> { String lang = Locale.getDefault().getLanguage(); - String url = "https://itunes.apple.com/" + lang + "/rss/toppodcasts/limit=25/explicit=true/json"; OkHttpClient client = AntennapodHttpClient.getHttpClient(); - Request.Builder httpReq = new Request.Builder() - .url(url) - .header("User-Agent", ClientConfig.USER_AGENT); - List<Podcast> results = new ArrayList<>(); + String feedString; try { - Response response = client.newCall(httpReq.build()).execute(); - if(!response.isSuccessful()) { - // toplist for language does not exist, fall back to united states - url = "https://itunes.apple.com/us/rss/toppodcasts/limit=25/explicit=true/json"; - httpReq = new Request.Builder() - .url(url) - .header("User-Agent", ClientConfig.USER_AGENT); - response = client.newCall(httpReq.build()).execute(); - } - if(response.isSuccessful()) { - String resultString = response.body().string(); - JSONObject result = new JSONObject(resultString); - JSONObject feed = result.getJSONObject("feed"); - JSONArray entries = feed.getJSONArray("entry"); - - for(int i=0; i < entries.length(); i++) { - JSONObject json = entries.getJSONObject(i); - Podcast podcast = Podcast.fromToplist(json); - results.add(podcast); - } - } - else { - String prefix = getString(R.string.error_msg_prefix); - emitter.onError(new IOException(prefix + response)); + try { + feedString = getTopListFeed(client, lang); + } catch (IOException e) { + feedString = getTopListFeed(client, "us"); } + List<Podcast> podcasts = parseFeed(feedString); + emitter.onSuccess(podcasts); } catch (IOException | JSONException e) { emitter.onError(e); } - emitter.onSuccess(results); }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -291,6 +268,35 @@ public class ItunesSearchFragment extends Fragment { }); } + private String getTopListFeed(OkHttpClient client, String language) throws IOException { + String url = "https://itunes.apple.com/%s/rss/toppodcasts/limit=25/explicit=true/json"; + Request.Builder httpReq = new Request.Builder() + .header("User-Agent", ClientConfig.USER_AGENT) + .url(String.format(url, language)); + + try (Response response = client.newCall(httpReq.build()).execute()) { + if (response.isSuccessful()) { + return response.body().string(); + } + String prefix = getString(R.string.error_msg_prefix); + throw new IOException(prefix + response); + } + } + + private List<Podcast> parseFeed(String jsonString) throws JSONException { + JSONObject result = new JSONObject(jsonString); + JSONObject feed = result.getJSONObject("feed"); + JSONArray entries = feed.getJSONArray("entry"); + + List<Podcast> results = new ArrayList<>(); + for (int i=0; i < entries.length(); i++) { + JSONObject json = entries.getJSONObject(i); + results.add(Podcast.fromToplist(json)); + } + + return results; + } + private void search(String query) { if (disposable != null) { disposable.dispose(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java index c2b61bf75..5751855c7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -46,8 +46,8 @@ public class NewEpisodesFragment extends AllEpisodesFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = super.onCreateViewHelper(inflater, container, savedInstanceState, R.layout.all_episodes_fragment); - ((TextView)root.findViewById(R.id.emptyViewTitle)).setText(R.string.no_new_episodes_head_label); - ((TextView)root.findViewById(R.id.emptyViewMessage)).setText(R.string.no_new_episodes_label); + emptyView.setTitle(R.string.no_new_episodes_head_label); + emptyView.setMessage(R.string.no_new_episodes_label); ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java index 1a2152f51..13580be4f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -52,6 +52,7 @@ import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.QueueSorter; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; +import de.danoeh.antennapod.view.EmptyViewHandler; import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -72,7 +73,7 @@ public class QueueFragment extends Fragment { private TextView infoBar; private RecyclerView recyclerView; private QueueRecyclerAdapter recyclerAdapter; - private View emptyView; + private EmptyViewHandler emptyView; private ProgressBar progLoading; private List<FeedItem> queue; @@ -125,7 +126,10 @@ public class QueueFragment extends Fragment { public void onEventMainThread(QueueEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); - if(queue == null || recyclerAdapter == null) { + if (queue == null) { + return; + } else if (recyclerAdapter == null) { + loadItems(true); return; } switch(event.action) { @@ -160,7 +164,10 @@ public class QueueFragment extends Fragment { public void onEventMainThread(FeedItemEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); - if(queue == null || recyclerAdapter == null) { + if (queue == null) { + return; + } else if (recyclerAdapter == null) { + loadItems(true); return; } for(int i=0, size = event.items.size(); i < size; i++) { @@ -170,6 +177,7 @@ public class QueueFragment extends Fragment { queue.remove(pos); queue.add(pos, item); recyclerAdapter.notifyItemChanged(pos); + refreshInfoBar(); } } } @@ -271,7 +279,9 @@ public class QueueFragment extends Fragment { boolean newLockState = !UserPreferences.isQueueLocked(); UserPreferences.setQueueLocked(newLockState); getActivity().supportInvalidateOptionsMenu(); - recyclerAdapter.setLocked(newLockState); + if (recyclerAdapter != null) { + recyclerAdapter.setLocked(newLockState); + } if (newLockState) { Snackbar.make(getActivity().findViewById(R.id.content), R.string .queue_locked, Snackbar.LENGTH_SHORT).show(); @@ -493,11 +503,11 @@ public class QueueFragment extends Fragment { } ); itemTouchHelper.attachToRecyclerView(recyclerView); - //empty view - emptyView = (View) root.findViewById(R.id.emptyView); - emptyView.setVisibility(View.GONE); - ((TextView)emptyView.findViewById(R.id.emptyViewTitle)).setText(R.string.no_items_header_label); - ((TextView)emptyView.findViewById(R.id.emptyViewMessage)).setText(R.string.no_items_label); + + emptyView = new EmptyViewHandler(getContext()); + emptyView.attachToRecyclerView(recyclerView); + emptyView.setTitle(R.string.no_items_header_label); + emptyView.setMessage(R.string.no_items_label); progLoading = root.findViewById(R.id.progLoading); progLoading.setVisibility(View.VISIBLE); @@ -513,13 +523,13 @@ public class QueueFragment extends Fragment { new DefaultActionButtonCallback(activity), itemTouchHelper); recyclerAdapter.setHasStableIds(true); recyclerView.setAdapter(recyclerAdapter); + emptyView.updateAdapter(recyclerAdapter); } - emptyView.setVisibility(View.GONE); recyclerView.setVisibility(View.VISIBLE); } else { recyclerAdapter = null; recyclerView.setVisibility(View.GONE); - emptyView.setVisibility(View.VISIBLE); + emptyView.updateAdapter(recyclerAdapter); } if (restoreScrollPosition) { @@ -632,7 +642,7 @@ public class QueueFragment extends Fragment { } if (queue == null) { recyclerView.setVisibility(View.GONE); - emptyView.setVisibility(View.GONE); + emptyView.hide(); progLoading.setVisibility(View.VISIBLE); } disposable = Observable.fromCallable(DBReader::getQueue) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java index 5f09be8ce..75da522d1 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -1,11 +1,14 @@ package de.danoeh.antennapod.fragment; +import android.content.Context; import android.content.DialogInterface; +import android.content.SharedPreferences; import android.os.Bundle; import android.support.v4.app.Fragment; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; +import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; @@ -41,6 +44,8 @@ public class SubscriptionFragment extends Fragment { private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE | EventDistributor.UNREAD_ITEMS_UPDATE; + private static final String PREFS = "SubscriptionFragment"; + private static final String PREF_NUM_COLUMNS = "columns"; private GridView subscriptionGridLayout; private DBReader.NavDrawerData navDrawerData; @@ -49,6 +54,7 @@ public class SubscriptionFragment extends Fragment { private int mPosition = -1; private Disposable disposable; + private SharedPreferences prefs; public SubscriptionFragment() { } @@ -57,11 +63,9 @@ public class SubscriptionFragment extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); - - // So, we certainly *don't* have an options menu, - // but unless we say we do, old options menus sometimes - // persist. mfietz thinks this causes the ActionBar to be invalidated setHasOptionsMenu(true); + + prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE); } @Override @@ -69,11 +73,53 @@ public class SubscriptionFragment extends Fragment { Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_subscriptions, container, false); subscriptionGridLayout = root.findViewById(R.id.subscriptions_grid); + subscriptionGridLayout.setNumColumns(prefs.getInt(PREF_NUM_COLUMNS, 3)); registerForContextMenu(subscriptionGridLayout); return root; } @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.subscriptions, menu); + + int columns = prefs.getInt(PREF_NUM_COLUMNS, 3); + menu.findItem(R.id.subscription_num_columns_2).setChecked(columns == 2); + menu.findItem(R.id.subscription_num_columns_3).setChecked(columns == 3); + menu.findItem(R.id.subscription_num_columns_4).setChecked(columns == 4); + menu.findItem(R.id.subscription_num_columns_5).setChecked(columns == 5); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (super.onOptionsItemSelected(item)) { + return true; + } + switch (item.getItemId()) { + case R.id.subscription_num_columns_2: + setColumnNumber(2); + return true; + case R.id.subscription_num_columns_3: + setColumnNumber(3); + return true; + case R.id.subscription_num_columns_4: + setColumnNumber(4); + return true; + case R.id.subscription_num_columns_5: + setColumnNumber(5); + return true; + default: + return false; + } + } + + private void setColumnNumber(int columns) { + subscriptionGridLayout.setNumColumns(columns); + prefs.edit().putInt(PREF_NUM_COLUMNS, columns).apply(); + getActivity().invalidateOptionsMenu(); + } + + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); subscriptionAdapter = new SubscriptionsAdapter((MainActivity)getActivity(), itemAccess); diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java index a157a2cfa..042aa594a 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java @@ -842,7 +842,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc } else if (v > 0 && v < 24) { entries[x] = res.getQuantityString(R.plurals.episode_cleanup_hours_after_listening, v, v); } else { - int numDays = (int)(v / 24); // assume underlying value will be NOT fraction of days, e.g., 36 (hours) + int numDays = v / 24; // assume underlying value will be NOT fraction of days, e.g., 36 (hours) entries[x] = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, numDays, numDays); } } diff --git a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java index e3fd63235..42c11bc8e 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java @@ -1,59 +1,95 @@ package de.danoeh.antennapod.view;
-import android.app.Activity;
import android.content.Context;
+import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import de.danoeh.antennapod.R;
-public class EmptyViewHandler extends View {
- private Activity activity;
- private int title;
- private int message;
+public class EmptyViewHandler {
+ private boolean layoutAdded = false;
+ private RecyclerView recyclerView;
+ private RecyclerView.Adapter adapter;
- public EmptyViewHandler(Context context) {
- super(context);
- this.setActivity((Activity) context);
- }
+ private final View emptyView;
+ private final TextView tvTitle;
+ private final TextView tvMessage;
- public int getTitle() {
- return title;
+ public EmptyViewHandler(Context context) {
+ emptyView = View.inflate(context, R.layout.empty_view_layout, null);
+ tvTitle = emptyView.findViewById(R.id.emptyViewTitle);
+ tvMessage = emptyView.findViewById(R.id.emptyViewMessage);
}
public void setTitle(int title) {
- this.title = title;
- }
-
- public int getMessage() {
- return message;
+ tvTitle.setText(title);
}
public void setMessage(int message) {
- this.message = message;
+ tvMessage.setText(message);
}
- public void attachToListView(ListView listView){
+ public void hide() {
+ emptyView.setVisibility(View.GONE);
+ }
- View emptyView = getActivity().getLayoutInflater().inflate(R.layout.empty_view_layout, null);
+ public void attachToListView(ListView listView) {
+ if (layoutAdded) {
+ throw new IllegalStateException("Can not attach to ListView multiple times");
+ }
+ layoutAdded = true;
((ViewGroup) listView.getParent()).addView(emptyView);
listView.setEmptyView(emptyView);
+ }
- TextView tvTitle = (TextView) emptyView.findViewById(R.id.emptyViewTitle);
- tvTitle.setText(title);
-
- TextView tvMessage = (TextView) emptyView.findViewById(R.id.emptyViewMessage);
- tvMessage.setText(message);
+ public void attachToRecyclerView(RecyclerView recyclerView) {
+ if (layoutAdded) {
+ throw new IllegalStateException("Can not attach to ListView multiple times");
+ }
+ layoutAdded = true;
+ this.recyclerView = recyclerView;
+ ViewGroup parent = ((ViewGroup) recyclerView.getParent());
+ parent.addView(emptyView);
+ updateAdapter(recyclerView.getAdapter());
+ if (parent instanceof RelativeLayout) {
+ RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams)emptyView.getLayoutParams();
+ layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
+ emptyView.setLayoutParams(layoutParams);
+ }
}
- public Activity getActivity() {
- return activity;
+ public void updateAdapter(RecyclerView.Adapter adapter) {
+ if (this.adapter != null) {
+ this.adapter.unregisterAdapterDataObserver(adapterObserver);
+ }
+ this.adapter = adapter;
+ if (adapter != null) {
+ adapter.registerAdapterDataObserver(adapterObserver);
+ }
+ updateVisibility();
}
- public void setActivity(Activity activity) {
- this.activity = activity;
+ private final SimpleAdapterDataObserver adapterObserver = new SimpleAdapterDataObserver() {
+ @Override
+ public void anythingChanged() {
+ updateVisibility();
+ }
+ };
+
+ private void updateVisibility() {
+ boolean empty;
+ if (adapter == null) {
+ empty = true;
+ } else {
+ empty = adapter.getItemCount() == 0;
+ }
+ recyclerView.setVisibility(empty ? View.GONE : View.VISIBLE);
+ emptyView.setVisibility(empty ? View.VISIBLE : View.GONE);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java b/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java new file mode 100644 index 000000000..45c3a35bd --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java @@ -0,0 +1,41 @@ +package de.danoeh.antennapod.view; + +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; + +/** + * AdapterDataObserver that relays all events to the method anythingChanged(). + */ +public abstract class SimpleAdapterDataObserver extends RecyclerView.AdapterDataObserver { + public abstract void anythingChanged(); + + @Override + public void onChanged() { + anythingChanged(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + anythingChanged(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) { + anythingChanged(); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + anythingChanged(); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + anythingChanged(); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + anythingChanged(); + } +} |