summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java6
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java7
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java27
-rw-r--r--app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java4
-rw-r--r--app/src/main/AndroidManifest.xml25
-rw-r--r--app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java56
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java136
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java80
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java66
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java127
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java282
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java101
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SkipPreferenceDialog.java58
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java505
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java66
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java41
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/LockableBottomSheetBehavior.java86
-rw-r--r--app/src/main/res/drawable/shadow.xml7
-rw-r--r--app/src/main/res/layout/audioplayer_fragment.xml (renamed from app/src/main/res/layout/mediaplayerinfo_activity.xml)113
-rw-r--r--app/src/main/res/layout/external_player_fragment.xml157
-rw-r--r--app/src/main/res/layout/item_description_fragment.xml11
-rw-r--r--app/src/main/res/layout/main.xml49
-rw-r--r--app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java187
30 files changed, 1115 insertions, 1145 deletions
diff --git a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
index 225f07dfd..beb6ad705 100644
--- a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
@@ -32,7 +32,9 @@ import java.util.concurrent.TimeoutException;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
@@ -193,4 +195,8 @@ public class EspressoTestUtils {
}
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
+
+ public static Matcher<View> actionBarOverflow() {
+ return allOf(isDisplayed(), withContentDescription("More options"));
+ }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java b/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
index 37d76bb6d..ec0d1fa64 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
@@ -1,6 +1,7 @@
package de.test.antennapod.ui;
import android.content.Intent;
+import android.view.View;
import androidx.test.espresso.Espresso;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.runner.AndroidJUnit4;
@@ -8,6 +9,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.test.antennapod.EspressoTestUtils;
+import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -18,6 +20,7 @@ import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.NthMatcher.first;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.endsWith;
@@ -48,14 +51,14 @@ public class QueueFragmentTest {
@Test
public void testSortEmptyQueue() {
- Espresso.openContextualActionModeOverflowMenu();
+ onView(first(EspressoTestUtils.actionBarOverflow())).perform(click());
onView(withText(R.string.sort)).perform(click());
onView(withText(R.string.random)).perform(click());
}
@Test
public void testKeepEmptyQueueSorted() {
- Espresso.openContextualActionModeOverflowMenu();
+ onView(first(EspressoTestUtils.actionBarOverflow())).perform(click());
onView(withText(R.string.sort)).perform(click());
onView(withText(R.string.keep_sorted)).perform(click());
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java b/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java
index 194d51a3c..5565bf474 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java
@@ -7,12 +7,14 @@ import android.preference.PreferenceManager;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.AudioplayerActivity;
+import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.test.antennapod.EspressoTestUtils;
import de.test.antennapod.IgnoreOnCi;
@@ -34,6 +36,8 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static de.test.antennapod.EspressoTestUtils.waitForView;
+import static de.test.antennapod.NthMatcher.first;
+import static org.hamcrest.Matchers.allOf;
/**
* User interface tests for changing the playback speed.
@@ -43,10 +47,10 @@ import static de.test.antennapod.EspressoTestUtils.waitForView;
public class SpeedChangeTest {
@Rule
- public ActivityTestRule<AudioplayerActivity> activityRule
- = new ActivityTestRule<>(AudioplayerActivity.class, false, false);
+ public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class, false, false);
private UITestUtils uiTestUtils;
private String[] availableSpeeds;
+ private PlaybackController controller;
@Before
public void setUp() throws Exception {
@@ -70,7 +74,10 @@ public class SpeedChangeTest {
UserPreferences.setPlaybackSpeedArray(availableSpeeds);
EspressoTestUtils.tryKillPlaybackService();
- activityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent().putExtra(MainActivity.EXTRA_OPEN_PLAYER, true));
+ controller = new PlaybackController(activityRule.getActivity(), true);
+ controller.init();
+ controller.getMedia(); // To load media
}
@After
@@ -86,21 +93,21 @@ public class SpeedChangeTest {
@Test
public void testChangeSpeedPlaying() {
onView(isRoot()).perform(waitForView(withId(R.id.butPlay), 1000));
- onView(withId(R.id.butPlay)).perform(click());
+ controller.playPause();
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
- -> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PLAYING);
+ -> controller.getStatus() == PlayerStatus.PLAYING);
clickThroughSpeeds();
}
@Test
public void testChangeSpeedPaused() {
onView(isRoot()).perform(waitForView(withId(R.id.butPlay), 1000));
- onView(withId(R.id.butPlay)).perform(click());
+ controller.playPause();
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
- -> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PLAYING);
- onView(withId(R.id.butPlay)).perform(click());
+ -> controller.getStatus() == PlayerStatus.PLAYING);
+ controller.playPause();
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
- -> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PAUSED);
+ -> controller.getStatus() == PlayerStatus.PAUSED);
clickThroughSpeeds();
}
diff --git a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
index aff1d6ea4..98d506f65 100644
--- a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
@@ -2,6 +2,8 @@ package de.danoeh.antennapod.activity;
import androidx.appcompat.app.AppCompatActivity;
+import android.view.Menu;
+
/**
* Activity that allows for showing the MediaRouter button whenever there's a cast device in the
* network.
@@ -9,7 +11,7 @@ import androidx.appcompat.app.AppCompatActivity;
public abstract class CastEnabledActivity extends AppCompatActivity {
public static final String TAG = "CastEnabledActivity";
- public final void requestCastButton(int showAsAction) {
+ public final void requestCastButton(Menu menu) {
// no-op
}
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 62335b5e6..3f0df81f1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -74,31 +74,6 @@
</activity>
<activity
- android:name=".activity.AudioplayerActivity"
- android:launchMode="singleTop">
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="de.danoeh.antennapod.activity.MainActivity"/>
- <intent-filter>
- <action android:name="android.intent.action.VIEW"/>
-
- <category android:name="android.intent.category.DEFAULT"/>
- <category android:name="android.intent.category.BROWSABLE"/>
-
- <data android:scheme="file"/>
- <data android:mimeType="audio/*"/>
- </intent-filter>
- </activity>
-
- <activity
- android:name=".activity.CastplayerActivity"
- android:launchMode="singleTop">
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="de.danoeh.antennapod.activity.MainActivity"/>
- </activity>
-
- <activity
android:name=".activity.DownloadAuthenticationActivity"
android:launchMode="singleInstance"/>
diff --git a/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java b/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java
new file mode 100644
index 000000000..1667006a5
--- /dev/null
+++ b/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java
@@ -0,0 +1,56 @@
+package com.google.android.material.bottomsheet;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.core.view.ViewCompat;
+import androidx.viewpager.widget.ViewPager;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Override {@link #findScrollingChild(View)} to support {@link ViewPager}'s nested scrolling.
+ * In order to override package level method and field.
+ * This class put in the same package path where {@link BottomSheetBehavior} located.
+ * Source: https://medium.com/@hanru.yeh/funny-solution-that-makes-bottomsheetdialog-support-viewpager-with-nestedscrollingchilds-bfdca72235c3
+ */
+public class ViewPagerBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
+
+ public ViewPagerBottomSheetBehavior() {
+ super();
+ }
+
+ public ViewPagerBottomSheetBehavior(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ View findScrollingChild(View view) {
+ if (ViewCompat.isNestedScrollingEnabled(view)) {
+ return view;
+ }
+
+ if (view instanceof ViewPager) {
+ ViewPager viewPager = (ViewPager) view;
+ View currentViewPagerChild = viewPager.getChildAt(viewPager.getCurrentItem());
+ if (currentViewPagerChild != null) {
+ return findScrollingChild(currentViewPagerChild);
+ }
+ } else if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+ for (int i = 0, count = group.getChildCount(); i < count; i++) {
+ View scrollingChild = findScrollingChild(group.getChildAt(i));
+ if (scrollingChild != null) {
+ return scrollingChild;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void updateScrollingChild() {
+ final View scrollingChild = findScrollingChild(viewRef.get());
+ nestedScrollingChildRef = new WeakReference<>(scrollingChild);
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
deleted file mode 100644
index ae8634516..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ /dev/null
@@ -1,136 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.Intent;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import de.danoeh.antennapod.core.feed.MediaType;
-import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
-import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.dialog.VariableSpeedDialog;
-
-import java.text.DecimalFormat;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Activity for playing audio files.
- */
-public class AudioplayerActivity extends MediaplayerInfoActivity {
- private static final String TAG = "AudioPlayerActivity";
- private static final float EPSILON = 0.001f;
-
- private final AtomicBoolean isSetup = new AtomicBoolean(false);
-
- @Override
- protected void onResume() {
- super.onResume();
- if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
- playExternalMedia(getIntent(), MediaType.AUDIO);
- } else if (PlaybackService.isCasting()) {
- Intent intent = PlaybackService.getPlayerActivityIntent(this);
- if (intent.getComponent() != null
- && !intent.getComponent().getClassName().equals(AudioplayerActivity.class.getName())) {
- saveCurrentFragment();
- finish();
- startActivity(intent);
- }
- }
- }
-
- @Override
- protected void onReloadNotification(int notificationCode) {
- if (notificationCode == PlaybackService.EXTRA_CODE_CAST) {
- Log.d(TAG, "ReloadNotification received, switching to Castplayer now");
- saveCurrentFragment();
- finish();
- startActivity(new Intent(this, CastplayerActivity.class));
-
- } else {
- super.onReloadNotification(notificationCode);
- }
- }
-
- @Override
- protected void updatePlaybackSpeedButton() {
- if (butPlaybackSpeed == null) {
- return;
- }
- if (controller == null) {
- butPlaybackSpeed.setVisibility(View.GONE);
- txtvPlaybackSpeed.setVisibility(View.GONE);
- return;
- }
- updatePlaybackSpeedButtonText();
- butPlaybackSpeed.setAlpha(controller.canSetPlaybackSpeed() ? 1.0f : 0.5f);
- butPlaybackSpeed.setVisibility(View.VISIBLE);
- txtvPlaybackSpeed.setVisibility(View.VISIBLE);
- }
-
- @Override
- protected void updatePlaybackSpeedButtonText() {
- if (butPlaybackSpeed == null) {
- return;
- }
- if (controller == null) {
- butPlaybackSpeed.setVisibility(View.GONE);
- txtvPlaybackSpeed.setVisibility(View.GONE);
- return;
- }
- float speed = 1.0f;
- if (controller.canSetPlaybackSpeed()) {
- speed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(controller.getMedia());
- }
- String speedStr = new DecimalFormat("0.00").format(speed);
- txtvPlaybackSpeed.setText(speedStr);
- butPlaybackSpeed.setSpeed(speed);
- }
-
- @Override
- protected void setupGUI() {
- if (isSetup.getAndSet(true)) {
- return;
- }
- super.setupGUI();
- if (butPlaybackSpeed != null) {
- butPlaybackSpeed.setOnClickListener(v -> {
- if (controller == null) {
- return;
- }
- if (controller.canSetPlaybackSpeed()) {
- float[] availableSpeeds = UserPreferences.getPlaybackSpeedArray();
- float currentSpeed = controller.getCurrentPlaybackSpeedMultiplier();
-
- int newSpeedIndex = 0;
- while (newSpeedIndex < availableSpeeds.length
- && availableSpeeds[newSpeedIndex] < currentSpeed + EPSILON) {
- newSpeedIndex++;
- }
-
- float newSpeed;
- if (availableSpeeds.length == 0) {
- newSpeed = 1.0f;
- } else if (newSpeedIndex == availableSpeeds.length) {
- newSpeed = availableSpeeds[0];
- } else {
- newSpeed = availableSpeeds[newSpeedIndex];
- }
-
- PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(newSpeed);
- UserPreferences.setPlaybackSpeed(newSpeed);
- controller.setPlaybackSpeed(newSpeed);
- onPositionObserverUpdate();
- } else {
- VariableSpeedDialog.showGetPluginDialog(this);
- }
- });
- butPlaybackSpeed.setOnLongClickListener(v -> {
- VariableSpeedDialog.showDialog(this);
- return true;
- });
- butPlaybackSpeed.setVisibility(View.VISIBLE);
- txtvPlaybackSpeed.setVisibility(View.VISIBLE);
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
deleted file mode 100644
index bbab235c8..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-
-/**
- * Activity for controlling the remote playback on a Cast device.
- */
-public class CastplayerActivity extends MediaplayerInfoActivity {
- private static final String TAG = "CastPlayerActivity";
-
- private final AtomicBoolean isSetup = new AtomicBoolean(false);
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (!PlaybackService.isCasting()) {
- Intent intent = PlaybackService.getPlayerActivityIntent(this);
- if (!intent.getComponent().getClassName().equals(CastplayerActivity.class.getName())) {
- finish();
- startActivity(intent);
- }
- }
- }
-
- @Override
- protected void onReloadNotification(int notificationCode) {
- if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO) {
- Log.d(TAG, "ReloadNotification received, switching to Audioplayer now");
- saveCurrentFragment();
- finish();
- startActivity(new Intent(this, AudioplayerActivity.class));
- } else {
- super.onReloadNotification(notificationCode);
- }
- }
-
- @Override
- protected void setupGUI() {
- if(isSetup.getAndSet(true)) {
- return;
- }
- super.setupGUI();
- if (butPlaybackSpeed != null) {
- butPlaybackSpeed.setVisibility(View.GONE);
- txtvPlaybackSpeed.setVisibility(View.GONE);
- }
- }
-
- @Override
- protected void onResume() {
- if (!PlaybackService.isCasting()) {
- Intent intent = PlaybackService.getPlayerActivityIntent(this);
- if (!intent.getComponent().getClassName().equals(CastplayerActivity.class.getName())) {
- saveCurrentFragment();
- finish();
- startActivity(intent);
- }
- }
- super.onResume();
- }
-
- @Override
- protected void onBufferStart() {
- //sbPosition.setIndeterminate(true);
- sbPosition.setEnabled(false);
- }
-
- @Override
- protected void onBufferEnd() {
- //sbPosition.setIndeterminate(false);
- sbPosition.setEnabled(true);
- }
-}
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 872174403..403198bea 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -23,6 +23,7 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.bumptech.glide.Glide;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.MessageEvent;
@@ -31,9 +32,9 @@ import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.fragment.AddFeedFragment;
+import de.danoeh.antennapod.fragment.AudioPlayerFragment;
import de.danoeh.antennapod.fragment.DownloadsFragment;
import de.danoeh.antennapod.fragment.EpisodesFragment;
-import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
import de.danoeh.antennapod.fragment.NavDrawerFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
@@ -41,6 +42,7 @@ import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.fragment.TransitionEffect;
import de.danoeh.antennapod.preferences.PreferenceUpgrader;
+import de.danoeh.antennapod.view.LockableBottomSheetBehavior;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
import org.greenrobot.eventbus.EventBus;
@@ -61,12 +63,14 @@ public class MainActivity extends CastEnabledActivity {
public static final String EXTRA_FRAGMENT_TAG = "fragment_tag";
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
public static final String EXTRA_FEED_ID = "fragment_feed_id";
+ public static final String EXTRA_OPEN_PLAYER = "open_player";
private static final String SAVE_BACKSTACK_COUNT = "backstackCount";
private DrawerLayout drawerLayout;
private View navDrawer;
private ActionBarDrawerToggle drawerToggle;
+ private LockableBottomSheetBehavior sheetBehavior;
private long lastBackButtonPressTime = 0;
@NonNull
@@ -111,15 +115,34 @@ public class MainActivity extends CastEnabledActivity {
}
}
}
- ExternalPlayerFragment externalPlayerFragment = new ExternalPlayerFragment();
- transaction.replace(R.id.playerFragment, externalPlayerFragment, ExternalPlayerFragment.TAG);
NavDrawerFragment navDrawerFragment = new NavDrawerFragment();
transaction.replace(R.id.navDrawerFragment, navDrawerFragment, NavDrawerFragment.TAG);
-
+ AudioPlayerFragment audioPlayerFragment = new AudioPlayerFragment();
+ transaction.replace(R.id.audioplayerFragment, audioPlayerFragment, AudioPlayerFragment.TAG);
transaction.commit();
checkFirstLaunch();
PreferenceUpgrader.checkUpgrades(this);
+ View bottomSheet = findViewById(R.id.audioplayerFragment);
+ sheetBehavior = (LockableBottomSheetBehavior) BottomSheetBehavior.from(bottomSheet);
+ sheetBehavior.setPeekHeight((int) getResources().getDimension(R.dimen.external_player_height));
+ sheetBehavior.setHideable(false);
+ sheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
+ @Override
+ public void onStateChanged(@NonNull View view, int state) {
+
+ }
+
+ @Override
+ public void onSlide(@NonNull View view, float slideOffset) {
+ AudioPlayerFragment audioPlayer =
+ (AudioPlayerFragment) getSupportFragmentManager().findFragmentByTag(AudioPlayerFragment.TAG);
+ float condensedSlideOffset = Math.max(0.0f, Math.min(0.1f, slideOffset - 0.5f)) / 0.1f;
+ audioPlayer.getExternalPlayerHolder().setAlpha(1 - condensedSlideOffset);
+ audioPlayer.getExternalPlayerHolder().setVisibility(
+ condensedSlideOffset > 0.99f ? View.GONE : View.VISIBLE);
+ }
+ });
}
@Override
@@ -151,6 +174,10 @@ public class MainActivity extends CastEnabledActivity {
return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer);
}
+ public LockableBottomSheetBehavior getBottomSheet() {
+ return sheetBehavior;
+ }
+
public void loadFragment(String tag, Bundle args) {
Log.d(TAG, "loadFragment(tag: " + tag + ", args: " + args + ")");
Fragment fragment;
@@ -302,29 +329,6 @@ public class MainActivity extends CastEnabledActivity {
}
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
- boolean retVal = super.onCreateOptionsMenu(menu);
- if (Flavors.FLAVOR == Flavors.PLAY) {
- switch (NavDrawerFragment.getLastNavFragment(this)) {
- case QueueFragment.TAG:
- case EpisodesFragment.TAG:
- requestCastButton(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- return retVal;
- case DownloadsFragment.TAG:
- case PlaybackHistoryFragment.TAG:
- case AddFeedFragment.TAG:
- case SubscriptionFragment.TAG:
- return retVal;
- default:
- requestCastButton(MenuItem.SHOW_AS_ACTION_NEVER);
- return retVal;
- }
- } else {
- return retVal;
- }
- }
-
- @Override
public boolean onOptionsItemSelected(MenuItem item) {
if (drawerToggle.onOptionsItemSelected(item)) {
return true;
@@ -342,6 +346,8 @@ public class MainActivity extends CastEnabledActivity {
public void onBackPressed() {
if (isDrawerOpen()) {
drawerLayout.closeDrawer(navDrawer);
+ } else if (sheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
+ sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
super.onBackPressed();
} else {
@@ -400,9 +406,11 @@ public class MainActivity extends CastEnabledActivity {
} else if (feedId > 0) {
loadFeedFragmentById(feedId, args);
}
- // to avoid handling the intent twice when the configuration changes
- setIntent(new Intent(MainActivity.this, MainActivity.class));
+ } else if (intent.hasExtra(EXTRA_OPEN_PLAYER)) {
+ sheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
+ // to avoid handling the intent twice when the configuration changes
+ setIntent(new Intent(MainActivity.this, MainActivity.class));
}
@Override
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 698d7e295..55aadc60b 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -2,7 +2,6 @@ package de.danoeh.antennapod.activity;
import android.Manifest;
import android.annotation.TargetApi;
-import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
@@ -52,6 +51,7 @@ import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
+import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
import de.danoeh.antennapod.dialog.SleepTimerDialog;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -141,16 +141,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
@Override
- public void postStatusMsg(int msg, boolean showToast) {
- MediaplayerActivity.this.postStatusMsg(msg, showToast);
- }
-
- @Override
- public void clearStatusMsg() {
- MediaplayerActivity.this.clearStatusMsg();
- }
-
- @Override
public boolean loadMediaInfo() {
return MediaplayerActivity.this.loadMediaInfo();
}
@@ -198,13 +188,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
onPositionObserverUpdate();
}
- private static TextView getTxtvFFFromActivity(MediaplayerActivity activity) {
- return activity.txtvFF;
- }
- private static TextView getTxtvRevFromActivity(MediaplayerActivity activity) {
- return activity.txtvRev;
- }
-
private void onSetSpeedAbilityChanged() {
Log.d(TAG, "onSetSpeedAbilityChanged()");
updatePlaybackSpeedButton();
@@ -258,12 +241,16 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
* Should be used to inform the user that the PlaybackService is currently
* buffering.
*/
- protected abstract void onBufferStart();
+ protected void onBufferStart() {
+
+ }
/**
* Should be used to hide the view that was showing the 'buffering'-message.
*/
- protected abstract void onBufferEnd();
+ protected void onBufferEnd() {
+
+ }
private void onBufferUpdate(float progress) {
if (sbPosition != null) {
@@ -311,9 +298,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- if (Flavors.FLAVOR == Flavors.PLAY) {
- requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
- }
+ requestCastButton(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mediaplayer, menu);
return true;
@@ -478,10 +463,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
*/
protected abstract void onAwaitingVideoSurface();
- protected abstract void postStatusMsg(int resId, boolean showToast);
-
- protected abstract void clearStatusMsg();
-
void onPositionObserverUpdate() {
if (controller == null || txtvPosition == null || txtvLength == null) {
return;
@@ -543,92 +524,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
// Only meaningful on AudioplayerActivity, where it is overridden.
}
- /**
- * Abstract directions to skip forward or back (rewind) and encapsulates behavior to get or set preference (including update of UI on the skip buttons).
- */
- public enum SkipDirection {
- SKIP_FORWARD(
- UserPreferences::getFastForwardSecs,
- MediaplayerActivity::getTxtvFFFromActivity,
- UserPreferences::setFastForwardSecs,
- R.string.pref_fast_forward),
- SKIP_REWIND(UserPreferences::getRewindSecs,
- MediaplayerActivity::getTxtvRevFromActivity,
- UserPreferences::setRewindSecs,
- R.string.pref_rewind);
-
- private final Supplier<Integer> getPrefSecsFn;
- private final Function<MediaplayerActivity, TextView> getTextViewFn;
- private final Consumer<Integer> setPrefSecsFn;
- private final int titleResourceID;
-
- /**
- * Constructor for skip direction enum. Stores references to utility functions and resource
- * id's that vary dependending on the direction.
- *
- * @param getPrefSecsFn Handle to function that retrieves current seconds of the skip delta
- * @param getTextViewFn Handle to function that gets the TextView which displays the current skip delta value
- * @param setPrefSecsFn Handle to function that sets the preference (setting) for the skip delta value (and optionally updates the button label with the current values)
- * @param titleResourceID ID of the resource string with the title for a view
- */
- SkipDirection(Supplier<Integer> getPrefSecsFn, Function<MediaplayerActivity, TextView> getTextViewFn, Consumer<Integer> setPrefSecsFn, int titleResourceID) {
- this.getPrefSecsFn = getPrefSecsFn;
- this.getTextViewFn = getTextViewFn;
- this.setPrefSecsFn = setPrefSecsFn;
- this.titleResourceID = titleResourceID;
- }
-
-
- public int getPrefSkipSeconds() {
- return(getPrefSecsFn.get());
- }
-
- /**
- * Updates preferences for a forward or backward skip depending on the direction of the instance, optionally updating the UI.
- *
- * @param seconds Number of seconds to set the preference associated with the direction of the instance.
- * @param activity MediaplyerActivity that contains textview to update the display of the skip delta setting (or null if nothing to update)
- */
- public void setPrefSkipSeconds(int seconds, @Nullable Activity activity) {
- setPrefSecsFn.accept(seconds);
-
- if (activity != null && activity instanceof MediaplayerActivity) {
- TextView tv = getTextViewFn.apply((MediaplayerActivity)activity);
- if (tv != null) tv.setText(String.valueOf(seconds));
- }
- }
- public int getTitleResourceID() {
- return titleResourceID;
- }
- }
-
- public static void showSkipPreference(Activity activity, SkipDirection direction) {
- int checked = 0;
- int skipSecs = direction.getPrefSkipSeconds();
- final int[] values = activity.getResources().getIntArray(R.array.seek_delta_values);
- final String[] choices = new String[values.length];
- for (int i = 0; i < values.length; i++) {
- if (skipSecs == values[i]) {
- checked = i;
- }
- choices[i] = String.valueOf(values[i]) + " " + activity.getString(R.string.time_seconds);
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setTitle(direction.getTitleResourceID());
- builder.setSingleChoiceItems(choices, checked, null);
- builder.setNegativeButton(R.string.cancel_label, null);
- builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
- int choice = ((AlertDialog)dialog).getListView().getCheckedItemPosition();
- if (choice < 0 || choice >= values.length) {
- System.err.printf("Choice in showSkipPreference is out of bounds %d", choice);
- } else {
- direction.setPrefSkipSeconds(values[choice], activity);
- }
- });
- builder.create().show();
- }
-
void setupGUI() {
setContentView(getContentViewResourceId());
sbPosition = findViewById(R.id.sbPosition);
@@ -688,7 +583,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
if (butRev != null) {
butRev.setOnClickListener(v -> onRewind());
butRev.setOnLongClickListener(v -> {
- showSkipPreference(MediaplayerActivity.this, SkipDirection.SKIP_REWIND);
+ SkipPreferenceDialog.showSkipPreference(MediaplayerActivity.this,
+ SkipPreferenceDialog.SkipDirection.SKIP_REWIND, txtvRev);
return true;
});
}
@@ -698,7 +594,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
if (butFF != null) {
butFF.setOnClickListener(v -> onFastForward());
butFF.setOnLongClickListener(v -> {
- showSkipPreference(MediaplayerActivity.this, SkipDirection.SKIP_FORWARD);
+ SkipPreferenceDialog.showSkipPreference(MediaplayerActivity.this,
+ SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF);
return false;
});
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
deleted file mode 100644
index 1a7631813..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
+++ /dev/null
@@ -1,282 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.Toast;
-import androidx.appcompat.app.ActionBarDrawerToggle;
-import androidx.appcompat.widget.Toolbar;
-import androidx.drawerlayout.widget.DrawerLayout;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentStatePagerAdapter;
-import androidx.viewpager.widget.ViewPager;
-import com.google.android.material.snackbar.Snackbar;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.MessageEvent;
-import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.util.playback.PlaybackController;
-import de.danoeh.antennapod.fragment.ChaptersFragment;
-import de.danoeh.antennapod.fragment.CoverFragment;
-import de.danoeh.antennapod.fragment.ItemDescriptionFragment;
-import de.danoeh.antennapod.fragment.NavDrawerFragment;
-import de.danoeh.antennapod.view.PagerIndicatorView;
-import de.danoeh.antennapod.view.PlaybackSpeedIndicatorView;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.util.List;
-
-/**
- * Activity for playing files that do not require a video surface.
- */
-public abstract class MediaplayerInfoActivity extends MediaplayerActivity {
-
- private static final String TAG = "MediaplayerInfoActivity";
-
- private static final int POS_COVER = 0;
- private static final int POS_DESCR = 1;
- private static final int POS_CHAPTERS = 2;
- private static final int NUM_CONTENT_FRAGMENTS = 3;
-
- private static final String PREFS = "AudioPlayerActivityPreferences";
- private static final String PREF_KEY_SELECTED_FRAGMENT_POSITION = "selectedFragmentPosition";
-
- PlaybackSpeedIndicatorView butPlaybackSpeed;
- TextView txtvPlaybackSpeed;
- private DrawerLayout drawerLayout;
- private View navDrawer;
- private ActionBarDrawerToggle drawerToggle;
- private ViewPager pager;
- private PagerIndicatorView pageIndicator;
- private MediaplayerInfoPagerAdapter pagerAdapter;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- supportPostponeEnterTransition();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- Log.d(TAG, "onStop()");
- saveCurrentFragment();
- }
-
- @Override
- public void onDestroy() {
- Log.d(TAG, "onDestroy()");
- super.onDestroy();
- // don't risk creating memory leaks
- drawerLayout = null;
- navDrawer = null;
- drawerToggle = null;
- pager = null;
- pagerAdapter = null;
- }
-
- @Override
- protected void chooseTheme() {
- setTheme(UserPreferences.getNoTitleTheme());
- }
-
- void saveCurrentFragment() {
- if (pager == null) {
- return;
- }
- Log.d(TAG, "Saving preferences");
- SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
- prefs.edit()
- .putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, pager.getCurrentItem())
- .apply();
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (drawerToggle != null) {
- drawerToggle.onConfigurationChanged(newConfig);
- }
- }
-
- private void loadLastFragment() {
- Log.d(TAG, "Restoring instance state");
- SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
- int lastPosition = prefs.getInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, -1);
- pager.setCurrentItem(lastPosition);
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- setIntent(intent);
- }
-
- @Override
- protected void onAwaitingVideoSurface() {
- Log.d(TAG, "onAwaitingVideoSurface was called in audio player -> switching to video player");
- startActivity(new Intent(this, VideoplayerActivity.class));
- }
-
- @Override
- protected void postStatusMsg(int resId, boolean showToast) {
- if (resId == R.string.player_preparing_msg
- || resId == R.string.player_seeking_msg
- || resId == R.string.player_buffering_msg) {
- // TODO Show progress bar here
- }
- if (showToast) {
- Toast.makeText(this, resId, Toast.LENGTH_SHORT).show();
- }
- }
-
- @Override
- protected void clearStatusMsg() {
- // TODO Hide progress bar here
- }
-
-
- @Override
- protected void setupGUI() {
- super.setupGUI();
- Toolbar toolbar = findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setTitle("");
- drawerLayout = findViewById(R.id.drawer_layout);
- navDrawer = findViewById(R.id.navDrawerFragment);
- butPlaybackSpeed = findViewById(R.id.butPlaybackSpeed);
- txtvPlaybackSpeed = findViewById(R.id.txtvPlaybackSpeed);
-
- drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close);
- drawerToggle.setDrawerIndicatorEnabled(false);
- drawerLayout.addDrawerListener(drawerToggle);
- drawerToggle.syncState();
-
- getSupportFragmentManager().beginTransaction()
- .replace(R.id.navDrawerFragment, new NavDrawerFragment(), NavDrawerFragment.TAG)
- .commit();
-
- pager = findViewById(R.id.pager);
- pager.setOffscreenPageLimit(3);
- pagerAdapter = new MediaplayerInfoPagerAdapter(getSupportFragmentManager());
- pager.setAdapter(pagerAdapter);
- pageIndicator = findViewById(R.id.page_indicator);
- pageIndicator.setViewPager(pager);
- pageIndicator.setOnClickListener(v
- -> pager.setCurrentItem((pager.getCurrentItem() + 1) % pager.getChildCount()));
- loadLastFragment();
- pager.onSaveInstanceState();
-
- pager.post(this::supportStartPostponedEnterTransition);
- }
-
- @Override
- boolean loadMediaInfo() {
- if (controller != null && controller.getMedia() != null) {
- List<Chapter> chapters = controller.getMedia().getChapters();
- boolean hasChapters = chapters != null && !chapters.isEmpty();
- pageIndicator.setDisabledPage(hasChapters ? -1 : 2);
- }
- return super.loadMediaInfo();
- }
-
- @Override
- protected void onReloadNotification(int notificationCode) {
- if (notificationCode == PlaybackService.EXTRA_CODE_VIDEO) {
- Log.d(TAG, "ReloadNotification received, switching to Videoplayer now");
- finish();
- startActivity(new Intent(this, VideoplayerActivity.class));
-
- }
- }
-
- @Override
- protected void onBufferStart() {
- postStatusMsg(R.string.player_buffering_msg, false);
- }
-
- @Override
- protected void onBufferEnd() {
- clearStatusMsg();
- }
-
- public PlaybackController getPlaybackController() {
- return controller;
- }
-
- public boolean isDrawerOpen() {
- return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer);
- }
-
- @Override
- protected int getContentViewResourceId() {
- return R.layout.mediaplayerinfo_activity;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- return (drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) || super.onOptionsItemSelected(item);
- }
-
- @Override
- public void onBackPressed() {
- if (isDrawerOpen()) {
- drawerLayout.closeDrawer(navDrawer);
- } else if (pager == null || pager.getCurrentItem() == 0) {
- // If the user is currently looking at the first step, allow the system to handle the
- // Back button. This calls finish() on this activity and pops the back stack.
- super.onBackPressed();
- } else {
- // Otherwise, select the previous step.
- pager.setCurrentItem(pager.getCurrentItem() - 1);
- }
- }
-
- @Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(MessageEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
- View parentLayout = findViewById(R.id.drawer_layout);
- Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT);
- if (event.action != null) {
- snackbar.setAction(getString(R.string.undo), v -> event.action.run());
- }
- snackbar.show();
- }
-
- private static class MediaplayerInfoPagerAdapter extends FragmentStatePagerAdapter {
- private static final String TAG = "MPInfoPagerAdapter";
-
- public MediaplayerInfoPagerAdapter(FragmentManager fm) {
- super(fm);
- }
-
- @Override
- public Fragment getItem(int position) {
- Log.d(TAG, "getItem(" + position + ")");
- switch (position) {
- case POS_COVER:
- return new CoverFragment();
- case POS_DESCR:
- return new ItemDescriptionFragment();
- case POS_CHAPTERS:
- return new ChaptersFragment();
- default:
- return null;
- }
- }
-
- @Override
- public int getCount() {
- return NUM_CONTENT_FRAGMENTS;
- }
- }
-}
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 a1a4e0374..212cb2f75 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -171,20 +171,6 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
}
- @Override
- protected void postStatusMsg(int resId, boolean showToast) {
- if (resId == R.string.player_preparing_msg) {
- progressIndicator.setVisibility(View.VISIBLE);
- } else {
- progressIndicator.setVisibility(View.INVISIBLE);
- }
- }
-
- @Override
- protected void clearStatusMsg() {
- progressIndicator.setVisibility(View.INVISIBLE);
- }
-
private final View.OnTouchListener onVideoviewTouched = (v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (PictureInPictureUtil.isInPictureInPictureMode(this)) {
@@ -292,16 +278,11 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
return;
}
- if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO) {
- Log.d(TAG, "ReloadNotification received, switching to Audioplayer now");
- destroyingDueToReload = true;
- finish();
- startActivity(new Intent(this, AudioplayerActivity.class));
- } else if (notificationCode == PlaybackService.EXTRA_CODE_CAST) {
+ if (notificationCode == PlaybackService.EXTRA_CODE_CAST) {
Log.d(TAG, "ReloadNotification received, switching to Castplayer now");
destroyingDueToReload = true;
finish();
- startActivity(new Intent(this, CastplayerActivity.class));
+ startActivity(new Intent(this, MainActivity.class).putExtra(MainActivity.EXTRA_OPEN_PLAYER, true));
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
index 1171acaa5..cb72a9150 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
@@ -5,37 +5,34 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
-public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
- private static final String TAG = "ChapterListAdapter";
-
+public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapter.ChapterHolder> {
private Playable media;
private final Callback callback;
+ private final Context context;
private int currentChapterIndex = -1;
private boolean hasImages = false;
- public ChaptersListAdapter(Context context, int textViewResourceId, Callback callback) {
- super(context, textViewResourceId);
+ public ChaptersListAdapter(Context context, Callback callback) {
this.callback = callback;
+ this.context = context;
}
public void setMedia(Playable media) {
@@ -51,34 +48,10 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
notifyDataSetChanged();
}
- @NonNull
- @Override
- public View getView(final int position, View convertView, @NonNull ViewGroup parent) {
- Holder holder;
+ @Override
+ public void onBindViewHolder(@NonNull ChapterHolder holder, int position) {
Chapter sc = getItem(position);
-
- // Inflate Layout
- if (convertView == null) {
- holder = new Holder();
- LayoutInflater inflater = (LayoutInflater) getContext()
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- convertView = inflater.inflate(R.layout.simplechapter_item, parent, false);
- holder.view = convertView;
- holder.title = convertView.findViewById(R.id.txtvTitle);
- holder.start = convertView.findViewById(R.id.txtvStart);
- holder.link = convertView.findViewById(R.id.txtvLink);
- holder.image = convertView.findViewById(R.id.imgvCover);
- holder.duration = convertView.findViewById(R.id.txtvDuration);
- holder.secondaryActionButton = convertView.findViewById(R.id.secondaryActionButton);
- holder.secondaryActionIcon = convertView.findViewById(R.id.secondaryActionIcon);
- convertView.setTag(holder);
- } else {
- holder = (Holder) convertView.getTag();
-
- }
-
holder.title.setText(sc.getTitle());
holder.start.setText(Converter.getDurationStringLong((int) sc
.getStart()));
@@ -89,7 +62,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
} else {
duration = media.getDuration() - sc.getStart();
}
- holder.duration.setText(getContext().getString(R.string.chapter_duration,
+ holder.duration.setText(context.getString(R.string.chapter_duration,
Converter.getDurationStringLong((int) duration)));
if (sc.getLink() == null) {
@@ -97,9 +70,9 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
} else {
holder.link.setVisibility(View.VISIBLE);
holder.link.setText(sc.getLink());
- holder.link.setOnClickListener(v -> IntentUtils.openInBrowser(getContext(), sc.getLink()));
+ holder.link.setOnClickListener(v -> IntentUtils.openInBrowser(context, sc.getLink()));
}
- holder.secondaryActionIcon.setImageResource(ThemeUtils.getDrawableFromAttr(getContext(), R.attr.av_play));
+ holder.secondaryActionIcon.setImageResource(ThemeUtils.getDrawableFromAttr(context, R.attr.av_play));
holder.secondaryActionButton.setOnClickListener(v -> {
if (callback != null) {
callback.onPlayChapterButtonClicked(position);
@@ -107,46 +80,40 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
});
if (position == currentChapterIndex) {
- int playingBackGroundColor = ThemeUtils.getColorFromAttr(getContext(), R.attr.currently_playing_background);
- holder.view.setBackgroundColor(playingBackGroundColor);
+ int playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background);
+ holder.itemView.setBackgroundColor(playingBackGroundColor);
} else {
- holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
+ holder.itemView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.transparent));
}
if (hasImages) {
holder.image.setVisibility(View.VISIBLE);
if (TextUtils.isEmpty(sc.getImageUrl())) {
- Glide.with(getContext()).clear(holder.image);
+ Glide.with(context).clear(holder.image);
} else {
- Glide.with(getContext())
+ Glide.with(context)
.load(EmbeddedChapterImage.getModelFor(media, position))
.apply(new RequestOptions()
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.dontAnimate()
.transforms(new FitCenter(), new RoundedCorners((int)
- (4 * getContext().getResources().getDisplayMetrics().density))))
+ (4 * context.getResources().getDisplayMetrics().density))))
.into(holder.image);
}
} else {
holder.image.setVisibility(View.GONE);
}
-
- return convertView;
}
- static class Holder {
- View view;
- TextView title;
- TextView start;
- TextView link;
- TextView duration;
- ImageView image;
- View secondaryActionButton;
- ImageView secondaryActionIcon;
+ @NonNull
+ @Override
+ public ChapterHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ return new ChapterHolder(inflater.inflate(R.layout.simplechapter_item, parent, false));
}
@Override
- public int getCount() {
+ public int getItemCount() {
if (media == null || media.getChapters() == null) {
return 0;
}
@@ -160,6 +127,27 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
return counter;
}
+ static class ChapterHolder extends RecyclerView.ViewHolder {
+ final TextView title;
+ final TextView start;
+ final TextView link;
+ final TextView duration;
+ final ImageView image;
+ final View secondaryActionButton;
+ final ImageView secondaryActionIcon;
+
+ public ChapterHolder(@NonNull View itemView) {
+ super(itemView);
+ title = itemView.findViewById(R.id.txtvTitle);
+ start = itemView.findViewById(R.id.txtvStart);
+ link = itemView.findViewById(R.id.txtvLink);
+ image = itemView.findViewById(R.id.imgvCover);
+ duration = itemView.findViewById(R.id.txtvDuration);
+ secondaryActionButton = itemView.findViewById(R.id.secondaryActionButton);
+ secondaryActionIcon = itemView.findViewById(R.id.secondaryActionIcon);
+ }
+ }
+
public void notifyChapterChanged(int newChapterIndex) {
currentChapterIndex = newChapterIndex;
notifyDataSetChanged();
@@ -169,7 +157,6 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
return media.getDuration() > 0 && media.getDuration() < c.getStart();
}
- @Override
public Chapter getItem(int position) {
int i = 0;
for (Chapter chapter : media.getChapters()) {
@@ -181,7 +168,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
}
}
}
- return super.getItem(position);
+ return null;
}
public interface Callback {
diff --git a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
index a462c4034..44f18e9da 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
@@ -4,27 +4,22 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.AudioplayerActivity;
-import de.danoeh.antennapod.activity.CastplayerActivity;
+import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.VideoplayerActivity;
import de.danoeh.antennapod.core.PlaybackServiceCallbacks;
import de.danoeh.antennapod.core.feed.MediaType;
-
public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
@Override
public Intent getPlayerActivityIntent(Context context, MediaType mediaType, boolean remotePlayback) {
- if (remotePlayback) {
- return new Intent(context, CastplayerActivity.class);
- }
- if (mediaType == MediaType.VIDEO) {
+ if (mediaType == MediaType.AUDIO || remotePlayback) {
+ return new Intent(context, MainActivity.class).putExtra(MainActivity.EXTRA_OPEN_PLAYER, true);
+ } else {
Intent i = new Intent(context, VideoplayerActivity.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
i.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
}
return i;
- } else {
- return new Intent(context, AudioplayerActivity.class);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
index 9514ea5eb..40b8d5b84 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -313,7 +313,7 @@ public class EpisodesApplyActionFragment extends Fragment {
}
}
if (resId != 0) {
- Snackbar.make(getActivity().findViewById(R.id.content), resId, Snackbar.LENGTH_SHORT)
+ Snackbar.make(getActivity().findViewById(android.R.id.content), resId, Snackbar.LENGTH_SHORT)
.show();
return true;
} else {
@@ -469,7 +469,7 @@ public class EpisodesApplyActionFragment extends Fragment {
private void close(@PluralsRes int msgId, int numItems) {
if (numItems > 0) {
- Snackbar.make(getActivity().findViewById(R.id.content),
+ Snackbar.make(getActivity().findViewById(android.R.id.content),
getResources().getQuantityString(msgId, numItems, numItems),
Snackbar.LENGTH_LONG
)
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SkipPreferenceDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SkipPreferenceDialog.java
new file mode 100644
index 000000000..7bb8f5ad6
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SkipPreferenceDialog.java
@@ -0,0 +1,58 @@
+package de.danoeh.antennapod.dialog;
+
+import android.content.Context;
+import android.widget.TextView;
+import androidx.appcompat.app.AlertDialog;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
+/**
+ * Shows the dialog that allows setting the skip time.
+ */
+public class SkipPreferenceDialog {
+ public static void showSkipPreference(Context context, SkipDirection direction, TextView textView) {
+ int checked = 0;
+
+ int skipSecs;
+ if (direction == SkipDirection.SKIP_FORWARD) {
+ skipSecs = UserPreferences.getFastForwardSecs();
+ } else {
+ skipSecs = UserPreferences.getRewindSecs();
+ }
+
+ final int[] values = context.getResources().getIntArray(R.array.seek_delta_values);
+ final String[] choices = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ if (skipSecs == values[i]) {
+ checked = i;
+ }
+ choices[i] = values[i] + " " + context.getString(R.string.time_seconds);
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(direction == SkipDirection.SKIP_FORWARD ? R.string.pref_fast_forward : R.string.pref_rewind);
+ builder.setSingleChoiceItems(choices, checked, null);
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ int choice = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
+ if (choice < 0 || choice >= values.length) {
+ System.err.printf("Choice in showSkipPreference is out of bounds %d", choice);
+ } else {
+ int seconds = values[choice];
+ if (direction == SkipDirection.SKIP_FORWARD) {
+ UserPreferences.setFastForwardSecs(seconds);
+ } else {
+ UserPreferences.setRewindSecs(seconds);
+ }
+ if (textView != null) {
+ textView.setText(String.valueOf(seconds));
+ }
+ }
+ });
+ builder.create().show();
+ }
+
+ public enum SkipDirection {
+ SKIP_FORWARD, SKIP_REWIND
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
new file mode 100644
index 000000000..ed136af3c
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
@@ -0,0 +1,505 @@
+package de.danoeh.antennapod.fragment;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ProgressBar;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.CastEnabledActivity;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.event.FavoritesEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.TimeSpeedConverter;
+import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
+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.PagerIndicatorView;
+import de.danoeh.antennapod.view.PlaybackSpeedIndicatorView;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.text.DecimalFormat;
+import java.util.List;
+
+/**
+ * Shows the audio player.
+ */
+public class AudioPlayerFragment extends Fragment implements
+ SeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
+ public static final String TAG = "AudioPlayerFragment";
+ private static final int POS_COVER = 0;
+ private static final int POS_DESCR = 1;
+ private static final int POS_CHAPTERS = 2;
+ private static final int NUM_CONTENT_FRAGMENTS = 3;
+ private static final String PREFS = "AudioPlayerFragmentPreferences";
+ private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
+ private static final float EPSILON = 0.001f;
+
+ PlaybackSpeedIndicatorView butPlaybackSpeed;
+ TextView txtvPlaybackSpeed;
+ private ViewPager pager;
+ private PagerIndicatorView pageIndicator;
+ private TextView txtvPosition;
+ private TextView txtvLength;
+ private SeekBar sbPosition;
+ private ImageButton butRev;
+ private TextView txtvRev;
+ private ImageButton butPlay;
+ private ImageButton butFF;
+ private TextView txtvFF;
+ private ImageButton butSkip;
+ private Toolbar toolbar;
+ private ProgressBar progressIndicator;
+
+ private PlaybackController controller;
+ private boolean showTimeLeft;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View root = inflater.inflate(R.layout.audioplayer_fragment, container, false);
+ toolbar = root.findViewById(R.id.toolbar);
+ toolbar.setTitle("");
+ toolbar.setNavigationOnClickListener(v ->
+ ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED));
+ toolbar.setOnMenuItemClickListener(this);
+ setupOptionsMenu();
+
+ ExternalPlayerFragment externalPlayerFragment = new ExternalPlayerFragment();
+ getFragmentManager().beginTransaction()
+ .replace(R.id.playerFragment, externalPlayerFragment, ExternalPlayerFragment.TAG)
+ .commit();
+
+ butPlaybackSpeed = root.findViewById(R.id.butPlaybackSpeed);
+ txtvPlaybackSpeed = root.findViewById(R.id.txtvPlaybackSpeed);
+ sbPosition = root.findViewById(R.id.sbPosition);
+ txtvPosition = root.findViewById(R.id.txtvPosition);
+ txtvLength = root.findViewById(R.id.txtvLength);
+ butRev = root.findViewById(R.id.butRev);
+ txtvRev = root.findViewById(R.id.txtvRev);
+ butPlay = root.findViewById(R.id.butPlay);
+ butFF = root.findViewById(R.id.butFF);
+ txtvFF = root.findViewById(R.id.txtvFF);
+ butSkip = root.findViewById(R.id.butSkip);
+ progressIndicator = root.findViewById(R.id.progLoading);
+
+ setupLengthTextView();
+ setupControlButtons();
+ setupPlaybackSpeedButton();
+ txtvRev.setText(String.valueOf(UserPreferences.getRewindSecs()));
+ txtvFF.setText(String.valueOf(UserPreferences.getFastForwardSecs()));
+ sbPosition.setOnSeekBarChangeListener(this);
+
+ pager = root.findViewById(R.id.pager);
+ AudioPlayerPagerAdapter pagerAdapter = new AudioPlayerPagerAdapter(getFragmentManager());
+ pager.setAdapter(pagerAdapter);
+ // Required for getChildAt(int) in ViewPagerBottomSheetBehavior to return the correct page
+ pager.setOffscreenPageLimit(NUM_CONTENT_FRAGMENTS);
+ pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
+ @Override
+ public void onPageSelected(int position) {
+ pager.post(() -> ((MainActivity) getActivity()).getBottomSheet().updateScrollingChild());
+ }
+ });
+ pageIndicator = root.findViewById(R.id.page_indicator);
+ pageIndicator.setViewPager(pager);
+ pageIndicator.setOnClickListener(v ->
+ pager.setCurrentItem((pager.getCurrentItem() + 1) % pager.getChildCount()));
+ return root;
+ }
+
+ public View getExternalPlayerHolder() {
+ return getView().findViewById(R.id.playerFragment);
+ }
+
+ private void setupControlButtons() {
+ butRev.setOnClickListener(v -> {
+ if (controller != null) {
+ int curr = controller.getPosition();
+ controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000);
+ }
+ });
+ butRev.setOnLongClickListener(v -> {
+ SkipPreferenceDialog.showSkipPreference(getContext(),
+ SkipPreferenceDialog.SkipDirection.SKIP_REWIND, txtvRev);
+ return true;
+ });
+ butPlay.setOnClickListener(v -> {
+ if (controller != null) {
+ controller.init();
+ controller.playPause();
+ }
+ });
+ butFF.setOnClickListener(v -> {
+ if (controller != null) {
+ int curr = controller.getPosition();
+ controller.seekTo(curr + UserPreferences.getFastForwardSecs() * 1000);
+ }
+ });
+ butFF.setOnLongClickListener(v -> {
+ SkipPreferenceDialog.showSkipPreference(getContext(),
+ SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF);
+ return false;
+ });
+ butSkip.setOnClickListener(v ->
+ IntentUtils.sendLocalBroadcast(getActivity(), PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
+ }
+
+ private void setupLengthTextView() {
+ SharedPreferences prefs = getContext().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+ showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
+ txtvLength.setOnClickListener(v -> {
+ if (controller == null) {
+ return;
+ }
+ showTimeLeft = !showTimeLeft;
+ prefs.edit().putBoolean(PREF_SHOW_TIME_LEFT, showTimeLeft).apply();
+ updatePosition(new PlaybackPositionEvent(controller.getPosition(), controller.getDuration()));
+ });
+ }
+
+ private void setupPlaybackSpeedButton() {
+ butPlaybackSpeed.setOnClickListener(v -> {
+ if (controller == null) {
+ return;
+ }
+ if (!controller.canSetPlaybackSpeed()) {
+ VariableSpeedDialog.showGetPluginDialog(getContext());
+ return;
+ }
+ float[] availableSpeeds = UserPreferences.getPlaybackSpeedArray();
+ float currentSpeed = controller.getCurrentPlaybackSpeedMultiplier();
+
+ int newSpeedIndex = 0;
+ while (newSpeedIndex < availableSpeeds.length && availableSpeeds[newSpeedIndex] < currentSpeed + EPSILON) {
+ newSpeedIndex++;
+ }
+
+ float newSpeed;
+ if (availableSpeeds.length == 0) {
+ newSpeed = 1.0f;
+ } else if (newSpeedIndex == availableSpeeds.length) {
+ newSpeed = availableSpeeds[0];
+ } else {
+ newSpeed = availableSpeeds[newSpeedIndex];
+ }
+
+ PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(newSpeed);
+ UserPreferences.setPlaybackSpeed(newSpeed);
+ controller.setPlaybackSpeed(newSpeed);
+ updateUi();
+ });
+ butPlaybackSpeed.setOnLongClickListener(v -> {
+ VariableSpeedDialog.showDialog(getContext());
+ return true;
+ });
+ butPlaybackSpeed.setVisibility(View.VISIBLE);
+ txtvPlaybackSpeed.setVisibility(View.VISIBLE);
+ }
+
+ protected void updatePlaybackSpeedButton() {
+ if (butPlaybackSpeed == null || controller == null) {
+ return;
+ }
+ float speed = 1.0f;
+ if (controller.canSetPlaybackSpeed()) {
+ speed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(controller.getMedia());
+ }
+ String speedStr = new DecimalFormat("0.00").format(speed);
+ txtvPlaybackSpeed.setText(speedStr);
+ butPlaybackSpeed.setSpeed(speed);
+ butPlaybackSpeed.setAlpha(controller.canSetPlaybackSpeed() ? 1.0f : 0.5f);
+ butPlaybackSpeed.setVisibility(View.VISIBLE);
+ txtvPlaybackSpeed.setVisibility(View.VISIBLE);
+ }
+
+ private PlaybackController newPlaybackController() {
+ return new PlaybackController(getActivity(), false) {
+
+ @Override
+ public void setupGUI() {
+ updateUi();
+ }
+
+ @Override
+ public void onBufferStart() {
+ progressIndicator.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onBufferEnd() {
+ progressIndicator.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onBufferUpdate(float progress) {
+ sbPosition.setSecondaryProgress((int) (progress * sbPosition.getMax()));
+ }
+
+ @Override
+ public void handleError(int code) {
+ final AlertDialog.Builder errorDialog = new AlertDialog.Builder(getContext());
+ errorDialog.setTitle(R.string.error_label);
+ errorDialog.setMessage(MediaPlayerError.getErrorString(getContext(), code));
+ errorDialog.setNeutralButton(android.R.string.ok,
+ (dialog, which) -> {
+ dialog.dismiss();
+ ((MainActivity) getActivity()).getBottomSheet()
+ .setState(BottomSheetBehavior.STATE_COLLAPSED);
+ }
+ );
+ errorDialog.create().show();
+ }
+
+ @Override
+ public void onSleepTimerUpdate() {
+ setupOptionsMenu();
+ }
+
+ @Override
+ public ImageButton getPlayButton() {
+ return butPlay;
+ }
+
+ @Override
+ public boolean loadMediaInfo() {
+ updateUi();
+ return true;
+ }
+
+ @Override
+ public void onShutdownNotification() {
+ ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
+ }
+
+ @Override
+ public void onPlaybackEnd() {
+ ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
+ }
+
+ @Override
+ public void onPlaybackSpeedChange() {
+ updatePlaybackSpeedButton();
+ }
+
+ @Override
+ public void onSetSpeedAbilityChanged() {
+ updatePlaybackSpeedButton();
+ }
+ };
+ }
+
+ private void updateUi() {
+ if (controller == null) {
+ return;
+ }
+ updatePosition(new PlaybackPositionEvent(controller.getPosition(), controller.getDuration()));
+ updatePlaybackSpeedButton();
+ setupOptionsMenu();
+
+ if (controller.getMedia() != null) {
+ List<Chapter> chapters = controller.getMedia().getChapters();
+ boolean hasChapters = chapters != null && !chapters.isEmpty();
+ pageIndicator.setDisabledPage(hasChapters ? -1 : 2);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ controller = newPlaybackController();
+ controller.init();
+ updateUi();
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ controller.release();
+ controller = null;
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void updatePosition(PlaybackPositionEvent event) {
+ if (controller == null || txtvPosition == null || txtvLength == null || sbPosition == null) {
+ return;
+ }
+
+ TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
+ int currentPosition = converter.convert(event.getPosition());
+ int duration = converter.convert(event.getDuration());
+ int remainingTime = converter.convert(event.getDuration() - event.getPosition());
+ 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");
+ return;
+ }
+ txtvPosition.setText(Converter.getDurationStringLong(currentPosition));
+ if (showTimeLeft) {
+ txtvLength.setText("-" + Converter.getDurationStringLong(remainingTime));
+ } else {
+ txtvLength.setText(Converter.getDurationStringLong(duration));
+ }
+ float progress = ((float) event.getPosition()) / event.getDuration();
+ sbPosition.setProgress((int) (progress * sbPosition.getMax()));
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void favoritesChanged(FavoritesEvent event) {
+ setupOptionsMenu();
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (controller == null || txtvLength == null) {
+ return;
+ }
+ if (fromUser) {
+ float prog = progress / ((float) seekBar.getMax());
+ int duration = controller.getDuration();
+ TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
+ int position = converter.convert((int) (prog * duration));
+ txtvPosition.setText(Converter.getDurationStringLong(position));
+
+ if (showTimeLeft && prog != 0) {
+ int timeLeft = converter.convert(duration - (int) (prog * duration));
+ String length = "-" + Converter.getDurationStringLong(timeLeft);
+ txtvLength.setText(length);
+ }
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ // interrupt position Observer, restart later
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ if (controller != null) {
+ float prog = seekBar.getProgress() / ((float) seekBar.getMax());
+ controller.seekTo((int) (prog * controller.getDuration()));
+ }
+ }
+
+ public void setupOptionsMenu() {
+ if (toolbar.getMenu().size() == 0) {
+ toolbar.inflateMenu(R.menu.mediaplayer);
+ }
+ if (controller == null) {
+ return;
+ }
+ Playable media = controller.getMedia();
+ boolean isFeedMedia = media instanceof FeedMedia;
+ toolbar.getMenu().findItem(R.id.open_feed_item).setVisible(isFeedMedia);
+ if (isFeedMedia) {
+ FeedItemMenuHandler.onPrepareMenu(toolbar.getMenu(), ((FeedMedia) media).getItem());
+ }
+
+ toolbar.getMenu().findItem(R.id.set_sleeptimer_item).setVisible(!controller.sleepTimerActive());
+ toolbar.getMenu().findItem(R.id.disable_sleeptimer_item).setVisible(controller.sleepTimerActive());
+
+ ((CastEnabledActivity) getActivity()).requestCastButton(toolbar.getMenu());
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ if (controller == null) {
+ return false;
+ }
+ Playable media = controller.getMedia();
+ if (media == null) {
+ return false;
+ }
+
+ final @Nullable FeedItem feedItem = (media instanceof FeedMedia) ? ((FeedMedia) media).getItem() : null;
+ if (feedItem != null && FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), feedItem)) {
+ return true;
+ }
+ switch (item.getItemId()) {
+ case R.id.disable_sleeptimer_item: // Fall-through
+ case R.id.set_sleeptimer_item:
+ new SleepTimerDialog().show(getFragmentManager(), "SleepTimerDialog");
+ return true;
+ case R.id.audio_controls:
+ PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(false);
+ dialog.show(getFragmentManager(), "playback_controls");
+ return true;
+ case R.id.open_feed_item:
+ if (feedItem != null) {
+ Intent intent = MainActivity.getIntentToOpenFeed(getContext(), feedItem.getFeedId());
+ startActivity(intent);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private static class AudioPlayerPagerAdapter extends FragmentPagerAdapter {
+ private static final String TAG = "AudioPlayerPagerAdapter";
+
+ public AudioPlayerPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ Log.d(TAG, "getItem(" + position + ")");
+ switch (position) {
+ case POS_COVER:
+ return new CoverFragment();
+ case POS_DESCR:
+ return new ItemDescriptionFragment();
+ case POS_CHAPTERS:
+ return new ChaptersFragment();
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return NUM_CONTENT_FRAGMENTS;
+ }
+ }
+}
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 9940ccbdd..0aba568d1 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -1,24 +1,24 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
-import androidx.fragment.app.ListFragment;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.View;
-import android.widget.ListView;
-
-import de.danoeh.antennapod.core.util.ChapterUtils;
-import java.util.List;
-import java.util.ListIterator;
-
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
-import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
+import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
-
import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -28,38 +28,43 @@ import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
-public class ChaptersFragment extends ListFragment {
+public class ChaptersFragment extends Fragment {
private static final String TAG = "ChaptersFragment";
private ChaptersListAdapter adapter;
private PlaybackController controller;
private Disposable disposable;
private int focusedChapter = -1;
private Playable media;
+ private LinearLayoutManager layoutManager;
+ @Nullable
@Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- // add padding
- final ListView lv = getListView();
- lv.setClipToPadding(false);
- final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
- lv.setPadding(0, vertPadding, 0, vertPadding);
-
- EmptyViewHandler emptyView = new EmptyViewHandler(getContext());
- emptyView.attachToListView(lv);
- emptyView.setIcon(R.attr.ic_bookmark);
- emptyView.setTitle(R.string.no_chapters_head_label);
- emptyView.setMessage(R.string.no_chapters_label);
-
- adapter = new ChaptersListAdapter(getActivity(), 0, pos -> {
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View root = inflater.inflate(R.layout.simple_list_fragment, container, false);
+ root.findViewById(R.id.toolbar).setVisibility(View.GONE);
+ RecyclerView recyclerView = root.findViewById(R.id.recyclerView);
+ layoutManager = new LinearLayoutManager(getActivity());
+ recyclerView.setLayoutManager(layoutManager);
+ recyclerView.setHasFixedSize(true);
+ recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
+
+ adapter = new ChaptersListAdapter(getActivity(), pos -> {
if (controller.getStatus() != PlayerStatus.PLAYING) {
controller.playPause();
}
- Chapter chapter = (Chapter) getListAdapter().getItem(pos);
+ Chapter chapter = adapter.getItem(pos);
controller.seekToChapter(chapter);
updateChapterSelection(pos);
});
- setListAdapter(adapter);
+ recyclerView.setAdapter(adapter);
+
+ EmptyViewHandler emptyView = new EmptyViewHandler(getContext());
+ emptyView.attachToRecyclerView(recyclerView);
+ emptyView.setIcon(R.attr.ic_bookmark);
+ emptyView.setTitle(R.string.no_chapters_head_label);
+ emptyView.setMessage(R.string.no_chapters_label);
+
+ return root;
}
@Override
@@ -136,7 +141,6 @@ public class ChaptersFragment extends ListFragment {
return;
}
adapter.setMedia(media);
- adapter.notifyDataSetChanged();
int positionOfCurrentChapter = getCurrentChapter(media);
updateChapterSelection(positionOfCurrentChapter);
}
@@ -149,9 +153,9 @@ public class ChaptersFragment extends ListFragment {
if (position != -1 && focusedChapter != position) {
focusedChapter = position;
adapter.notifyChapterChanged(focusedChapter);
- if (getListView().getFirstVisiblePosition() >= position
- || getListView().getLastVisiblePosition() <= position) {
- getListView().setSelectionFromTop(position, 100);
+ if (layoutManager.findFirstCompletelyVisibleItemPosition() >= position
+ || layoutManager.findLastCompletelyVisibleItemPosition() <= position) {
+ layoutManager.scrollToPositionWithOffset(position, 100);
}
}
}
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 ce2232a55..03b1d6f8f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -1,9 +1,7 @@
package de.danoeh.antennapod.fragment;
import android.content.Intent;
-import android.os.Build;
import android.os.Bundle;
-import androidx.core.app.ActivityOptionsCompat;
import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
@@ -17,7 +15,9 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
@@ -40,7 +40,6 @@ import org.greenrobot.eventbus.ThreadMode;
public class ExternalPlayerFragment extends Fragment {
public static final String TAG = "ExternalPlayerFragment";
- private ViewGroup fragmentLayout;
private ImageView imgvCover;
private TextView txtvTitle;
private ImageButton butPlay;
@@ -56,26 +55,21 @@ public class ExternalPlayerFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- View root = inflater.inflate(R.layout.external_player_fragment,
- container, false);
- fragmentLayout = root.findViewById(R.id.fragmentLayout);
+ View root = inflater.inflate(R.layout.external_player_fragment, container, false);
imgvCover = root.findViewById(R.id.imgvCover);
txtvTitle = root.findViewById(R.id.txtvTitle);
butPlay = root.findViewById(R.id.butPlay);
mFeedName = root.findViewById(R.id.txtvAuthor);
mProgressBar = root.findViewById(R.id.episodeProgress);
- fragmentLayout.setOnClickListener(v -> {
+ root.findViewById(R.id.fragmentLayout).setOnClickListener(v -> {
Log.d(TAG, "layoutInfo was clicked");
if (controller != null && controller.getMedia() != null) {
- Intent intent = PlaybackService.getPlayerActivityIntent(getActivity(), controller.getMedia());
-
if (controller.getMedia().getMediaType() == MediaType.AUDIO) {
- ActivityOptionsCompat options = ActivityOptionsCompat
- .makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition");
- startActivity(intent, options.toBundle());
+ ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_EXPANDED);
} else {
+ Intent intent = PlaybackService.getPlayerActivityIntent(getActivity(), controller.getMedia());
startActivity(intent);
}
}
@@ -178,9 +172,7 @@ public class ExternalPlayerFragment extends Fragment {
}
private void playbackDone() {
- if (fragmentLayout != null) {
- fragmentLayout.setVisibility(View.GONE);
- }
+ clearUi();
if (controller != null) {
controller.release();
}
@@ -217,10 +209,22 @@ public class ExternalPlayerFragment extends Fragment {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(media -> updateUi((Playable) media),
error -> Log.e(TAG, Log.getStackTraceString(error)),
- () -> fragmentLayout.setVisibility(View.GONE));
+ this::clearUi);
return true;
}
+ private void clearUi() {
+ if (txtvTitle == null || mFeedName == null || mProgressBar == null || butPlay == null) {
+ return;
+ }
+ txtvTitle.setText(R.string.no_media_playing_label);
+ mFeedName.setText("");
+ butPlay.setVisibility(View.GONE);
+ mProgressBar.setProgress(0);
+ Glide.with(getActivity()).clear(imgvCover);
+ ((MainActivity) getActivity()).getBottomSheet().setLocked(true);
+ }
+
private void updateUi(Playable media) {
if (media != null) {
txtvTitle.setText(media.getEpisodeTitle());
@@ -236,12 +240,13 @@ public class ExternalPlayerFragment extends Fragment {
.fitCenter()
.dontAnimate())
.into(imgvCover);
-
- fragmentLayout.setVisibility(View.VISIBLE);
if (controller != null && controller.isPlayingVideoLocally()) {
butPlay.setVisibility(View.GONE);
+ ((MainActivity) getActivity()).getBottomSheet().setLocked(true);
+ ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
butPlay.setVisibility(View.VISIBLE);
+ ((MainActivity) getActivity()).getBottomSheet().setLocked(false);
}
} else {
Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!");
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 256615199..58cc9290c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -10,6 +10,7 @@ import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
import de.danoeh.antennapod.view.ShownotesWebView;
@@ -35,7 +36,8 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "Creating view");
- webvDescription = new ShownotesWebView(getActivity().getApplicationContext());
+ View root = inflater.inflate(R.layout.item_description_fragment, container, false);
+ webvDescription = root.findViewById(R.id.webview);
webvDescription.setTimecodeSelectedListener(time -> {
if (controller != null) {
controller.seekTo(time);
@@ -46,7 +48,7 @@ public class ItemDescriptionFragment extends Fragment {
webvDescription.postDelayed(ItemDescriptionFragment.this::restoreFromPreference, 50);
});
registerForContextMenu(webvDescription);
- return webvDescription;
+ return root;
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
index 6f04ff1e5..aac89a65f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
@@ -136,9 +136,6 @@ public class ItemPagerFragment extends Fragment {
return;
}
super.onCreateOptionsMenu(menu, inflater);
- if (Flavors.FLAVOR == Flavors.PLAY) {
- ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
- }
inflater.inflate(R.menu.feeditem_options, menu);
if (item.hasMedia()) {
FeedItemMenuHandler.onPrepareMenu(menu, item);
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 0a752b855..0411fd01b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -416,11 +416,11 @@ public class QueueFragment extends Fragment {
recyclerAdapter.setLocked(locked);
}
if (locked) {
- Snackbar.make(getActivity().findViewById(R.id.content), R.string
- .queue_locked, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(getActivity().findViewById(android.R.id.content),
+ R.string.queue_locked, Snackbar.LENGTH_SHORT).show();
} else {
- Snackbar.make(getActivity().findViewById(R.id.content), R.string
- .queue_unlocked, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(getActivity().findViewById(android.R.id.content),
+ R.string.queue_unlocked, Snackbar.LENGTH_SHORT).show();
}
}
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 34684ac49..6b2255b52 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
@@ -9,11 +9,11 @@ import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MediaplayerActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
+import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.preferences.PreferenceControllerFlavorHelper;
import java.util.Map;
@@ -48,11 +48,11 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
return true;
});
findPreference(PREF_PLAYBACK_REWIND_DELTA_LAUNCHER).setOnPreferenceClickListener(preference -> {
- MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_REWIND);
+ SkipPreferenceDialog.showSkipPreference(activity, SkipPreferenceDialog.SkipDirection.SKIP_REWIND, null);
return true;
});
findPreference(PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER).setOnPreferenceClickListener(preference -> {
- MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_FORWARD);
+ SkipPreferenceDialog.showSkipPreference(activity, SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, null);
return true;
});
if (!PictureInPictureUtil.supportsPictureInPicture(activity)) {
diff --git a/app/src/main/java/de/danoeh/antennapod/view/LockableBottomSheetBehavior.java b/app/src/main/java/de/danoeh/antennapod/view/LockableBottomSheetBehavior.java
new file mode 100644
index 000000000..8e8d98fc9
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/LockableBottomSheetBehavior.java
@@ -0,0 +1,86 @@
+package de.danoeh.antennapod.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import com.google.android.material.bottomsheet.ViewPagerBottomSheetBehavior;
+
+/**
+ * Based on https://stackoverflow.com/a/40798214
+ */
+public class LockableBottomSheetBehavior<V extends View> extends ViewPagerBottomSheetBehavior<V> {
+ private boolean isLocked = false;
+
+ public LockableBottomSheetBehavior() {}
+
+ public LockableBottomSheetBehavior(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setLocked(boolean locked) {
+ isLocked = locked;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
+ boolean handled = false;
+
+ if (!isLocked) {
+ handled = super.onInterceptTouchEvent(parent, child, event);
+ }
+
+ return handled;
+ }
+
+ @Override
+ public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
+ boolean handled = false;
+
+ if (!isLocked) {
+ handled = super.onTouchEvent(parent, child, event);
+ }
+
+ return handled;
+ }
+
+ @Override
+ public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild,
+ View target, int nestedScrollAxes) {
+ boolean handled = false;
+
+ if (!isLocked) {
+ handled = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
+ }
+
+ return handled;
+ }
+
+ @Override
+ public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,
+ int dx, int dy, int[] consumed) {
+ if (!isLocked) {
+ super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
+ }
+ }
+
+ @Override
+ public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
+ if (!isLocked) {
+ super.onStopNestedScroll(coordinatorLayout, child, target);
+ }
+ }
+
+ @Override
+ public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target,
+ float velocityX, float velocityY) {
+ boolean handled = false;
+
+ if (!isLocked) {
+ handled = super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
+ }
+
+ return handled;
+ }
+}
diff --git a/app/src/main/res/drawable/shadow.xml b/app/src/main/res/drawable/shadow.xml
deleted file mode 100644
index fc5110e0b..000000000
--- a/app/src/main/res/drawable/shadow.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <gradient
- android:startColor="@android:color/transparent"
- android:endColor="#40000000"
- android:angle="90" />
-</shape>
diff --git a/app/src/main/res/layout/mediaplayerinfo_activity.xml b/app/src/main/res/layout/audioplayer_fragment.xml
index 526994752..905371fa4 100644
--- a/app/src/main/res/layout/mediaplayerinfo_activity.xml
+++ b/app/src/main/res/layout/audioplayer_fragment.xml
@@ -1,24 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
-<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <RelativeLayout
+<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:layout_width="match_parent"
android:layout_height="match_parent">
- <androidx.appcompat.widget.Toolbar
+ <androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
android:layout_alignParentTop="true"
+ app:navigationIcon="?homeAsUpIndicator"
android:id="@+id/toolbar"/>
- <de.danoeh.antennapod.view.PagerIndicatorView
+ <de.danoeh.antennapod.view.PagerIndicatorView
android:id="@+id/page_indicator"
android:layout_height="16dp"
android:layout_width="40dp"
@@ -27,7 +24,16 @@
android:layout_below="@id/toolbar"
android:layout_centerHorizontal="true"/>
- <androidx.viewpager.widget.ViewPager
+ <FrameLayout
+ android:id="@+id/playerFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:background="?android:attr/windowBackground"
+ tools:layout_height="@dimen/external_player_height"
+ tools:background="@android:color/holo_green_light" />
+
+ <androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
@@ -35,9 +41,9 @@
android:layout_below="@id/toolbar"
android:foreground="?android:windowContentOverlay"
tools:background="@android:color/holo_orange_light"
- android:layout_marginBottom="12dp" />
+ android:layout_marginBottom="12dp"/>
- <SeekBar
+ <SeekBar
android:id="@+id/sbPosition"
android:layout_width="match_parent"
android:layout_height="24dp"
@@ -47,9 +53,9 @@
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layoutDirection="ltr"
- tools:background="@android:color/holo_green_dark" />
+ tools:background="@android:color/holo_green_dark"/>
- <LinearLayout
+ <LinearLayout
android:id="@+id/playtime_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -57,14 +63,14 @@
android:layoutDirection="ltr"
android:orientation="vertical">
- <RelativeLayout
+ <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:layout_marginBottom="4dp">
- <TextView
+ <TextView
android:id="@+id/txtvPosition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -75,9 +81,9 @@
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"
- tools:background="@android:color/holo_green_dark" />
+ tools:background="@android:color/holo_green_dark"/>
- <TextView
+ <TextView
android:id="@+id/txtvLength"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -88,18 +94,19 @@
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"
- tools:background="@android:color/holo_green_dark" />
+ android:background="?android:attr/selectableItemBackground"
+ tools:background="@android:color/holo_green_dark"/>
- </RelativeLayout>
+ </RelativeLayout>
- <RelativeLayout
+ <RelativeLayout
android:id="@+id/player_control"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
tools:background="@android:color/holo_purple">
- <ImageButton
+ <ImageButton
android:id="@+id/butPlay"
android:layout_width="@dimen/audioplayer_playercontrols_length_big"
android:layout_height="@dimen/audioplayer_playercontrols_length_big"
@@ -115,9 +122,9 @@
app:srcCompat="?attr/av_play"
android:scaleType="fitCenter"
tools:srcCompat="@drawable/ic_av_play_white_24dp"
- tools:background="@android:color/holo_green_dark" />
+ tools:background="@android:color/holo_green_dark"/>
- <de.danoeh.antennapod.view.CircularProgressBar
+ <de.danoeh.antennapod.view.CircularProgressBar
android:layout_width="@dimen/audioplayer_playercontrols_length_big"
android:layout_height="@dimen/audioplayer_playercontrols_length_big"
android:layout_marginLeft="16dp"
@@ -125,9 +132,18 @@
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:layout_centerHorizontal="true"
- android:layout_centerVertical="true" />
+ android:layout_centerVertical="true"/>
+
+ <ProgressBar
+ style="?android:attr/progressBarStyle"
+ android:layout_width="@dimen/audioplayer_playercontrols_length_big"
+ android:layout_height="@dimen/audioplayer_playercontrols_length_big"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:id="@+id/progLoading"
+ android:visibility="gone"/>
- <ImageButton
+ <ImageButton
android:id="@+id/butRev"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
@@ -141,9 +157,9 @@
app:srcCompat="?attr/av_rewind"
android:scaleType="fitCenter"
tools:srcCompat="@drawable/ic_av_fast_rewind_white_48dp"
- tools:background="@android:color/holo_blue_dark" />
+ tools:background="@android:color/holo_blue_dark"/>
- <TextView
+ <TextView
android:id="@+id/txtvRev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -156,9 +172,9 @@
android:text="30"
android:textSize="12sp"
android:textColor="?android:attr/textColorSecondary"
- android:clickable="false" />
+ android:clickable="false"/>
- <de.danoeh.antennapod.view.PlaybackSpeedIndicatorView
+ <de.danoeh.antennapod.view.PlaybackSpeedIndicatorView
android:id="@+id/butPlaybackSpeed"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
@@ -169,9 +185,9 @@
android:contentDescription="@string/set_playback_speed_label"
tools:srcCompat="@drawable/ic_playback_speed_white"
tools:visibility="gone"
- tools:background="@android:color/holo_green_dark" />
+ tools:background="@android:color/holo_green_dark"/>
- <TextView
+ <TextView
android:id="@+id/txtvPlaybackSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -184,9 +200,9 @@
android:text="1.00"
android:textSize="12sp"
android:textColor="?android:attr/textColorSecondary"
- android:clickable="false" />
+ android:clickable="false"/>
- <ImageButton
+ <ImageButton
android:id="@+id/butFF"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
@@ -200,9 +216,9 @@
app:srcCompat="?attr/av_fast_forward"
android:scaleType="fitCenter"
tools:srcCompat="@drawable/ic_av_fast_forward_white_48dp"
- tools:background="@android:color/holo_blue_dark" />
+ tools:background="@android:color/holo_blue_dark"/>
- <TextView
+ <TextView
android:id="@+id/txtvFF"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -215,9 +231,9 @@
android:text="30"
android:textSize="12sp"
android:textColor="?android:attr/textColorSecondary"
- android:clickable="false" />
+ android:clickable="false"/>
- <ImageButton
+ <ImageButton
android:id="@+id/butSkip"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
@@ -229,20 +245,9 @@
app:srcCompat="?attr/av_skip"
android:contentDescription="@string/skip_episode_label"
tools:srcCompat="@drawable/ic_av_skip_white_48dp"
- tools:background="@android:color/holo_green_dark" />
- </RelativeLayout>
-
- </LinearLayout>
+ tools:background="@android:color/holo_green_dark"/>
+ </RelativeLayout>
- </RelativeLayout>
-
- <FrameLayout
- android:id="@+id/navDrawerFragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginEnd="24dp"
- android:layout_marginRight="24dp"
- android:layout_gravity="start"
- android:orientation="vertical" />
+ </LinearLayout>
-</androidx.drawerlayout.widget.DrawerLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/app/src/main/res/layout/external_player_fragment.xml b/app/src/main/res/layout/external_player_fragment.xml
index 3e2efe47e..651953b0e 100644
--- a/app/src/main/res/layout/external_player_fragment.xml
+++ b/app/src/main/res/layout/external_player_fragment.xml
@@ -1,87 +1,84 @@
<?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/fragmentLayout"
- android:layout_width="match_parent"
- android:layout_height="@dimen/external_player_height"
- android:visibility="gone"
- android:background="?attr/selectableItemBackground">
-
- <ImageView
- android:id="@+id/imgvCover"
- android:contentDescription="@string/cover_label"
- android:layout_width="@dimen/external_player_height"
+<LinearLayout
+ 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/fragmentLayout"
+ android:layout_width="match_parent"
android:layout_height="@dimen/external_player_height"
- android:adjustViewBounds="true"
- android:cropToPadding="true"
- android:scaleType="centerCrop"
- tools:src="@drawable/ic_drag_vertical_white_48dp"
- tools:background="@android:color/holo_green_dark"
- android:transitionName="coverTransition"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"/>
+ android:background="?attr/selectableItemBackground"
+ android:orientation="vertical">
- <ProgressBar
- android:id="@+id/episodeProgress"
- android:layout_width="match_parent"
- android:layout_height="4dp"
- android:layout_toRightOf="@id/imgvCover"
- android:layout_toEndOf="@id/imgvCover"
- android:layout_alignParentTop="true"
- style="?attr/progressBarTheme"
- android:indeterminate="false"
- tools:progress="100"/>
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/dividerHorizontal" />
- <ImageButton
- android:id="@+id/butPlay"
- android:layout_width="52dp"
- android:layout_height="52dp"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"
- android:layout_below="@id/episodeProgress"
- android:layout_centerVertical="true"
- android:contentDescription="@string/pause_label"
- android:background="?attr/selectableItemBackground"
- app:srcCompat="?attr/av_play"
- android:scaleType="fitCenter"
- android:padding="8dp"
- tools:src="@drawable/ic_play_arrow_white_36dp"/>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:gravity="center_vertical"
+ android:layout_weight="1">
+
+ <ImageView
+ android:id="@+id/imgvCover"
+ android:contentDescription="@string/cover_label"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:adjustViewBounds="true"
+ android:cropToPadding="true"
+ android:scaleType="centerCrop"
+ tools:src="@tools:sample/avatars"/>
- <TextView
- android:id="@+id/txtvTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="26dp"
- android:layout_toRightOf="@id/imgvCover"
- android:layout_toEndOf="@id/imgvCover"
- android:layout_marginLeft="16dp"
- android:layout_marginStart="16dp"
- android:layout_toLeftOf="@id/butPlay"
- android:layout_toStartOf="@id/butPlay"
- style="@style/Base.TextAppearance.AppCompat.Body1"
- android:ellipsize="end"
- android:maxLines="1"
- tools:text="Episode title that is too long and will cause the text to wrap"/>
- <TextView
- android:id="@+id/txtvAuthor"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/episodeProgress"
- android:layout_marginTop="26dp"
- android:layout_toRightOf="@id/imgvCover"
- android:layout_toEndOf="@id/imgvCover"
- android:layout_marginLeft="16dp"
- android:layout_marginStart="16dp"
- android:layout_toLeftOf="@id/butPlay"
- android:layout_toStartOf="@id/butPlay"
- style="@style/TextAppearance.AppCompat.Body1"
- android:textColor="?android:attr/textColorSecondary"
- android:ellipsize="end"
- android:maxLines="1"
- tools:text="Episode author that is too long and will cause the text to wrap"/>
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp">
+
+ <TextView
+ android:id="@+id/txtvTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/Base.TextAppearance.AppCompat.Body1"
+ android:ellipsize="end"
+ android:maxLines="1"
+ tools:text="Episode title that is too long and will cause the text to wrap"/>
+
+ <TextView
+ android:id="@+id/txtvAuthor"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AppCompat.Body1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:ellipsize="end"
+ android:maxLines="1"
+ tools:text="Episode author that is too long and will cause the text to wrap"/>
+ </LinearLayout>
+
+
+ <ImageButton
+ android:id="@+id/butPlay"
+ android:layout_width="52dp"
+ android:layout_height="match_parent"
+ android:contentDescription="@string/pause_label"
+ android:background="?attr/selectableItemBackground"
+ app:srcCompat="?attr/av_play"
+ android:scaleType="fitCenter"
+ android:padding="8dp"
+ tools:src="@drawable/ic_play_arrow_white_36dp"/>
+
+ </LinearLayout>
+
+ <ProgressBar
+ android:id="@+id/episodeProgress"
+ android:layout_width="match_parent"
+ android:layout_height="4dp"
+ style="?attr/progressBarTheme"
+ android:indeterminate="false"
+ tools:progress="100"/>
-</RelativeLayout>
+</LinearLayout>
diff --git a/app/src/main/res/layout/item_description_fragment.xml b/app/src/main/res/layout/item_description_fragment.xml
new file mode 100644
index 000000000..96382eae3
--- /dev/null
+++ b/app/src/main/res/layout/item_description_fragment.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.core.widget.NestedScrollView
+xmlns:android="http://schemas.android.com/apk/res/android"
+android:layout_width="match_parent"
+android:layout_height="match_parent"
+android:fillViewport="false">
+ <de.danoeh.antennapod.view.ShownotesWebView
+ android:id="@+id/webview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+</androidx.core.widget.NestedScrollView> \ No newline at end of file
diff --git a/app/src/main/res/layout/main.xml b/app/src/main/res/layout/main.xml
index 89b7e0c47..60b9623bc 100644
--- a/app/src/main/res/layout/main.xml
+++ b/app/src/main/res/layout/main.xml
@@ -1,42 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/drawer_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <RelativeLayout
- android:id="@+id/content"
+ 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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <FrameLayout
- android:id="@+id/playerFragment"
+ <androidx.coordinatorlayout.widget.CoordinatorLayout
+ android:id="@+id/overview_coordinator_layout"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- tools:layout_height="64dp"
- tools:background="@android:color/holo_green_light" />
+ android:layout_height="match_parent">
<FrameLayout
android:id="@+id/main_view"
android:layout_width="match_parent"
- android:layout_height="0px"
- android:layout_above="@id/playerFragment"
+ android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:foreground="?android:windowContentOverlay"
+ android:layout_marginBottom="@dimen/external_player_height"
tools:background="@android:color/holo_red_dark" />
- </RelativeLayout>
+ <FrameLayout
+ android:elevation="8dp"
+ android:id="@+id/audioplayerFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?android:attr/windowBackground"
+ app:layout_behavior="de.danoeh.antennapod.view.LockableBottomSheetBehavior" />
+
+ </androidx.coordinatorlayout.widget.CoordinatorLayout>
<FrameLayout
- android:id="@+id/navDrawerFragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginEnd="24dp"
- android:layout_marginRight="24dp"
- android:layout_gravity="start"
- android:orientation="vertical" />
+ android:id="@+id/navDrawerFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="24dp"
+ android:layout_marginRight="24dp"
+ android:layout_gravity="start"
+ android:orientation="vertical" />
</androidx.drawerlayout.widget.DrawerLayout> \ No newline at end of file
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 e392a50c6..10678f556 100644
--- a/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ b/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
@@ -4,8 +4,6 @@ import android.content.SharedPreferences;
import android.media.AudioManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
-import androidx.annotation.CallSuper;
-import androidx.core.view.MenuItemCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
@@ -14,6 +12,7 @@ import android.view.MenuItem;
import com.google.android.gms.cast.ApplicationMetadata;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.cast.CastButtonVisibilityManager;
import de.danoeh.antennapod.core.cast.CastConsumer;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
@@ -21,6 +20,9 @@ import de.danoeh.antennapod.core.cast.SwitchableMediaRouteActionProvider;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Activity that allows for showing the MediaRouter button whenever there's a cast device in the
* network.
@@ -31,9 +33,7 @@ public abstract class CastEnabledActivity extends AppCompatActivity
private CastConsumer castConsumer;
private CastManager castManager;
-
- private SwitchableMediaRouteActionProvider mediaRouteActionProvider;
- private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
+ private final List<CastButtonVisibilityManager> castButtons = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -59,8 +59,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
};
castManager = CastManager.getInstance();
castManager.addCastConsumer(castConsumer);
+ CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager(castManager);
castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
onCastConnectionChanged(castManager.isConnected());
+ castButtons.add(castButtonVisibilityManager);
}
@Override
@@ -76,45 +78,14 @@ public abstract class CastEnabledActivity extends AppCompatActivity
}
@Override
- @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;
- }
-
- @Override
- @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());
- mediaRouteActionProvider = null;
- return true;
- }
- mediaRouteActionProvider = castManager.addMediaRouterButton(mediaRouteButton);
- if (mediaRouteActionProvider != null) {
- mediaRouteActionProvider.setEnabled(castButtonVisibilityManager.shouldEnable());
- }
- return true;
- }
-
- @Override
protected void onResume() {
super.onResume();
if (!CastManager.isInitialized()) {
return;
}
- castButtonVisibilityManager.setResumed(true);
+ for (CastButtonVisibilityManager castButton : castButtons) {
+ castButton.setResumed(true);
+ }
}
@Override
@@ -123,7 +94,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
if (!CastManager.isInitialized()) {
return;
}
- castButtonVisibilityManager.setResumed(false);
+ for (CastButtonVisibilityManager castButton : castButtons) {
+ castButton.setResumed(false);
+ }
}
@@ -132,7 +105,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
boolean newValue = UserPreferences.isCastEnabled();
Log.d(TAG, "onSharedPreferenceChanged(), isCastEnabled set to " + newValue);
- castButtonVisibilityManager.setPrefEnabled(newValue);
+ for (CastButtonVisibilityManager castButton : castButtons) {
+ castButton.setPrefEnabled(newValue);
+ }
// PlaybackService has its own listener, so if it's active we don't have to take action here.
if (!newValue && !PlaybackService.isRunning) {
CastManager.getInstance().disconnect();
@@ -142,129 +117,41 @@ public abstract class CastEnabledActivity extends AppCompatActivity
private void onCastConnectionChanged(boolean connected) {
if (connected) {
- castButtonVisibilityManager.onConnected();
+ for (CastButtonVisibilityManager castButton : castButtons) {
+ castButton.onConnected();
+ }
setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
} else {
- castButtonVisibilityManager.onDisconnected();
+ for (CastButtonVisibilityManager castButton : castButtons) {
+ castButton.onDisconnected();
+ }
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
}
/**
* Should be called by any activity or fragment for which the cast button should be shown.
- *
- * @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
*/
- public final void requestCastButton(int showAsAction) {
+ public final void requestCastButton(Menu menu) {
if (!CastManager.isInitialized()) {
return;
}
- castButtonVisibilityManager.requestCastButton(showAsAction);
- }
-
- private class CastButtonVisibilityManager {
- private volatile boolean prefEnabled = false;
- private volatile boolean viewRequested = false;
- private volatile boolean resumed = false;
- private volatile boolean connected = false;
- private volatile int showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
- private Menu menu;
-
- public synchronized void setPrefEnabled(boolean newValue) {
- if (prefEnabled != newValue && resumed && (viewRequested || connected)) {
- if (newValue) {
- castManager.incrementUiCounter();
- } else {
- castManager.decrementUiCounter();
- }
- }
- prefEnabled = newValue;
- if (mediaRouteActionProvider != null) {
- mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
- }
- }
-
- public synchronized void setResumed(boolean newValue) {
- if (resumed == newValue) {
- Log.e(TAG, "resumed should never change to the same value");
- return;
- }
- resumed = newValue;
- if (prefEnabled && (viewRequested || connected)) {
- if (resumed) {
- castManager.incrementUiCounter();
- } else {
- castManager.decrementUiCounter();
- }
- }
- }
-
- public synchronized void setViewRequested(boolean newValue) {
- if (viewRequested != newValue && resumed && prefEnabled && !connected) {
- if (newValue) {
- castManager.incrementUiCounter();
- } else {
- castManager.decrementUiCounter();
- }
- }
- viewRequested = newValue;
- if (mediaRouteActionProvider != null) {
- mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
- }
- }
-
- public synchronized void setConnected(boolean newValue) {
- if (connected != newValue && resumed && prefEnabled && !prefEnabled) {
- if (newValue) {
- castManager.incrementUiCounter();
- } else {
- castManager.decrementUiCounter();
- }
- }
- connected = newValue;
- if (mediaRouteActionProvider != null) {
- mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
- }
- }
-
- public synchronized boolean shouldEnable() {
- return prefEnabled && viewRequested;
- }
- public void setMenu(Menu menu) {
- setViewRequested(false);
- showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
- this.menu = menu;
- setShowAsAction();
- }
-
- public void requestCastButton(int showAsAction) {
- setViewRequested(true);
- this.showAsAction = showAsAction;
- setShowAsAction();
- }
-
- public void onConnected() {
- setConnected(true);
- setShowAsAction();
- }
-
- public void onDisconnected() {
- setConnected(false);
- setShowAsAction();
+ MenuItem mediaRouteButton = menu.findItem(R.id.media_route_menu_item);
+ if (mediaRouteButton == null) {
+ getMenuInflater().inflate(R.menu.cast_enabled, menu);
+ mediaRouteButton = menu.findItem(R.id.media_route_menu_item);
}
- private void setShowAsAction() {
- if (menu == null) {
- Log.d(TAG, "setShowAsAction() without a menu");
- return;
- }
- MenuItem item = menu.findItem(R.id.media_route_menu_item);
- if (item == null) {
- Log.e(TAG, "setShowAsAction(), but cast button not inflated");
- return;
- }
- MenuItemCompat.setShowAsAction(item, connected? MenuItem.SHOW_AS_ACTION_ALWAYS : showAsAction);
- }
+ SwitchableMediaRouteActionProvider mediaRouteActionProvider =
+ CastManager.getInstance().addMediaRouterButton(mediaRouteButton);
+ CastButtonVisibilityManager castButtonVisibilityManager =
+ new CastButtonVisibilityManager(CastManager.getInstance());
+ castButtonVisibilityManager.setMenu(menu);
+ castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
+ castButtonVisibilityManager.mediaRouteActionProvider = mediaRouteActionProvider;
+ castButtonVisibilityManager.setResumed(true);
+ castButtonVisibilityManager.requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ mediaRouteActionProvider.setEnabled(castButtonVisibilityManager.shouldEnable());
}
}