summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorueen <ueli.sarnighausen@online.de>2021-05-14 21:06:04 +0200
committerGitHub <noreply@github.com>2021-05-14 21:06:04 +0200
commit292c9bf15136f76fea8928ed7abb2b55fb316678 (patch)
tree144f3969825b2b910e6bc0640325942bb8ed969e /app
parentfb6bd0cbaa909b6067fedef1e8a108130841f3fe (diff)
downloadAntennaPod-292c9bf15136f76fea8928ed7abb2b55fb316678.zip
New media player screen (#5075)
Co-authored-by: jonasburian <jonas.burian@protonmail.com> Co-authored-by: ByteHamster <info@bytehamster.com>
Diffstat (limited to 'app')
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java96
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java56
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java191
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java165
-rw-r--r--app/src/main/res/drawable-v21/grey_border.xml29
-rw-r--r--app/src/main/res/drawable/grey_border.xml21
-rw-r--r--app/src/main/res/layout/audioplayer_fragment.xml15
-rw-r--r--app/src/main/res/layout/cover_fragment.xml124
-rw-r--r--app/src/main/res/layout/feeditem_fragment.xml4
-rw-r--r--app/src/main/res/layout/item_description_fragment.xml21
-rw-r--r--app/src/main/res/values/attrs.xml12
13 files changed, 581 insertions, 188 deletions
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 6afe27d17..f07ad6ad5 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -20,6 +20,7 @@ import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.Toast;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBarDrawerToggle;
@@ -32,16 +33,23 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.recyclerview.widget.RecyclerView;
+
import com.bumptech.glide.Glide;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.Validate;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.StorageUtils;
-import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.fragment.AddFeedFragment;
@@ -57,12 +65,8 @@ import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.fragment.TransitionEffect;
import de.danoeh.antennapod.preferences.PreferenceUpgrader;
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.view.LockableBottomSheetBehavior;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.Validate;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
/**
* The activity that is shown when the user launches the app.
@@ -184,6 +188,11 @@ public class MainActivity extends CastEnabledActivity {
if (audioPlayer == null) {
return;
}
+
+ if (slideOffset == 0.0f) { //STATE_COLLAPSED
+ audioPlayer.scrollToPage(AudioPlayerFragment.POS_COVER);
+ }
+
float condensedSlideOffset = Math.max(0.0f, Math.min(0.2f, slideOffset - 0.2f)) / 0.2f;
audioPlayer.getExternalPlayerHolder().setAlpha(1 - condensedSlideOffset);
audioPlayer.getExternalPlayerHolder().setVisibility(
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
index d5807cd90..d54ea566b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
@@ -11,6 +11,7 @@ import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
@@ -20,10 +21,18 @@ import androidx.fragment.app.Fragment;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
+
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
-import com.google.android.material.tabs.TabLayout;
-import com.google.android.material.tabs.TabLayoutMediator;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.List;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity;
@@ -48,20 +57,13 @@ import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
import de.danoeh.antennapod.dialog.SleepTimerDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
-import de.danoeh.antennapod.view.ChapterSeekBar;
import de.danoeh.antennapod.ui.common.PlaybackSpeedIndicatorView;
+import de.danoeh.antennapod.view.ChapterSeekBar;
import de.danoeh.antennapod.view.PlayButton;
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;
-
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.util.List;
/**
* Shows the audio player.
@@ -69,10 +71,9 @@ import java.util.List;
public class AudioPlayerFragment extends Fragment implements
ChapterSeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
public static final String TAG = "AudioPlayerFragment";
- private static final int POS_COVER = 0;
- private static final int POS_SHOWNOTES = 1;
- private static final int POS_CHAPTERS = 2;
- private static final int NUM_CONTENT_FRAGMENTS = 3;
+ public static final int POS_COVER = 0;
+ public static final int POS_DESCRIPTION = 1;
+ private static final int NUM_CONTENT_FRAGMENTS = 2;
private static final float EPSILON = 0.001f;
PlaybackSpeedIndicatorView butPlaybackSpeed;
@@ -95,11 +96,9 @@ public class AudioPlayerFragment extends Fragment implements
private PlaybackController controller;
private Disposable disposable;
private boolean showTimeLeft;
- private boolean hasChapters = false;
private boolean seekedToChapterStart = false;
private int currentChapterIndex = -1;
private int duration;
- private TabLayoutMediator tabLayoutMediator;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@@ -155,36 +154,9 @@ public class AudioPlayerFragment extends Fragment implements
}
});
- TabLayout tabLayout = root.findViewById(R.id.sliding_tabs);
- tabLayoutMediator = new TabLayoutMediator(tabLayout, pager, (tab, position) -> {
- tab.view.setAlpha(1.0f);
- switch (position) {
- case POS_COVER:
- tab.setText(R.string.cover_label);
- break;
- case POS_SHOWNOTES:
- tab.setText(R.string.shownotes_label);
- break;
- case POS_CHAPTERS:
- tab.setText(R.string.chapters_label);
- if (!hasChapters) {
- tab.view.setAlpha(0.5f);
- }
- break;
- default:
- break;
- }
- });
- tabLayoutMediator.attach();
return root;
}
- private void setHasChapters(boolean hasChapters) {
- this.hasChapters = hasChapters;
- tabLayoutMediator.detach();
- tabLayoutMediator.attach();
- }
-
private void setChapterDividers(Playable media) {
if (media == null) {
@@ -193,7 +165,7 @@ public class AudioPlayerFragment extends Fragment implements
float[] dividerPos = null;
- if (hasChapters) {
+ if (media.getChapters() != null) {
List<Chapter> chapters = media.getChapters();
dividerPos = new float[chapters.size()];
@@ -201,7 +173,7 @@ public class AudioPlayerFragment extends Fragment implements
dividerPos[i] = chapters.get(i).getStart() / (float) duration;
}
}
-
+
sbPosition.setDividerPos(dividerPos);
}
@@ -417,16 +389,7 @@ public class AudioPlayerFragment extends Fragment implements
if (controller == null) {
return;
}
-
duration = controller.getDuration();
-
- if (media != null && media.getChapters() != null) {
- setHasChapters(media.getChapters().size() > 0);
- currentChapterIndex = ChapterUtils.getCurrentChapterIndex(media, controller.getPosition());
- } else {
- setHasChapters(false);
- currentChapterIndex = -1;
- }
updatePosition(new PlaybackPositionEvent(controller.getPosition(), duration));
updatePlaybackSpeedButton(media);
setChapterDividers(media);
@@ -472,6 +435,7 @@ public class AudioPlayerFragment extends Fragment implements
int currentPosition = converter.convert(event.getPosition());
int duration = converter.convert(event.getDuration());
int remainingTime = converter.convert(Math.max(event.getDuration() - event.getPosition(), 0));
+ currentChapterIndex = ChapterUtils.getCurrentChapterIndex(controller.getMedia(), currentPosition);
Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition));
if (currentPosition == PlaybackService.INVALID_TIME || duration == PlaybackService.INVALID_TIME) {
Log.w(TAG, "Could not react to position observer update because of invalid time");
@@ -620,14 +584,13 @@ public class AudioPlayerFragment extends Fragment implements
@Override
public Fragment createFragment(int position) {
Log.d(TAG, "getItem(" + position + ")");
+
switch (position) {
case POS_COVER:
return new CoverFragment();
- case POS_SHOWNOTES:
- return new ItemDescriptionFragment();
default:
- case POS_CHAPTERS:
- return new ChaptersFragment();
+ case POS_DESCRIPTION:
+ return new ItemDescriptionFragment();
}
}
@@ -636,4 +599,21 @@ public class AudioPlayerFragment extends Fragment implements
return NUM_CONTENT_FRAGMENTS;
}
}
+
+ public void scrollToPage(int page, boolean smoothScroll) {
+ if (pager == null) {
+ return;
+ }
+
+ pager.setCurrentItem(page, smoothScroll);
+
+ Fragment visibleChild = getChildFragmentManager().findFragmentByTag("f" + POS_DESCRIPTION);
+ if (visibleChild instanceof ItemDescriptionFragment) {
+ ((ItemDescriptionFragment) visibleChild).scrollToTop();
+ }
+ }
+
+ public void scrollToPage(int page) {
+ scrollToPage(page, false);
+ }
}
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 a98901081..6a8d648ad 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -1,48 +1,65 @@
package de.danoeh.antennapod.fragment;
+import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-import de.danoeh.antennapod.model.feed.Chapter;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.util.ChapterUtils;
-import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
-import de.danoeh.antennapod.view.EmptyViewHandler;
+import de.danoeh.antennapod.model.feed.Chapter;
+import de.danoeh.antennapod.model.playback.Playable;
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;
-public class ChaptersFragment extends Fragment {
- private static final String TAG = "ChaptersFragment";
+public class ChaptersFragment extends AppCompatDialogFragment {
+ public static final String TAG = "ChaptersFragment";
private ChaptersListAdapter adapter;
private PlaybackController controller;
private Disposable disposable;
private int focusedChapter = -1;
private Playable media;
private LinearLayoutManager layoutManager;
+ private ProgressBar progressBar;
- @Nullable
+ @NonNull
@Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
- View root = inflater.inflate(R.layout.simple_list_fragment, container, false);
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ return new AlertDialog.Builder(requireContext())
+ .setTitle(getString(R.string.chapters_label))
+ .setView(onCreateView(getLayoutInflater()))
+ .setNegativeButton(getString(R.string.cancel_label), null) //dismisses
+ .create();
+ }
+
+
+ public View onCreateView(@NonNull LayoutInflater inflater) {
+ View root = inflater.inflate(R.layout.simple_list_fragment, null, false);
root.findViewById(R.id.toolbar).setVisibility(View.GONE);
RecyclerView recyclerView = root.findViewById(R.id.recyclerView);
+ progressBar = root.findViewById(R.id.progLoading);
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(),
@@ -58,11 +75,11 @@ public class ChaptersFragment extends Fragment {
});
recyclerView.setAdapter(adapter);
- EmptyViewHandler emptyView = new EmptyViewHandler(getContext());
- emptyView.attachToRecyclerView(recyclerView);
- emptyView.setIcon(R.drawable.ic_bookmark);
- emptyView.setTitle(R.string.no_chapters_head_label);
- emptyView.setMessage(R.string.no_chapters_label);
+ progressBar.setVisibility(View.VISIBLE);
+
+ RelativeLayout.LayoutParams wrapHeight = new RelativeLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ recyclerView.setLayoutParams(wrapHeight);
return root;
}
@@ -136,6 +153,11 @@ public class ChaptersFragment extends Fragment {
if (adapter == null) {
return;
}
+ if (media.getChapters() != null && media.getChapters().size() <= 0) {
+ dismiss();
+ } else {
+ progressBar.setVisibility(View.GONE);
+ }
adapter.setMedia(media);
int positionOfCurrentChapter = getCurrentChapter(media);
updateChapterSelection(positionOfCurrentChapter);
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 5e1cda43c..f60930a35 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -1,7 +1,9 @@
package de.danoeh.antennapod.fragment;
+import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.ColorFilter;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
@@ -10,17 +12,29 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.Space;
import android.widget.TextView;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.graphics.BlendModeColorFilterCompat;
+import androidx.core.graphics.BlendModeCompat;
import androidx.fragment.app.Fragment;
+
import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
-import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.request.RequestOptions;
+
+import org.apache.commons.lang3.StringUtils;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
@@ -28,16 +42,13 @@ import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
-import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.model.feed.Chapter;
+import de.danoeh.antennapod.model.playback.Playable;
import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.apache.commons.lang3.StringUtils;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
/**
* Displays the cover and the title of a FeedItem.
@@ -51,9 +62,16 @@ public class CoverFragment extends Fragment {
private TextView txtvPodcastTitle;
private TextView txtvEpisodeTitle;
private ImageView imgvCover;
+ private LinearLayout openDescription;
+ private Space counterweight;
+ private Space spacer;
+ private ImageButton butPrevChapter;
+ private ImageButton butNextChapter;
+ private LinearLayout episodeDetails;
+ private LinearLayout chapterControl;
private PlaybackController controller;
private Disposable disposable;
- private int displayedChapterIndex = -2;
+ private int displayedChapterIndex = -1;
private Playable media;
@Override
@@ -64,7 +82,30 @@ public class CoverFragment extends Fragment {
txtvPodcastTitle = root.findViewById(R.id.txtvPodcastTitle);
txtvEpisodeTitle = root.findViewById(R.id.txtvEpisodeTitle);
imgvCover = root.findViewById(R.id.imgvCover);
+ episodeDetails = root.findViewById(R.id.episode_details);
+ final ImageView descriptionIcon = root.findViewById(R.id.description_icon);
+ chapterControl = root.findViewById(R.id.chapterButton);
+ butPrevChapter = root.findViewById(R.id.butPrevChapter);
+ butNextChapter = root.findViewById(R.id.butNextChapter);
+
imgvCover.setOnClickListener(v -> onPlayPause());
+ openDescription = root.findViewById(R.id.openDescription);
+ counterweight = root.findViewById(R.id.counterweight);
+ spacer = root.findViewById(R.id.details_spacer);
+ View.OnClickListener scrollToDesc = view ->
+ ((AudioPlayerFragment) requireParentFragment()).scrollToPage(AudioPlayerFragment.POS_DESCRIPTION, true);
+ openDescription.setOnClickListener(scrollToDesc);
+ ColorFilter colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
+ txtvPodcastTitle.getCurrentTextColor(), BlendModeCompat.SRC_IN);
+ butNextChapter.setColorFilter(colorFilter);
+ butPrevChapter.setColorFilter(colorFilter);
+ descriptionIcon.setColorFilter(colorFilter);
+ ChaptersFragment chaptersFragment = new ChaptersFragment();
+ chapterControl.setOnClickListener(v ->
+ chaptersFragment.show(getChildFragmentManager(), ChaptersFragment.TAG));
+ butPrevChapter.setOnClickListener(v -> seekToPrevChapter());
+ butNextChapter.setOnClickListener(v -> seekToNextChapter());
+
return root;
}
@@ -80,6 +121,7 @@ public class CoverFragment extends Fragment {
disposable = Maybe.<Playable>create(emitter -> {
Playable media = controller.getMedia();
if (media != null) {
+ ChapterUtils.loadChapters(media, getContext());
emitter.onSuccess(media);
} else {
emitter.onComplete();
@@ -100,8 +142,73 @@ public class CoverFragment extends Fragment {
+ "\u00A0"
+ StringUtils.replace(StringUtils.stripToEmpty(pubDateStr), " ", "\u00A0"));
txtvEpisodeTitle.setText(media.getEpisodeTitle());
- displayedChapterIndex = -2; // Force refresh
- displayCoverImage(media.getPosition());
+ displayedChapterIndex = -1;
+ refreshChapterData(ChapterUtils.getCurrentChapterIndex(media, media.getPosition())); //calls displayCoverImage
+ updateChapterControlVisibility();
+ }
+
+ private void updateChapterControlVisibility() {
+ if (media.getChapters() != null) {
+ boolean chapterControlVisible = media.getChapters().size() > 0;
+ int newVisibility = chapterControlVisible ? View.VISIBLE : View.GONE;
+ if (chapterControl.getVisibility() != newVisibility) {
+ chapterControl.setVisibility(newVisibility);
+ ObjectAnimator.ofFloat(chapterControl,
+ "alpha",
+ chapterControlVisible ? 0 : 1,
+ chapterControlVisible ? 1 : 0)
+ .start();
+ }
+ }
+ }
+
+ private void refreshChapterData(int chapterIndex) {
+ if (chapterIndex > -1) {
+ if (media.getPosition() > media.getDuration() || chapterIndex >= media.getChapters().size() - 1) {
+ displayedChapterIndex = media.getChapters().size() - 1;
+ butNextChapter.setVisibility(View.INVISIBLE);
+ } else {
+ displayedChapterIndex = chapterIndex;
+ butNextChapter.setVisibility(View.VISIBLE);
+ }
+ }
+
+ displayCoverImage();
+ }
+
+ private Chapter getCurrentChapter() {
+ if (media == null || media.getChapters() == null || displayedChapterIndex == -1) {
+ return null;
+ }
+ return media.getChapters().get(displayedChapterIndex);
+ }
+
+ private void seekToPrevChapter() {
+ Chapter curr = getCurrentChapter();
+
+ if (controller == null || curr == null || displayedChapterIndex == -1) {
+ return;
+ }
+
+ if (displayedChapterIndex < 1) {
+ controller.seekTo(0);
+ } else if ((controller.getPosition() - 10000 * controller.getCurrentPlaybackSpeedMultiplier())
+ < curr.getStart()) {
+ refreshChapterData(displayedChapterIndex - 1);
+ controller.seekToChapter(media.getChapters().get(displayedChapterIndex));
+ } else {
+ controller.seekToChapter(curr);
+ }
+ }
+
+ private void seekToNextChapter() {
+ if (controller == null || media == null || media.getChapters() == null
+ || displayedChapterIndex == -1 || displayedChapterIndex + 1 >= media.getChapters().size()) {
+ return;
+ }
+
+ refreshChapterData(displayedChapterIndex + 1);
+ controller.seekToChapter(media.getChapters().get(displayedChapterIndex));
}
@Override
@@ -139,22 +246,18 @@ public class CoverFragment extends Fragment {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(PlaybackPositionEvent event) {
- if (media == null) {
- return;
+ int newChapterIndex = ChapterUtils.getCurrentChapterIndex(media, event.getPosition());
+ if (newChapterIndex > -1 && newChapterIndex != displayedChapterIndex) {
+ refreshChapterData(newChapterIndex);
}
- displayCoverImage(event.getPosition());
}
- private void displayCoverImage(int position) {
- int chapter = ChapterUtils.getCurrentChapterIndex(media, position);
- if (chapter != displayedChapterIndex) {
- displayedChapterIndex = chapter;
-
- RequestOptions options = new RequestOptions()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .dontAnimate()
- .transforms(new FitCenter(),
- new RoundedCorners((int) (16 * getResources().getDisplayMetrics().density)));
+ private void displayCoverImage() {
+ RequestOptions options = new RequestOptions()
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .dontAnimate()
+ .transforms(new FitCenter(),
+ new RoundedCorners((int) (16 * getResources().getDisplayMetrics().density)));
RequestBuilder<Drawable> cover = Glide.with(this)
.load(media.getImageLocation())
@@ -163,16 +266,16 @@ public class CoverFragment extends Fragment {
.apply(options))
.apply(options);
- if (chapter == -1 || TextUtils.isEmpty(media.getChapters().get(chapter).getImageUrl())) {
- cover.into(imgvCover);
- } else {
- Glide.with(this)
- .load(EmbeddedChapterImage.getModelFor(media, chapter))
- .apply(options)
- .thumbnail(cover)
- .error(cover)
- .into(imgvCover);
- }
+ if (displayedChapterIndex == -1 || media == null || media.getChapters() == null
+ || TextUtils.isEmpty(media.getChapters().get(displayedChapterIndex).getImageUrl())) {
+ cover.into(imgvCover);
+ } else {
+ Glide.with(this)
+ .load(EmbeddedChapterImage.getModelFor(media, displayedChapterIndex))
+ .apply(options)
+ .thumbnail(cover)
+ .error(cover)
+ .into(imgvCover);
}
}
@@ -196,6 +299,10 @@ public class CoverFragment extends Fragment {
LinearLayout.LayoutParams textParams = (LinearLayout.LayoutParams) textContainer.getLayoutParams();
double ratio = (float) newConfig.screenHeightDp / (float) newConfig.screenWidthDp;
+ boolean spacerVisible = true;
+ ViewGroup detailsParent = (ViewGroup) getView();
+ int detailsWidth = ViewGroup.LayoutParams.MATCH_PARENT;
+
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
double percentageWidth = 0.8;
if (ratio <= SIXTEEN_BY_NINE) {
@@ -217,6 +324,26 @@ public class CoverFragment extends Fragment {
textParams.weight = 1;
imgvCover.setLayoutParams(params);
}
+
+ spacerVisible = false;
+ detailsParent = textContainer;
+ detailsWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+
+ if (displayedChapterIndex == -1) {
+ detailsWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+
+ spacer.setVisibility(spacerVisible ? View.VISIBLE : View.GONE);
+ counterweight.setVisibility(spacerVisible ? View.VISIBLE : View.GONE);
+ LinearLayout.LayoutParams wrapHeight =
+ new LinearLayout.LayoutParams(detailsWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
+ episodeDetails.setLayoutParams(wrapHeight);
+ getView().findViewById(R.id.vertical_divider).setVisibility(spacerVisible ? View.GONE : View.VISIBLE);
+
+ if (episodeDetails.getParent() != detailsParent) {
+ ((ViewGroup) episodeDetails.getParent()).removeView(episodeDetails);
+ detailsParent.addView(episodeDetails);
}
}
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 6c8a967ae..518450f92 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -8,13 +8,15 @@ import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+
import androidx.fragment.app.Fragment;
+
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.view.ShownotesWebView;
import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -143,14 +145,18 @@ public class ItemDescriptionFragment extends Fragment {
&& id.equals(controller.getMedia().getIdentifier().toString())
&& webvDescription != null) {
Log.d(TAG, "Restored scroll Position: " + scrollY);
- webvDescription.scrollTo(webvDescription.getScrollX(),
- scrollY);
+ webvDescription.scrollTo(webvDescription.getScrollX(), scrollY);
return true;
}
}
return false;
}
+ public void scrollToTop() {
+ webvDescription.scrollTo(0, 0);
+ savePreference();
+ }
+
@Override
public void onStart() {
super.onStart();
diff --git a/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java
index 0e1846f1c..ff52df71f 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java
@@ -20,92 +20,181 @@
package de.danoeh.antennapod.view;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewParent;
+import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.viewpager2.widget.ViewPager2;
+import de.danoeh.antennapod.R;
+
import static androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL;
import static androidx.viewpager2.widget.ViewPager2.ORIENTATION_VERTICAL;
+
/**
* Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem
* where pages of ViewPager2 have nested scrollable elements that scroll in the same direction as
* ViewPager2. The scrollable element needs to be the immediate and only child of this host layout.
*
- * <p>This solution has limitations when using multiple levels of nested scrollable elements
+ * This solution has limitations when using multiple levels of nested scrollable elements
* (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2).
- */
-class NestedScrollableHost extends FrameLayout {
+ */ // KhaledAlharthi/NestedScrollableHost.java
+public class NestedScrollableHost extends FrameLayout {
+
+ private ViewPager2 parentViewPager;
+ private int touchSlop = 0;
+ private float initialX = 0f;
+ private float initialY = 0f;
+ private int preferVertical = 1;
+ private int preferHorizontal = 1;
+ private int scrollDirection = 0;
public NestedScrollableHost(@NonNull Context context) {
super(context);
- touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ init(context);
}
public NestedScrollableHost(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
+ init(context);
+ setAttributes(context, attrs);
+ }
+
+ public NestedScrollableHost(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ setAttributes(context, attrs);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ public NestedScrollableHost(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(context);
+ setAttributes(context, attrs);
+ }
+
+ private void setAttributes(@NonNull Context context, @Nullable AttributeSet attrs) {
+ TypedArray a = context.getTheme().obtainStyledAttributes(
+ attrs,
+ R.styleable.NestedScrollableHost,
+ 0, 0);
+
+ try {
+ preferHorizontal = a.getInteger(R.styleable.NestedScrollableHost_preferHorizontal, 1);
+ preferVertical = a.getInteger(R.styleable.NestedScrollableHost_preferVertical, 1);
+ scrollDirection = a.getInteger(R.styleable.NestedScrollableHost_scrollDirection, 0);
+ } finally {
+ a.recycle();
+ }
+
+ }
+
+ private void init(Context context) {
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+
+
+ getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ View v = (View) getParent();
+ while (v != null && !(v instanceof ViewPager2) || isntSameDirection(v)) {
+ v = (View) v.getParent();
+ }
+ parentViewPager = (ViewPager2) v;
+
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ return false;
+ }
+ });
}
- private int touchSlop;
- private float initialX = 0f;
- private float initialY = 0f;
+ private Boolean isntSameDirection(View v) {
+ int orientation = 0;
+ switch (scrollDirection) {
+ default:
+ case 0:
+ return false;
+ case 1:
+ orientation = ORIENTATION_VERTICAL;
+ break;
+ case 2:
+ orientation = ORIENTATION_HORIZONTAL;
+ break;
+ }
+ return ((v instanceof ViewPager2) && ((ViewPager2) v).getOrientation() != orientation);
+ }
+
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ handleInterceptTouchEvent(ev);
+ return super.onInterceptTouchEvent(ev);
+ }
- private ViewPager2 getParentViewPager() {
- View v = (View) getParent();
- while (v != null && !(v instanceof ViewPager2)) {
- v = (View) v.getParent();
+
+ private boolean canChildScroll(int orientation, float delta) {
+ int direction = (int) -delta;
+ View child = getChildAt(0);
+ if (orientation == 0) {
+ return child.canScrollHorizontally(direction);
+ } else if (orientation == 1) {
+ return child.canScrollVertically(direction);
+ } else {
+ throw new IllegalArgumentException();
}
- return v == null ? null : (ViewPager2) v;
}
- public boolean onInterceptTouchEvent(MotionEvent e) {
- ViewPager2 parentViewPager = getParentViewPager();
+ private void handleInterceptTouchEvent(MotionEvent e) {
if (parentViewPager == null) {
- return super.onInterceptTouchEvent(e);
+ return;
}
-
- ViewParent parent = getParent();
int orientation = parentViewPager.getOrientation();
+ boolean preferedDirection = preferHorizontal + preferVertical > 2;
+
+ // Early return if child can't scroll in same direction as parent and theres no prefered scroll direction
+ if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f) && !preferedDirection) {
+ return;
+ }
+
if (e.getAction() == MotionEvent.ACTION_DOWN) {
initialX = e.getX();
initialY = e.getY();
- parent.requestDisallowInterceptTouchEvent(true);
+ getParent().requestDisallowInterceptTouchEvent(true);
} else if (e.getAction() == MotionEvent.ACTION_MOVE) {
- int dx = (int) (e.getX() - initialX);
- int dy = (int) (e.getY() - initialY);
- boolean isVpHorizontal = orientation == ORIENTATION_HORIZONTAL;
+ float dx = e.getX() - initialX;
+ float dy = e.getY() - initialY;
+ boolean isVpHorizontal = orientation == ViewPager2.ORIENTATION_HORIZONTAL;
// assuming ViewPager2 touch-slop is 2x touch-slop of child
- float scaledDx = Math.abs(dx) * (isVpHorizontal ? .5f : 1f);
- float scaledDy = Math.abs(dy) * (isVpHorizontal ? 1f : .5f);
+ float scaledDx = Math.abs(dx) * (isVpHorizontal ? 1f : 0.5f) * preferHorizontal;
+ float scaledDy = Math.abs(dy) * (isVpHorizontal ? 0.5f : 1f) * preferVertical;
if (scaledDx > touchSlop || scaledDy > touchSlop) {
- int value = isVpHorizontal ? dy : dx;
if (isVpHorizontal == (scaledDy > scaledDx)) {
- // Gesture is perpendicular
- orientation = orientation == ORIENTATION_VERTICAL
- ? ORIENTATION_HORIZONTAL : ORIENTATION_VERTICAL;
- value = isVpHorizontal ? dy : dx;
- }
-
- int direction = (int) -Math.copySign(1, value);
- View child = getChildAt(0);
- if (orientation == ORIENTATION_HORIZONTAL) {
- parent.requestDisallowInterceptTouchEvent(child.canScrollHorizontally(direction));
+ // Gesture is perpendicular, allow all parents to intercept
+ getParent().requestDisallowInterceptTouchEvent(preferedDirection);
} else {
- parent.requestDisallowInterceptTouchEvent(child.canScrollVertically(direction));
+ // Gesture is parallel, query child if movement in that direction is possible
+ if (canChildScroll(orientation, isVpHorizontal ? dx : dy)) {
+ // Child can scroll, disallow all parents to intercept
+ getParent().requestDisallowInterceptTouchEvent(true);
+ } else {
+ // Child cannot scroll, allow all parents to intercept
+ getParent().requestDisallowInterceptTouchEvent(false);
+ }
}
}
- }
- return super.onInterceptTouchEvent(e);
+ }
}
}
diff --git a/app/src/main/res/drawable-v21/grey_border.xml b/app/src/main/res/drawable-v21/grey_border.xml
new file mode 100644
index 000000000..beccf9e85
--- /dev/null
+++ b/app/src/main/res/drawable-v21/grey_border.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+android:color="?attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape
+ android:shape="rectangle">
+ <corners
+ android:radius="8dp"
+ android:topRightRadius="8dp"
+ android:bottomRightRadius="8dp"
+ android:bottomLeftRadius="8dp" />
+ <solid android:color="@android:color/white"/>
+ </shape>
+ </item>
+ <item>
+ <shape
+ android:shape="rectangle">
+ <corners
+ android:radius="8dp"
+ android:topRightRadius="8dp"
+ android:bottomRightRadius="8dp"
+ android:bottomLeftRadius="8dp" />
+ <stroke
+ android:width="1dp"
+ android:color="?android:attr/textColorSecondary" />
+ <solid android:color="@android:color/transparent"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/app/src/main/res/drawable/grey_border.xml b/app/src/main/res/drawable/grey_border.xml
new file mode 100644
index 000000000..4362f05b6
--- /dev/null
+++ b/app/src/main/res/drawable/grey_border.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector>
+
+ <item>
+
+ <shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:radius="8dp"
+ android:topRightRadius="8dp"
+ android:bottomRightRadius="8dp"
+ android:bottomLeftRadius="8dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@android:color/darker_gray" />
+ <solid android:color="@android:color/transparent"/>
+ </shape>
+
+ </item>
+
+</selector> \ No newline at end of file
diff --git a/app/src/main/res/layout/audioplayer_fragment.xml b/app/src/main/res/layout/audioplayer_fragment.xml
index c173371d3..f801930f5 100644
--- a/app/src/main/res/layout/audioplayer_fragment.xml
+++ b/app/src/main/res/layout/audioplayer_fragment.xml
@@ -15,16 +15,6 @@
app:navigationIcon="?homeAsUpIndicator"
android:id="@+id/toolbar"/>
- <com.google.android.material.tabs.TabLayout
- android:id="@+id/sliding_tabs"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/toolbar"
- android:background="?android:attr/windowBackground"
- app:tabBackground="?attr/selectableItemBackground"
- app:tabMode="fixed"
- app:tabGravity="fill"/>
-
<FrameLayout
android:id="@+id/playerFragment"
android:layout_width="match_parent"
@@ -39,9 +29,10 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_above="@id/playtime_layout"
- android:layout_below="@id/sliding_tabs"
+ android:layout_below="@id/toolbar"
+ android:layout_marginBottom="12dp"
android:foreground="?android:windowContentOverlay"
- android:layout_marginBottom="12dp"/>
+ android:orientation="vertical" />
<ImageView
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/cover_fragment.xml b/app/src/main/res/layout/cover_fragment.xml
index 0ec46cbcd..2ac9992a3 100644
--- a/app/src/main/res/layout/cover_fragment.xml
+++ b/app/src/main/res/layout/cover_fragment.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:squareImageView="http://schemas.android.com/apk/de.danoeh.antennapod"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -10,14 +11,18 @@
android:padding="8dp"
android:gravity="center">
+ <Space
+ android:id="@+id/counterweight"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
<de.danoeh.antennapod.ui.common.SquareImageView
android:id="@+id/imgvCover"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_gravity="center"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginTop="8dp"
+ android:layout_marginHorizontal="16dp"
android:layout_weight="0"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:importantForAccessibility="no"
@@ -29,9 +34,9 @@
android:id="@+id/cover_fragment_text_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_marginTop="8dp"
- android:layout_marginBottom="8dp">
+ android:layout_marginVertical="8dp"
+ android:gravity="center"
+ android:orientation="vertical">
<TextView
android:id="@+id/txtvPodcastTitle"
@@ -40,9 +45,9 @@
android:ellipsize="none"
android:gravity="center_horizontal"
android:maxLines="2"
- android:textSize="@dimen/text_size_small"
android:textColor="?android:attr/textColorSecondary"
android:textIsSelectable="true"
+ android:textSize="@dimen/text_size_small"
tools:text="Podcast" />
<TextView
@@ -51,11 +56,114 @@
android:layout_height="wrap_content"
android:ellipsize="none"
android:gravity="center_horizontal"
- android:textSize="@dimen/text_size_small"
android:maxLines="2"
android:textColor="?android:attr/textColorPrimary"
android:textIsSelectable="true"
+ android:textSize="@dimen/text_size_small"
tools:text="Episode" />
+
+ <Space
+ android:id="@+id/vertical_divider"
+ android:layout_width="match_parent"
+ android:layout_height="8dp"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+ <Space
+ android:id="@+id/details_spacer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:id="@+id/episode_details"
+ android:layout_width="wrap_content"
+ android:layout_height="36dp"
+ android:layout_weight="0"
+ android:baselineAligned="false"
+ android:orientation="horizontal"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp">
+
+ <LinearLayout
+ android:id="@+id/openDescription"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="8dp"
+ android:background="@drawable/grey_border"
+ android:clickable="true"
+ android:focusable="true"
+ android:gravity="center"
+ android:minWidth="150dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/description_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="36dp"
+ android:contentDescription="@string/shownotes_contentdescription"
+ android:padding="2dp"
+ app:srcCompat="@drawable/ic_info" />
+
+ <TextView
+ android:id="@+id/shownotes_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="none"
+ android:layout_marginLeft="2dp"
+ android:layout_marginStart="2dp"
+ android:gravity="center_horizontal"
+ android:maxLines="2"
+ android:text="@string/shownotes_label"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="16sp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/chapterButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="8dp"
+ android:layout_weight="1"
+ android:background="@drawable/grey_border"
+ android:clickable="true"
+ android:focusable="true"
+ android:gravity="center"
+ android:minWidth="150dp"
+ android:orientation="horizontal"
+ android:visibility="gone">
+
+ <ImageButton
+ android:id="@+id/butPrevChapter"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/prev_chapter"
+ android:scaleType="fitCenter"
+ app:srcCompat="@drawable/ic_chapter_prev" />
+
+ <TextView
+ android:id="@+id/chapters_label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:text="@string/chapters_label"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="@dimen/text_size_navdrawer" />
+
+ <ImageButton
+ android:id="@+id/butNextChapter"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/next_chapter"
+ android:scaleType="fitCenter"
+ app:srcCompat="@drawable/ic_chapter_next" />
+ </LinearLayout>
+
</LinearLayout>
</LinearLayout>
diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml
index 256f45fe7..f070b4fd0 100644
--- a/app/src/main/res/layout/feeditem_fragment.xml
+++ b/app/src/main/res/layout/feeditem_fragment.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/content_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -185,7 +186,8 @@
<de.danoeh.antennapod.view.NestedScrollableHost
android:layout_below="@id/header"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ app:preferVertical="3">
<de.danoeh.antennapod.view.ShownotesWebView
android:id="@+id/webvDescription"
diff --git a/app/src/main/res/layout/item_description_fragment.xml b/app/src/main/res/layout/item_description_fragment.xml
index 3766cf805..469cd4098 100644
--- a/app/src/main/res/layout/item_description_fragment.xml
+++ b/app/src/main/res/layout/item_description_fragment.xml
@@ -1,18 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
-<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<de.danoeh.antennapod.view.NestedScrollableHost xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fillViewport="false">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:fillViewport="false"
+ app:preferVertical="10"
+ android:nestedScrollingEnabled="true">
- <de.danoeh.antennapod.view.NestedScrollableHost
+ <de.danoeh.antennapod.view.ShownotesWebView
+ android:id="@+id/webview"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"/>
- <de.danoeh.antennapod.view.ShownotesWebView
- android:id="@+id/webview"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </de.danoeh.antennapod.view.NestedScrollableHost>
-
-</androidx.core.widget.NestedScrollView> \ No newline at end of file
+</de.danoeh.antennapod.view.NestedScrollableHost> \ No newline at end of file
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 000000000..4875b3f53
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="NestedScrollableHost">
+ <attr name="scrollDirection" format="enum">
+ <enum name="both" value="0"/>
+ <enum name="vertical" value="1"/>
+ <enum name="horizontal" value="2"/>
+ </attr>
+ <attr name="preferHorizontal" format="integer"/>
+ <attr name="preferVertical" format="integer"/>
+ </declare-styleable>
+</resources> \ No newline at end of file