Merge pull request #3954 from ByteHamster/audioplayer-fragment
Convert AudioPlayer to fragment
This commit is contained in:
commit
b5836650d1
|
@ -32,7 +32,9 @@ import java.util.concurrent.TimeoutException;
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
|
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.isRoot;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
@ -193,4 +195,8 @@ public class EspressoTestUtils {
|
||||||
}
|
}
|
||||||
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Matcher<View> actionBarOverflow() {
|
||||||
|
return allOf(isDisplayed(), withContentDescription("More options"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package de.test.antennapod.ui;
|
package de.test.antennapod.ui;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.view.View;
|
||||||
import androidx.test.espresso.Espresso;
|
import androidx.test.espresso.Espresso;
|
||||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
@ -8,6 +9,7 @@ import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||||
import de.test.antennapod.EspressoTestUtils;
|
import de.test.antennapod.EspressoTestUtils;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
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.withClassName;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
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.allOf;
|
||||||
import static org.hamcrest.CoreMatchers.endsWith;
|
import static org.hamcrest.CoreMatchers.endsWith;
|
||||||
|
|
||||||
|
@ -48,14 +51,14 @@ public class QueueFragmentTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSortEmptyQueue() {
|
public void testSortEmptyQueue() {
|
||||||
Espresso.openContextualActionModeOverflowMenu();
|
onView(first(EspressoTestUtils.actionBarOverflow())).perform(click());
|
||||||
onView(withText(R.string.sort)).perform(click());
|
onView(withText(R.string.sort)).perform(click());
|
||||||
onView(withText(R.string.random)).perform(click());
|
onView(withText(R.string.random)).perform(click());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeepEmptyQueueSorted() {
|
public void testKeepEmptyQueueSorted() {
|
||||||
Espresso.openContextualActionModeOverflowMenu();
|
onView(first(EspressoTestUtils.actionBarOverflow())).perform(click());
|
||||||
onView(withText(R.string.sort)).perform(click());
|
onView(withText(R.string.sort)).perform(click());
|
||||||
onView(withText(R.string.keep_sorted)).perform(click());
|
onView(withText(R.string.keep_sorted)).perform(click());
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,14 @@ import android.preference.PreferenceManager;
|
||||||
import androidx.test.rule.ActivityTestRule;
|
import androidx.test.rule.ActivityTestRule;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
import de.danoeh.antennapod.R;
|
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.feed.FeedItem;
|
||||||
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
||||||
import de.danoeh.antennapod.core.storage.DBReader;
|
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.danoeh.antennapod.fragment.QueueFragment;
|
||||||
import de.test.antennapod.EspressoTestUtils;
|
import de.test.antennapod.EspressoTestUtils;
|
||||||
import de.test.antennapod.IgnoreOnCi;
|
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.espresso.matcher.ViewMatchers.withText;
|
||||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||||
import static de.test.antennapod.EspressoTestUtils.waitForView;
|
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.
|
* User interface tests for changing the playback speed.
|
||||||
|
@ -43,10 +47,10 @@ import static de.test.antennapod.EspressoTestUtils.waitForView;
|
||||||
public class SpeedChangeTest {
|
public class SpeedChangeTest {
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ActivityTestRule<AudioplayerActivity> activityRule
|
public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class, false, false);
|
||||||
= new ActivityTestRule<>(AudioplayerActivity.class, false, false);
|
|
||||||
private UITestUtils uiTestUtils;
|
private UITestUtils uiTestUtils;
|
||||||
private String[] availableSpeeds;
|
private String[] availableSpeeds;
|
||||||
|
private PlaybackController controller;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
@ -70,7 +74,10 @@ public class SpeedChangeTest {
|
||||||
UserPreferences.setPlaybackSpeedArray(availableSpeeds);
|
UserPreferences.setPlaybackSpeedArray(availableSpeeds);
|
||||||
|
|
||||||
EspressoTestUtils.tryKillPlaybackService();
|
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
|
@After
|
||||||
|
@ -86,21 +93,21 @@ public class SpeedChangeTest {
|
||||||
@Test
|
@Test
|
||||||
public void testChangeSpeedPlaying() {
|
public void testChangeSpeedPlaying() {
|
||||||
onView(isRoot()).perform(waitForView(withId(R.id.butPlay), 1000));
|
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(()
|
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
|
||||||
-> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PLAYING);
|
-> controller.getStatus() == PlayerStatus.PLAYING);
|
||||||
clickThroughSpeeds();
|
clickThroughSpeeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testChangeSpeedPaused() {
|
public void testChangeSpeedPaused() {
|
||||||
onView(isRoot()).perform(waitForView(withId(R.id.butPlay), 1000));
|
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(()
|
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
|
||||||
-> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PLAYING);
|
-> controller.getStatus() == PlayerStatus.PLAYING);
|
||||||
onView(withId(R.id.butPlay)).perform(click());
|
controller.playPause();
|
||||||
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
|
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
|
||||||
-> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PAUSED);
|
-> controller.getStatus() == PlayerStatus.PAUSED);
|
||||||
clickThroughSpeeds();
|
clickThroughSpeeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ package de.danoeh.antennapod.activity;
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
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
|
* Activity that allows for showing the MediaRouter button whenever there's a cast device in the
|
||||||
* network.
|
* network.
|
||||||
|
@ -9,7 +11,7 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||||
public abstract class CastEnabledActivity extends AppCompatActivity {
|
public abstract class CastEnabledActivity extends AppCompatActivity {
|
||||||
public static final String TAG = "CastEnabledActivity";
|
public static final String TAG = "CastEnabledActivity";
|
||||||
|
|
||||||
public final void requestCastButton(int showAsAction) {
|
public final void requestCastButton(Menu menu) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,31 +73,6 @@
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
</activity>
|
</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
|
<activity
|
||||||
android:name=".activity.DownloadAuthenticationActivity"
|
android:name=".activity.DownloadAuthenticationActivity"
|
||||||
android:launchMode="singleInstance"/>
|
android:launchMode="singleInstance"/>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,6 +23,7 @@ import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.core.event.MessageEvent;
|
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.core.util.StorageUtils;
|
||||||
import de.danoeh.antennapod.dialog.RatingDialog;
|
import de.danoeh.antennapod.dialog.RatingDialog;
|
||||||
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
||||||
|
import de.danoeh.antennapod.fragment.AudioPlayerFragment;
|
||||||
import de.danoeh.antennapod.fragment.DownloadsFragment;
|
import de.danoeh.antennapod.fragment.DownloadsFragment;
|
||||||
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
||||||
import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
|
|
||||||
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
||||||
import de.danoeh.antennapod.fragment.NavDrawerFragment;
|
import de.danoeh.antennapod.fragment.NavDrawerFragment;
|
||||||
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
|
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.SubscriptionFragment;
|
||||||
import de.danoeh.antennapod.fragment.TransitionEffect;
|
import de.danoeh.antennapod.fragment.TransitionEffect;
|
||||||
import de.danoeh.antennapod.preferences.PreferenceUpgrader;
|
import de.danoeh.antennapod.preferences.PreferenceUpgrader;
|
||||||
|
import de.danoeh.antennapod.view.LockableBottomSheetBehavior;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.greenrobot.eventbus.EventBus;
|
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_TAG = "fragment_tag";
|
||||||
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
|
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
|
||||||
public static final String EXTRA_FEED_ID = "fragment_feed_id";
|
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 static final String SAVE_BACKSTACK_COUNT = "backstackCount";
|
||||||
|
|
||||||
private DrawerLayout drawerLayout;
|
private DrawerLayout drawerLayout;
|
||||||
private View navDrawer;
|
private View navDrawer;
|
||||||
private ActionBarDrawerToggle drawerToggle;
|
private ActionBarDrawerToggle drawerToggle;
|
||||||
|
private LockableBottomSheetBehavior sheetBehavior;
|
||||||
private long lastBackButtonPressTime = 0;
|
private long lastBackButtonPressTime = 0;
|
||||||
|
|
||||||
@NonNull
|
@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();
|
NavDrawerFragment navDrawerFragment = new NavDrawerFragment();
|
||||||
transaction.replace(R.id.navDrawerFragment, navDrawerFragment, NavDrawerFragment.TAG);
|
transaction.replace(R.id.navDrawerFragment, navDrawerFragment, NavDrawerFragment.TAG);
|
||||||
|
AudioPlayerFragment audioPlayerFragment = new AudioPlayerFragment();
|
||||||
|
transaction.replace(R.id.audioplayerFragment, audioPlayerFragment, AudioPlayerFragment.TAG);
|
||||||
transaction.commit();
|
transaction.commit();
|
||||||
|
|
||||||
checkFirstLaunch();
|
checkFirstLaunch();
|
||||||
PreferenceUpgrader.checkUpgrades(this);
|
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
|
@Override
|
||||||
|
@ -151,6 +174,10 @@ public class MainActivity extends CastEnabledActivity {
|
||||||
return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer);
|
return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LockableBottomSheetBehavior getBottomSheet() {
|
||||||
|
return sheetBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
public void loadFragment(String tag, Bundle args) {
|
public void loadFragment(String tag, Bundle args) {
|
||||||
Log.d(TAG, "loadFragment(tag: " + tag + ", args: " + args + ")");
|
Log.d(TAG, "loadFragment(tag: " + tag + ", args: " + args + ")");
|
||||||
Fragment fragment;
|
Fragment fragment;
|
||||||
|
@ -301,29 +328,6 @@ public class MainActivity extends CastEnabledActivity {
|
||||||
Glide.get(this).clearMemory();
|
Glide.get(this).clearMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
if (drawerToggle.onOptionsItemSelected(item)) {
|
if (drawerToggle.onOptionsItemSelected(item)) {
|
||||||
|
@ -342,6 +346,8 @@ public class MainActivity extends CastEnabledActivity {
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (isDrawerOpen()) {
|
if (isDrawerOpen()) {
|
||||||
drawerLayout.closeDrawer(navDrawer);
|
drawerLayout.closeDrawer(navDrawer);
|
||||||
|
} else if (sheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
|
||||||
|
sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||||
} else if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
|
} else if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
} else {
|
} else {
|
||||||
|
@ -400,9 +406,11 @@ public class MainActivity extends CastEnabledActivity {
|
||||||
} else if (feedId > 0) {
|
} else if (feedId > 0) {
|
||||||
loadFeedFragmentById(feedId, args);
|
loadFeedFragmentById(feedId, args);
|
||||||
}
|
}
|
||||||
// to avoid handling the intent twice when the configuration changes
|
} else if (intent.hasExtra(EXTRA_OPEN_PLAYER)) {
|
||||||
setIntent(new Intent(MainActivity.this, MainActivity.class));
|
sheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||||
}
|
}
|
||||||
|
// to avoid handling the intent twice when the configuration changes
|
||||||
|
setIntent(new Intent(MainActivity.this, MainActivity.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -2,7 +2,6 @@ package de.danoeh.antennapod.activity;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
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.PlaybackController;
|
||||||
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
|
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
|
||||||
import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
|
import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
|
||||||
|
import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
|
||||||
import de.danoeh.antennapod.dialog.SleepTimerDialog;
|
import de.danoeh.antennapod.dialog.SleepTimerDialog;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
@ -140,16 +140,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
||||||
return butPlay;
|
return butPlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postStatusMsg(int msg, boolean showToast) {
|
|
||||||
MediaplayerActivity.this.postStatusMsg(msg, showToast);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearStatusMsg() {
|
|
||||||
MediaplayerActivity.this.clearStatusMsg();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean loadMediaInfo() {
|
public boolean loadMediaInfo() {
|
||||||
return MediaplayerActivity.this.loadMediaInfo();
|
return MediaplayerActivity.this.loadMediaInfo();
|
||||||
|
@ -198,13 +188,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
||||||
onPositionObserverUpdate();
|
onPositionObserverUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TextView getTxtvFFFromActivity(MediaplayerActivity activity) {
|
|
||||||
return activity.txtvFF;
|
|
||||||
}
|
|
||||||
private static TextView getTxtvRevFromActivity(MediaplayerActivity activity) {
|
|
||||||
return activity.txtvRev;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onSetSpeedAbilityChanged() {
|
private void onSetSpeedAbilityChanged() {
|
||||||
Log.d(TAG, "onSetSpeedAbilityChanged()");
|
Log.d(TAG, "onSetSpeedAbilityChanged()");
|
||||||
updatePlaybackSpeedButton();
|
updatePlaybackSpeedButton();
|
||||||
|
@ -258,12 +241,16 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
||||||
* Should be used to inform the user that the PlaybackService is currently
|
* Should be used to inform the user that the PlaybackService is currently
|
||||||
* buffering.
|
* buffering.
|
||||||
*/
|
*/
|
||||||
protected abstract void onBufferStart();
|
protected void onBufferStart() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be used to hide the view that was showing the 'buffering'-message.
|
* 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) {
|
private void onBufferUpdate(float progress) {
|
||||||
if (sbPosition != null) {
|
if (sbPosition != null) {
|
||||||
|
@ -311,9 +298,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
super.onCreateOptionsMenu(menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
if (Flavors.FLAVOR == Flavors.PLAY) {
|
requestCastButton(menu);
|
||||||
requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
|
||||||
}
|
|
||||||
MenuInflater inflater = getMenuInflater();
|
MenuInflater inflater = getMenuInflater();
|
||||||
inflater.inflate(R.menu.mediaplayer, menu);
|
inflater.inflate(R.menu.mediaplayer, menu);
|
||||||
return true;
|
return true;
|
||||||
|
@ -478,10 +463,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
||||||
*/
|
*/
|
||||||
protected abstract void onAwaitingVideoSurface();
|
protected abstract void onAwaitingVideoSurface();
|
||||||
|
|
||||||
protected abstract void postStatusMsg(int resId, boolean showToast);
|
|
||||||
|
|
||||||
protected abstract void clearStatusMsg();
|
|
||||||
|
|
||||||
void onPositionObserverUpdate() {
|
void onPositionObserverUpdate() {
|
||||||
if (controller == null || txtvPosition == null || txtvLength == null) {
|
if (controller == null || txtvPosition == null || txtvLength == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -543,92 +524,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
||||||
// Only meaningful on AudioplayerActivity, where it is overridden.
|
// 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() {
|
void setupGUI() {
|
||||||
setContentView(getContentViewResourceId());
|
setContentView(getContentViewResourceId());
|
||||||
sbPosition = findViewById(R.id.sbPosition);
|
sbPosition = findViewById(R.id.sbPosition);
|
||||||
|
@ -688,7 +583,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
||||||
if (butRev != null) {
|
if (butRev != null) {
|
||||||
butRev.setOnClickListener(v -> onRewind());
|
butRev.setOnClickListener(v -> onRewind());
|
||||||
butRev.setOnLongClickListener(v -> {
|
butRev.setOnLongClickListener(v -> {
|
||||||
showSkipPreference(MediaplayerActivity.this, SkipDirection.SKIP_REWIND);
|
SkipPreferenceDialog.showSkipPreference(MediaplayerActivity.this,
|
||||||
|
SkipPreferenceDialog.SkipDirection.SKIP_REWIND, txtvRev);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -698,7 +594,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
||||||
if (butFF != null) {
|
if (butFF != null) {
|
||||||
butFF.setOnClickListener(v -> onFastForward());
|
butFF.setOnClickListener(v -> onFastForward());
|
||||||
butFF.setOnLongClickListener(v -> {
|
butFF.setOnLongClickListener(v -> {
|
||||||
showSkipPreference(MediaplayerActivity.this, SkipDirection.SKIP_FORWARD);
|
SkipPreferenceDialog.showSkipPreference(MediaplayerActivity.this,
|
||||||
|
SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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) -> {
|
private final View.OnTouchListener onVideoviewTouched = (v, event) -> {
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
if (PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
if (PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
||||||
|
@ -292,16 +278,11 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO) {
|
if (notificationCode == PlaybackService.EXTRA_CODE_CAST) {
|
||||||
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) {
|
|
||||||
Log.d(TAG, "ReloadNotification received, switching to Castplayer now");
|
Log.d(TAG, "ReloadNotification received, switching to Castplayer now");
|
||||||
destroyingDueToReload = true;
|
destroyingDueToReload = true;
|
||||||
finish();
|
finish();
|
||||||
startActivity(new Intent(this, CastplayerActivity.class));
|
startActivity(new Intent(this, MainActivity.class).putExtra(MainActivity.EXTRA_OPEN_PLAYER, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,37 +5,34 @@ import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import com.bumptech.glide.load.resource.bitmap.FitCenter;
|
import com.bumptech.glide.load.resource.bitmap.FitCenter;
|
||||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
||||||
import com.bumptech.glide.request.RequestOptions;
|
import com.bumptech.glide.request.RequestOptions;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.core.feed.Chapter;
|
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.glide.ApGlideSettings;
|
||||||
import de.danoeh.antennapod.core.util.ChapterUtils;
|
|
||||||
import de.danoeh.antennapod.core.util.Converter;
|
import de.danoeh.antennapod.core.util.Converter;
|
||||||
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
|
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
|
||||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
import de.danoeh.antennapod.core.util.ThemeUtils;
|
||||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||||
|
|
||||||
public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
|
public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapter.ChapterHolder> {
|
||||||
private static final String TAG = "ChapterListAdapter";
|
|
||||||
|
|
||||||
private Playable media;
|
private Playable media;
|
||||||
private final Callback callback;
|
private final Callback callback;
|
||||||
|
private final Context context;
|
||||||
private int currentChapterIndex = -1;
|
private int currentChapterIndex = -1;
|
||||||
private boolean hasImages = false;
|
private boolean hasImages = false;
|
||||||
|
|
||||||
public ChaptersListAdapter(Context context, int textViewResourceId, Callback callback) {
|
public ChaptersListAdapter(Context context, Callback callback) {
|
||||||
super(context, textViewResourceId);
|
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMedia(Playable media) {
|
public void setMedia(Playable media) {
|
||||||
|
@ -51,34 +48,10 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
@Override
|
||||||
public View getView(final int position, View convertView, @NonNull ViewGroup parent) {
|
public void onBindViewHolder(@NonNull ChapterHolder holder, int position) {
|
||||||
Holder holder;
|
|
||||||
|
|
||||||
Chapter sc = getItem(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.title.setText(sc.getTitle());
|
||||||
holder.start.setText(Converter.getDurationStringLong((int) sc
|
holder.start.setText(Converter.getDurationStringLong((int) sc
|
||||||
.getStart()));
|
.getStart()));
|
||||||
|
@ -89,7 +62,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
|
||||||
} else {
|
} else {
|
||||||
duration = media.getDuration() - sc.getStart();
|
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)));
|
Converter.getDurationStringLong((int) duration)));
|
||||||
|
|
||||||
if (sc.getLink() == null) {
|
if (sc.getLink() == null) {
|
||||||
|
@ -97,9 +70,9 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
|
||||||
} else {
|
} else {
|
||||||
holder.link.setVisibility(View.VISIBLE);
|
holder.link.setVisibility(View.VISIBLE);
|
||||||
holder.link.setText(sc.getLink());
|
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 -> {
|
holder.secondaryActionButton.setOnClickListener(v -> {
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
callback.onPlayChapterButtonClicked(position);
|
callback.onPlayChapterButtonClicked(position);
|
||||||
|
@ -107,46 +80,40 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (position == currentChapterIndex) {
|
if (position == currentChapterIndex) {
|
||||||
int playingBackGroundColor = ThemeUtils.getColorFromAttr(getContext(), R.attr.currently_playing_background);
|
int playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background);
|
||||||
holder.view.setBackgroundColor(playingBackGroundColor);
|
holder.itemView.setBackgroundColor(playingBackGroundColor);
|
||||||
} else {
|
} else {
|
||||||
holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
|
holder.itemView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.transparent));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasImages) {
|
if (hasImages) {
|
||||||
holder.image.setVisibility(View.VISIBLE);
|
holder.image.setVisibility(View.VISIBLE);
|
||||||
if (TextUtils.isEmpty(sc.getImageUrl())) {
|
if (TextUtils.isEmpty(sc.getImageUrl())) {
|
||||||
Glide.with(getContext()).clear(holder.image);
|
Glide.with(context).clear(holder.image);
|
||||||
} else {
|
} else {
|
||||||
Glide.with(getContext())
|
Glide.with(context)
|
||||||
.load(EmbeddedChapterImage.getModelFor(media, position))
|
.load(EmbeddedChapterImage.getModelFor(media, position))
|
||||||
.apply(new RequestOptions()
|
.apply(new RequestOptions()
|
||||||
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
|
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
|
||||||
.dontAnimate()
|
.dontAnimate()
|
||||||
.transforms(new FitCenter(), new RoundedCorners((int)
|
.transforms(new FitCenter(), new RoundedCorners((int)
|
||||||
(4 * getContext().getResources().getDisplayMetrics().density))))
|
(4 * context.getResources().getDisplayMetrics().density))))
|
||||||
.into(holder.image);
|
.into(holder.image);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
holder.image.setVisibility(View.GONE);
|
holder.image.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return convertView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Holder {
|
@NonNull
|
||||||
View view;
|
@Override
|
||||||
TextView title;
|
public ChapterHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
TextView start;
|
LayoutInflater inflater = LayoutInflater.from(context);
|
||||||
TextView link;
|
return new ChapterHolder(inflater.inflate(R.layout.simplechapter_item, parent, false));
|
||||||
TextView duration;
|
|
||||||
ImageView image;
|
|
||||||
View secondaryActionButton;
|
|
||||||
ImageView secondaryActionIcon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public int getItemCount() {
|
||||||
if (media == null || media.getChapters() == null) {
|
if (media == null || media.getChapters() == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -160,6 +127,27 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
|
||||||
return counter;
|
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) {
|
public void notifyChapterChanged(int newChapterIndex) {
|
||||||
currentChapterIndex = newChapterIndex;
|
currentChapterIndex = newChapterIndex;
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
|
@ -169,7 +157,6 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
|
||||||
return media.getDuration() > 0 && media.getDuration() < c.getStart();
|
return media.getDuration() > 0 && media.getDuration() < c.getStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Chapter getItem(int position) {
|
public Chapter getItem(int position) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Chapter chapter : media.getChapters()) {
|
for (Chapter chapter : media.getChapters()) {
|
||||||
|
@ -181,7 +168,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.getItem(position);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
|
|
|
@ -4,27 +4,22 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.AudioplayerActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.activity.CastplayerActivity;
|
|
||||||
import de.danoeh.antennapod.activity.VideoplayerActivity;
|
import de.danoeh.antennapod.activity.VideoplayerActivity;
|
||||||
import de.danoeh.antennapod.core.PlaybackServiceCallbacks;
|
import de.danoeh.antennapod.core.PlaybackServiceCallbacks;
|
||||||
import de.danoeh.antennapod.core.feed.MediaType;
|
import de.danoeh.antennapod.core.feed.MediaType;
|
||||||
|
|
||||||
|
|
||||||
public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
|
public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
|
||||||
@Override
|
@Override
|
||||||
public Intent getPlayerActivityIntent(Context context, MediaType mediaType, boolean remotePlayback) {
|
public Intent getPlayerActivityIntent(Context context, MediaType mediaType, boolean remotePlayback) {
|
||||||
if (remotePlayback) {
|
if (mediaType == MediaType.AUDIO || remotePlayback) {
|
||||||
return new Intent(context, CastplayerActivity.class);
|
return new Intent(context, MainActivity.class).putExtra(MainActivity.EXTRA_OPEN_PLAYER, true);
|
||||||
}
|
} else {
|
||||||
if (mediaType == MediaType.VIDEO) {
|
|
||||||
Intent i = new Intent(context, VideoplayerActivity.class);
|
Intent i = new Intent(context, VideoplayerActivity.class);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
i.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
} else {
|
|
||||||
return new Intent(context, AudioplayerActivity.class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -313,7 +313,7 @@ public class EpisodesApplyActionFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (resId != 0) {
|
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();
|
.show();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -469,7 +469,7 @@ public class EpisodesApplyActionFragment extends Fragment {
|
||||||
|
|
||||||
private void close(@PluralsRes int msgId, int numItems) {
|
private void close(@PluralsRes int msgId, int numItems) {
|
||||||
if (numItems > 0) {
|
if (numItems > 0) {
|
||||||
Snackbar.make(getActivity().findViewById(R.id.content),
|
Snackbar.make(getActivity().findViewById(android.R.id.content),
|
||||||
getResources().getQuantityString(msgId, numItems, numItems),
|
getResources().getQuantityString(msgId, numItems, numItems),
|
||||||
Snackbar.LENGTH_LONG
|
Snackbar.LENGTH_LONG
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,24 +1,24 @@
|
||||||
package de.danoeh.antennapod.fragment;
|
package de.danoeh.antennapod.fragment;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.fragment.app.ListFragment;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ListView;
|
import android.view.ViewGroup;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import de.danoeh.antennapod.core.util.ChapterUtils;
|
import androidx.annotation.Nullable;
|
||||||
import java.util.List;
|
import androidx.fragment.app.Fragment;
|
||||||
import java.util.ListIterator;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
|
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
|
||||||
import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
|
|
||||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||||
import de.danoeh.antennapod.core.feed.Chapter;
|
import de.danoeh.antennapod.core.feed.Chapter;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
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.Playable;
|
||||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||||
|
|
||||||
import de.danoeh.antennapod.view.EmptyViewHandler;
|
import de.danoeh.antennapod.view.EmptyViewHandler;
|
||||||
import io.reactivex.Maybe;
|
import io.reactivex.Maybe;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
@ -28,38 +28,43 @@ import org.greenrobot.eventbus.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
|
|
||||||
public class ChaptersFragment extends ListFragment {
|
public class ChaptersFragment extends Fragment {
|
||||||
private static final String TAG = "ChaptersFragment";
|
private static final String TAG = "ChaptersFragment";
|
||||||
private ChaptersListAdapter adapter;
|
private ChaptersListAdapter adapter;
|
||||||
private PlaybackController controller;
|
private PlaybackController controller;
|
||||||
private Disposable disposable;
|
private Disposable disposable;
|
||||||
private int focusedChapter = -1;
|
private int focusedChapter = -1;
|
||||||
private Playable media;
|
private Playable media;
|
||||||
|
private LinearLayoutManager layoutManager;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
View root = inflater.inflate(R.layout.simple_list_fragment, container, false);
|
||||||
// add padding
|
root.findViewById(R.id.toolbar).setVisibility(View.GONE);
|
||||||
final ListView lv = getListView();
|
RecyclerView recyclerView = root.findViewById(R.id.recyclerView);
|
||||||
lv.setClipToPadding(false);
|
layoutManager = new LinearLayoutManager(getActivity());
|
||||||
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
|
recyclerView.setLayoutManager(layoutManager);
|
||||||
lv.setPadding(0, vertPadding, 0, vertPadding);
|
recyclerView.setHasFixedSize(true);
|
||||||
|
recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
|
||||||
|
|
||||||
|
adapter = new ChaptersListAdapter(getActivity(), pos -> {
|
||||||
|
if (controller.getStatus() != PlayerStatus.PLAYING) {
|
||||||
|
controller.playPause();
|
||||||
|
}
|
||||||
|
Chapter chapter = adapter.getItem(pos);
|
||||||
|
controller.seekToChapter(chapter);
|
||||||
|
updateChapterSelection(pos);
|
||||||
|
});
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
EmptyViewHandler emptyView = new EmptyViewHandler(getContext());
|
EmptyViewHandler emptyView = new EmptyViewHandler(getContext());
|
||||||
emptyView.attachToListView(lv);
|
emptyView.attachToRecyclerView(recyclerView);
|
||||||
emptyView.setIcon(R.attr.ic_bookmark);
|
emptyView.setIcon(R.attr.ic_bookmark);
|
||||||
emptyView.setTitle(R.string.no_chapters_head_label);
|
emptyView.setTitle(R.string.no_chapters_head_label);
|
||||||
emptyView.setMessage(R.string.no_chapters_label);
|
emptyView.setMessage(R.string.no_chapters_label);
|
||||||
|
|
||||||
adapter = new ChaptersListAdapter(getActivity(), 0, pos -> {
|
return root;
|
||||||
if (controller.getStatus() != PlayerStatus.PLAYING) {
|
|
||||||
controller.playPause();
|
|
||||||
}
|
|
||||||
Chapter chapter = (Chapter) getListAdapter().getItem(pos);
|
|
||||||
controller.seekToChapter(chapter);
|
|
||||||
updateChapterSelection(pos);
|
|
||||||
});
|
|
||||||
setListAdapter(adapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -136,7 +141,6 @@ public class ChaptersFragment extends ListFragment {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
adapter.setMedia(media);
|
adapter.setMedia(media);
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
int positionOfCurrentChapter = getCurrentChapter(media);
|
int positionOfCurrentChapter = getCurrentChapter(media);
|
||||||
updateChapterSelection(positionOfCurrentChapter);
|
updateChapterSelection(positionOfCurrentChapter);
|
||||||
}
|
}
|
||||||
|
@ -149,9 +153,9 @@ public class ChaptersFragment extends ListFragment {
|
||||||
if (position != -1 && focusedChapter != position) {
|
if (position != -1 && focusedChapter != position) {
|
||||||
focusedChapter = position;
|
focusedChapter = position;
|
||||||
adapter.notifyChapterChanged(focusedChapter);
|
adapter.notifyChapterChanged(focusedChapter);
|
||||||
if (getListView().getFirstVisiblePosition() >= position
|
if (layoutManager.findFirstCompletelyVisibleItemPosition() >= position
|
||||||
|| getListView().getLastVisiblePosition() <= position) {
|
|| layoutManager.findLastCompletelyVisibleItemPosition() <= position) {
|
||||||
getListView().setSelectionFromTop(position, 100);
|
layoutManager.scrollToPositionWithOffset(position, 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package de.danoeh.antennapod.fragment;
|
package de.danoeh.antennapod.fragment;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.core.app.ActivityOptionsCompat;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -17,7 +15,9 @@ import android.widget.TextView;
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
|
|
||||||
import com.bumptech.glide.request.RequestOptions;
|
import com.bumptech.glide.request.RequestOptions;
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||||
import de.danoeh.antennapod.core.feed.MediaType;
|
import de.danoeh.antennapod.core.feed.MediaType;
|
||||||
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
||||||
|
@ -40,7 +40,6 @@ import org.greenrobot.eventbus.ThreadMode;
|
||||||
public class ExternalPlayerFragment extends Fragment {
|
public class ExternalPlayerFragment extends Fragment {
|
||||||
public static final String TAG = "ExternalPlayerFragment";
|
public static final String TAG = "ExternalPlayerFragment";
|
||||||
|
|
||||||
private ViewGroup fragmentLayout;
|
|
||||||
private ImageView imgvCover;
|
private ImageView imgvCover;
|
||||||
private TextView txtvTitle;
|
private TextView txtvTitle;
|
||||||
private ImageButton butPlay;
|
private ImageButton butPlay;
|
||||||
|
@ -56,26 +55,21 @@ public class ExternalPlayerFragment extends Fragment {
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
View root = inflater.inflate(R.layout.external_player_fragment,
|
View root = inflater.inflate(R.layout.external_player_fragment, container, false);
|
||||||
container, false);
|
|
||||||
fragmentLayout = root.findViewById(R.id.fragmentLayout);
|
|
||||||
imgvCover = root.findViewById(R.id.imgvCover);
|
imgvCover = root.findViewById(R.id.imgvCover);
|
||||||
txtvTitle = root.findViewById(R.id.txtvTitle);
|
txtvTitle = root.findViewById(R.id.txtvTitle);
|
||||||
butPlay = root.findViewById(R.id.butPlay);
|
butPlay = root.findViewById(R.id.butPlay);
|
||||||
mFeedName = root.findViewById(R.id.txtvAuthor);
|
mFeedName = root.findViewById(R.id.txtvAuthor);
|
||||||
mProgressBar = root.findViewById(R.id.episodeProgress);
|
mProgressBar = root.findViewById(R.id.episodeProgress);
|
||||||
|
|
||||||
fragmentLayout.setOnClickListener(v -> {
|
root.findViewById(R.id.fragmentLayout).setOnClickListener(v -> {
|
||||||
Log.d(TAG, "layoutInfo was clicked");
|
Log.d(TAG, "layoutInfo was clicked");
|
||||||
|
|
||||||
if (controller != null && controller.getMedia() != null) {
|
if (controller != null && controller.getMedia() != null) {
|
||||||
Intent intent = PlaybackService.getPlayerActivityIntent(getActivity(), controller.getMedia());
|
|
||||||
|
|
||||||
if (controller.getMedia().getMediaType() == MediaType.AUDIO) {
|
if (controller.getMedia().getMediaType() == MediaType.AUDIO) {
|
||||||
ActivityOptionsCompat options = ActivityOptionsCompat
|
((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||||
.makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition");
|
|
||||||
startActivity(intent, options.toBundle());
|
|
||||||
} else {
|
} else {
|
||||||
|
Intent intent = PlaybackService.getPlayerActivityIntent(getActivity(), controller.getMedia());
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,9 +172,7 @@ public class ExternalPlayerFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void playbackDone() {
|
private void playbackDone() {
|
||||||
if (fragmentLayout != null) {
|
clearUi();
|
||||||
fragmentLayout.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
controller.release();
|
controller.release();
|
||||||
}
|
}
|
||||||
|
@ -217,10 +209,22 @@ public class ExternalPlayerFragment extends Fragment {
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(media -> updateUi((Playable) media),
|
.subscribe(media -> updateUi((Playable) media),
|
||||||
error -> Log.e(TAG, Log.getStackTraceString(error)),
|
error -> Log.e(TAG, Log.getStackTraceString(error)),
|
||||||
() -> fragmentLayout.setVisibility(View.GONE));
|
this::clearUi);
|
||||||
return true;
|
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) {
|
private void updateUi(Playable media) {
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
txtvTitle.setText(media.getEpisodeTitle());
|
txtvTitle.setText(media.getEpisodeTitle());
|
||||||
|
@ -236,12 +240,13 @@ public class ExternalPlayerFragment extends Fragment {
|
||||||
.fitCenter()
|
.fitCenter()
|
||||||
.dontAnimate())
|
.dontAnimate())
|
||||||
.into(imgvCover);
|
.into(imgvCover);
|
||||||
|
|
||||||
fragmentLayout.setVisibility(View.VISIBLE);
|
|
||||||
if (controller != null && controller.isPlayingVideoLocally()) {
|
if (controller != null && controller.isPlayingVideoLocally()) {
|
||||||
butPlay.setVisibility(View.GONE);
|
butPlay.setVisibility(View.GONE);
|
||||||
|
((MainActivity) getActivity()).getBottomSheet().setLocked(true);
|
||||||
|
((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||||
} else {
|
} else {
|
||||||
butPlay.setVisibility(View.VISIBLE);
|
butPlay.setVisibility(View.VISIBLE);
|
||||||
|
((MainActivity) getActivity()).getBottomSheet().setLocked(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!");
|
Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!");
|
||||||
|
|
|
@ -10,6 +10,7 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
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.PlaybackController;
|
||||||
import de.danoeh.antennapod.core.util.playback.Timeline;
|
import de.danoeh.antennapod.core.util.playback.Timeline;
|
||||||
import de.danoeh.antennapod.view.ShownotesWebView;
|
import de.danoeh.antennapod.view.ShownotesWebView;
|
||||||
|
@ -35,7 +36,8 @@ public class ItemDescriptionFragment extends Fragment {
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
Log.d(TAG, "Creating view");
|
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 -> {
|
webvDescription.setTimecodeSelectedListener(time -> {
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
controller.seekTo(time);
|
controller.seekTo(time);
|
||||||
|
@ -46,7 +48,7 @@ public class ItemDescriptionFragment extends Fragment {
|
||||||
webvDescription.postDelayed(ItemDescriptionFragment.this::restoreFromPreference, 50);
|
webvDescription.postDelayed(ItemDescriptionFragment.this::restoreFromPreference, 50);
|
||||||
});
|
});
|
||||||
registerForContextMenu(webvDescription);
|
registerForContextMenu(webvDescription);
|
||||||
return webvDescription;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -146,9 +146,6 @@ public class ItemPagerFragment extends Fragment {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
if (Flavors.FLAVOR == Flavors.PLAY) {
|
|
||||||
((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
|
||||||
}
|
|
||||||
inflater.inflate(R.menu.feeditem_options, menu);
|
inflater.inflate(R.menu.feeditem_options, menu);
|
||||||
if (item.hasMedia()) {
|
if (item.hasMedia()) {
|
||||||
FeedItemMenuHandler.onPrepareMenu(menu, item);
|
FeedItemMenuHandler.onPrepareMenu(menu, item);
|
||||||
|
|
|
@ -416,11 +416,11 @@ public class QueueFragment extends Fragment {
|
||||||
recyclerAdapter.setLocked(locked);
|
recyclerAdapter.setLocked(locked);
|
||||||
}
|
}
|
||||||
if (locked) {
|
if (locked) {
|
||||||
Snackbar.make(getActivity().findViewById(R.id.content), R.string
|
Snackbar.make(getActivity().findViewById(android.R.id.content),
|
||||||
.queue_locked, Snackbar.LENGTH_SHORT).show();
|
R.string.queue_locked, Snackbar.LENGTH_SHORT).show();
|
||||||
} else {
|
} else {
|
||||||
Snackbar.make(getActivity().findViewById(R.id.content), R.string
|
Snackbar.make(getActivity().findViewById(android.R.id.content),
|
||||||
.queue_unlocked, Snackbar.LENGTH_SHORT).show();
|
R.string.queue_unlocked, Snackbar.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,11 @@ import androidx.preference.ListPreference;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.MediaplayerActivity;
|
|
||||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||||
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
|
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
|
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
|
||||||
|
import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
|
||||||
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
|
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
|
||||||
import de.danoeh.antennapod.preferences.PreferenceControllerFlavorHelper;
|
import de.danoeh.antennapod.preferences.PreferenceControllerFlavorHelper;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -48,11 +48,11 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
findPreference(PREF_PLAYBACK_REWIND_DELTA_LAUNCHER).setOnPreferenceClickListener(preference -> {
|
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;
|
return true;
|
||||||
});
|
});
|
||||||
findPreference(PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER).setOnPreferenceClickListener(preference -> {
|
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;
|
return true;
|
||||||
});
|
});
|
||||||
if (!PictureInPictureUtil.supportsPictureInPicture(activity)) {
|
if (!PictureInPictureUtil.supportsPictureInPicture(activity)) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
|
@ -1,24 +1,21 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/drawer_layout"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="?attr/actionBarSize"
|
android:minHeight="?attr/actionBarSize"
|
||||||
android:theme="?attr/actionBarTheme"
|
android:theme="?attr/actionBarTheme"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
|
app:navigationIcon="?homeAsUpIndicator"
|
||||||
android:id="@+id/toolbar"/>
|
android:id="@+id/toolbar"/>
|
||||||
|
|
||||||
<de.danoeh.antennapod.view.PagerIndicatorView
|
<de.danoeh.antennapod.view.PagerIndicatorView
|
||||||
android:id="@+id/page_indicator"
|
android:id="@+id/page_indicator"
|
||||||
android:layout_height="16dp"
|
android:layout_height="16dp"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
|
@ -27,7 +24,16 @@
|
||||||
android:layout_below="@id/toolbar"
|
android:layout_below="@id/toolbar"
|
||||||
android:layout_centerHorizontal="true"/>
|
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:id="@+id/pager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
@ -35,9 +41,9 @@
|
||||||
android:layout_below="@id/toolbar"
|
android:layout_below="@id/toolbar"
|
||||||
android:foreground="?android:windowContentOverlay"
|
android:foreground="?android:windowContentOverlay"
|
||||||
tools:background="@android:color/holo_orange_light"
|
tools:background="@android:color/holo_orange_light"
|
||||||
android:layout_marginBottom="12dp" />
|
android:layout_marginBottom="12dp"/>
|
||||||
|
|
||||||
<SeekBar
|
<SeekBar
|
||||||
android:id="@+id/sbPosition"
|
android:id="@+id/sbPosition"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
|
@ -47,9 +53,9 @@
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:layoutDirection="ltr"
|
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:id="@+id/playtime_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -57,14 +63,14 @@
|
||||||
android:layoutDirection="ltr"
|
android:layoutDirection="ltr"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingRight="8dp"
|
android:paddingRight="8dp"
|
||||||
android:layout_marginBottom="4dp">
|
android:layout_marginBottom="4dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtvPosition"
|
android:id="@+id/txtvPosition"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -75,9 +81,9 @@
|
||||||
android:text="@string/position_default_label"
|
android:text="@string/position_default_label"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:textSize="@dimen/text_size_micro"
|
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:id="@+id/txtvLength"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -88,18 +94,19 @@
|
||||||
android:text="@string/position_default_label"
|
android:text="@string/position_default_label"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:textSize="@dimen/text_size_micro"
|
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:id="@+id/player_control"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="24dp"
|
android:layout_marginBottom="24dp"
|
||||||
tools:background="@android:color/holo_purple">
|
tools:background="@android:color/holo_purple">
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/butPlay"
|
android:id="@+id/butPlay"
|
||||||
android:layout_width="@dimen/audioplayer_playercontrols_length_big"
|
android:layout_width="@dimen/audioplayer_playercontrols_length_big"
|
||||||
android:layout_height="@dimen/audioplayer_playercontrols_length_big"
|
android:layout_height="@dimen/audioplayer_playercontrols_length_big"
|
||||||
|
@ -115,9 +122,9 @@
|
||||||
app:srcCompat="?attr/av_play"
|
app:srcCompat="?attr/av_play"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
tools:srcCompat="@drawable/ic_av_play_white_24dp"
|
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_width="@dimen/audioplayer_playercontrols_length_big"
|
||||||
android:layout_height="@dimen/audioplayer_playercontrols_length_big"
|
android:layout_height="@dimen/audioplayer_playercontrols_length_big"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
|
@ -125,9 +132,18 @@
|
||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
android:layout_centerVertical="true" />
|
android:layout_centerVertical="true"/>
|
||||||
|
|
||||||
<ImageButton
|
<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
|
||||||
android:id="@+id/butRev"
|
android:id="@+id/butRev"
|
||||||
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
||||||
android:layout_height="@dimen/audioplayer_playercontrols_length"
|
android:layout_height="@dimen/audioplayer_playercontrols_length"
|
||||||
|
@ -141,9 +157,9 @@
|
||||||
app:srcCompat="?attr/av_rewind"
|
app:srcCompat="?attr/av_rewind"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
tools:srcCompat="@drawable/ic_av_fast_rewind_white_48dp"
|
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:id="@+id/txtvRev"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -156,9 +172,9 @@
|
||||||
android:text="30"
|
android:text="30"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
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:id="@+id/butPlaybackSpeed"
|
||||||
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
||||||
android:layout_height="@dimen/audioplayer_playercontrols_length"
|
android:layout_height="@dimen/audioplayer_playercontrols_length"
|
||||||
|
@ -169,9 +185,9 @@
|
||||||
android:contentDescription="@string/set_playback_speed_label"
|
android:contentDescription="@string/set_playback_speed_label"
|
||||||
tools:srcCompat="@drawable/ic_playback_speed_white"
|
tools:srcCompat="@drawable/ic_playback_speed_white"
|
||||||
tools:visibility="gone"
|
tools:visibility="gone"
|
||||||
tools:background="@android:color/holo_green_dark" />
|
tools:background="@android:color/holo_green_dark"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtvPlaybackSpeed"
|
android:id="@+id/txtvPlaybackSpeed"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -184,9 +200,9 @@
|
||||||
android:text="1.00"
|
android:text="1.00"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:clickable="false" />
|
android:clickable="false"/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/butFF"
|
android:id="@+id/butFF"
|
||||||
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
||||||
android:layout_height="@dimen/audioplayer_playercontrols_length"
|
android:layout_height="@dimen/audioplayer_playercontrols_length"
|
||||||
|
@ -200,9 +216,9 @@
|
||||||
app:srcCompat="?attr/av_fast_forward"
|
app:srcCompat="?attr/av_fast_forward"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
tools:srcCompat="@drawable/ic_av_fast_forward_white_48dp"
|
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:id="@+id/txtvFF"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -215,9 +231,9 @@
|
||||||
android:text="30"
|
android:text="30"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:clickable="false" />
|
android:clickable="false"/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/butSkip"
|
android:id="@+id/butSkip"
|
||||||
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
||||||
android:layout_height="@dimen/audioplayer_playercontrols_length"
|
android:layout_height="@dimen/audioplayer_playercontrols_length"
|
||||||
|
@ -229,20 +245,9 @@
|
||||||
app:srcCompat="?attr/av_skip"
|
app:srcCompat="?attr/av_skip"
|
||||||
android:contentDescription="@string/skip_episode_label"
|
android:contentDescription="@string/skip_episode_label"
|
||||||
tools:srcCompat="@drawable/ic_av_skip_white_48dp"
|
tools:srcCompat="@drawable/ic_av_skip_white_48dp"
|
||||||
tools:background="@android:color/holo_green_dark" />
|
tools:background="@android:color/holo_green_dark"/>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</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" />
|
|
||||||
|
|
||||||
</androidx.drawerlayout.widget.DrawerLayout>
|
|
|
@ -1,87 +1,84 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/fragmentLayout"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/fragmentLayout"
|
||||||
android:layout_height="@dimen/external_player_height"
|
android:layout_width="match_parent"
|
||||||
android:visibility="gone"
|
|
||||||
android:background="?attr/selectableItemBackground">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/imgvCover"
|
|
||||||
android:contentDescription="@string/cover_label"
|
|
||||||
android:layout_width="@dimen/external_player_height"
|
|
||||||
android:layout_height="@dimen/external_player_height"
|
android:layout_height="@dimen/external_player_height"
|
||||||
android:adjustViewBounds="true"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:cropToPadding="true"
|
android:orientation="vertical">
|
||||||
android:scaleType="centerCrop"
|
|
||||||
tools:src="@drawable/ic_drag_vertical_white_48dp"
|
<View
|
||||||
tools:background="@android:color/holo_green_dark"
|
android:layout_width="match_parent"
|
||||||
android:transitionName="coverTransition"
|
android:layout_height="1dp"
|
||||||
android:layout_alignParentTop="true"
|
android:background="?android:attr/dividerHorizontal" />
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_alignParentStart="true"/>
|
<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"/>
|
||||||
|
|
||||||
|
|
||||||
|
<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
|
<ProgressBar
|
||||||
android:id="@+id/episodeProgress"
|
android:id="@+id/episodeProgress"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="4dp"
|
android:layout_height="4dp"
|
||||||
android:layout_toRightOf="@id/imgvCover"
|
style="?attr/progressBarTheme"
|
||||||
android:layout_toEndOf="@id/imgvCover"
|
android:indeterminate="false"
|
||||||
android:layout_alignParentTop="true"
|
tools:progress="100"/>
|
||||||
style="?attr/progressBarTheme"
|
|
||||||
android:indeterminate="false"
|
|
||||||
tools:progress="100"/>
|
|
||||||
|
|
||||||
<ImageButton
|
</LinearLayout>
|
||||||
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"/>
|
|
||||||
|
|
||||||
<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"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
|
@ -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>
|
|
@ -1,42 +1,43 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.drawerlayout.widget.DrawerLayout
|
<androidx.drawerlayout.widget.DrawerLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/drawer_layout"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/drawer_layout"
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:id="@+id/content"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<FrameLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/playerFragment"
|
android:id="@+id/overview_coordinator_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent">
|
||||||
android:layout_alignParentBottom="true"
|
|
||||||
tools:layout_height="64dp"
|
|
||||||
tools:background="@android:color/holo_green_light" />
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/main_view"
|
android:id="@+id/main_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0px"
|
android:layout_height="match_parent"
|
||||||
android:layout_above="@id/playerFragment"
|
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:foreground="?android:windowContentOverlay"
|
android:foreground="?android:windowContentOverlay"
|
||||||
|
android:layout_marginBottom="@dimen/external_player_height"
|
||||||
tools:background="@android:color/holo_red_dark" />
|
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
|
<FrameLayout
|
||||||
android:id="@+id/navDrawerFragment"
|
android:id="@+id/navDrawerFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginEnd="24dp"
|
android:layout_marginEnd="24dp"
|
||||||
android:layout_marginRight="24dp"
|
android:layout_marginRight="24dp"
|
||||||
android:layout_gravity="start"
|
android:layout_gravity="start"
|
||||||
android:orientation="vertical" />
|
android:orientation="vertical" />
|
||||||
|
|
||||||
</androidx.drawerlayout.widget.DrawerLayout>
|
</androidx.drawerlayout.widget.DrawerLayout>
|
|
@ -4,8 +4,6 @@ import android.content.SharedPreferences;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import androidx.annotation.CallSuper;
|
|
||||||
import androidx.core.view.MenuItemCompat;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
@ -14,6 +12,7 @@ import android.view.MenuItem;
|
||||||
import com.google.android.gms.cast.ApplicationMetadata;
|
import com.google.android.gms.cast.ApplicationMetadata;
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
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.CastConsumer;
|
||||||
import de.danoeh.antennapod.core.cast.CastManager;
|
import de.danoeh.antennapod.core.cast.CastManager;
|
||||||
import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
|
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.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
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
|
* Activity that allows for showing the MediaRouter button whenever there's a cast device in the
|
||||||
* network.
|
* network.
|
||||||
|
@ -31,9 +33,7 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
||||||
|
|
||||||
private CastConsumer castConsumer;
|
private CastConsumer castConsumer;
|
||||||
private CastManager castManager;
|
private CastManager castManager;
|
||||||
|
private final List<CastButtonVisibilityManager> castButtons = new ArrayList<>();
|
||||||
private SwitchableMediaRouteActionProvider mediaRouteActionProvider;
|
|
||||||
private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -59,8 +59,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
||||||
};
|
};
|
||||||
castManager = CastManager.getInstance();
|
castManager = CastManager.getInstance();
|
||||||
castManager.addCastConsumer(castConsumer);
|
castManager.addCastConsumer(castConsumer);
|
||||||
|
CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager(castManager);
|
||||||
castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
|
castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
|
||||||
onCastConnectionChanged(castManager.isConnected());
|
onCastConnectionChanged(castManager.isConnected());
|
||||||
|
castButtons.add(castButtonVisibilityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -75,46 +77,15 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
if (!CastManager.isInitialized()) {
|
if (!CastManager.isInitialized()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
castButtonVisibilityManager.setResumed(true);
|
for (CastButtonVisibilityManager castButton : castButtons) {
|
||||||
|
castButton.setResumed(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -123,7 +94,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
||||||
if (!CastManager.isInitialized()) {
|
if (!CastManager.isInitialized()) {
|
||||||
return;
|
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)) {
|
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
|
||||||
boolean newValue = UserPreferences.isCastEnabled();
|
boolean newValue = UserPreferences.isCastEnabled();
|
||||||
Log.d(TAG, "onSharedPreferenceChanged(), isCastEnabled set to " + newValue);
|
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.
|
// PlaybackService has its own listener, so if it's active we don't have to take action here.
|
||||||
if (!newValue && !PlaybackService.isRunning) {
|
if (!newValue && !PlaybackService.isRunning) {
|
||||||
CastManager.getInstance().disconnect();
|
CastManager.getInstance().disconnect();
|
||||||
|
@ -142,129 +117,41 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
||||||
|
|
||||||
private void onCastConnectionChanged(boolean connected) {
|
private void onCastConnectionChanged(boolean connected) {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
castButtonVisibilityManager.onConnected();
|
for (CastButtonVisibilityManager castButton : castButtons) {
|
||||||
|
castButton.onConnected();
|
||||||
|
}
|
||||||
setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
|
setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
|
||||||
} else {
|
} else {
|
||||||
castButtonVisibilityManager.onDisconnected();
|
for (CastButtonVisibilityManager castButton : castButtons) {
|
||||||
|
castButton.onDisconnected();
|
||||||
|
}
|
||||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be called by any activity or fragment for which the cast button should be shown.
|
* 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()) {
|
if (!CastManager.isInitialized()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
castButtonVisibilityManager.requestCastButton(showAsAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CastButtonVisibilityManager {
|
MenuItem mediaRouteButton = menu.findItem(R.id.media_route_menu_item);
|
||||||
private volatile boolean prefEnabled = false;
|
if (mediaRouteButton == null) {
|
||||||
private volatile boolean viewRequested = false;
|
getMenuInflater().inflate(R.menu.cast_enabled, menu);
|
||||||
private volatile boolean resumed = false;
|
mediaRouteButton = menu.findItem(R.id.media_route_menu_item);
|
||||||
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) {
|
SwitchableMediaRouteActionProvider mediaRouteActionProvider =
|
||||||
if (resumed == newValue) {
|
CastManager.getInstance().addMediaRouterButton(mediaRouteButton);
|
||||||
Log.e(TAG, "resumed should never change to the same value");
|
CastButtonVisibilityManager castButtonVisibilityManager =
|
||||||
return;
|
new CastButtonVisibilityManager(CastManager.getInstance());
|
||||||
}
|
castButtonVisibilityManager.setMenu(menu);
|
||||||
resumed = newValue;
|
castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
|
||||||
if (prefEnabled && (viewRequested || connected)) {
|
castButtonVisibilityManager.mediaRouteActionProvider = mediaRouteActionProvider;
|
||||||
if (resumed) {
|
castButtonVisibilityManager.setResumed(true);
|
||||||
castManager.incrementUiCounter();
|
castButtonVisibilityManager.requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||||
} else {
|
mediaRouteActionProvider.setEnabled(castButtonVisibilityManager.shouldEnable());
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,11 +181,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
||||||
*/
|
*/
|
||||||
public static final int NOTIFICATION_TYPE_SET_SPEED_ABILITY_CHANGED = 9;
|
public static final int NOTIFICATION_TYPE_SET_SPEED_ABILITY_CHANGED = 9;
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a message to the user (with provided String resource id)
|
|
||||||
*/
|
|
||||||
public static final int NOTIFICATION_TYPE_SHOW_TOAST = 10;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returned by getPositionSafe() or getDurationSafe() if the playbackService
|
* Returned by getPositionSafe() or getDurationSafe() if the playbackService
|
||||||
* is in an invalid state.
|
* is in an invalid state.
|
||||||
|
@ -1218,7 +1213,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
||||||
notificationBuilder.setCasting(isCasting);
|
notificationBuilder.setCasting(isCasting);
|
||||||
notificationBuilder.updatePosition(getCurrentPosition(), getCurrentPlaybackSpeed());
|
notificationBuilder.updatePosition(getCurrentPosition(), getCurrentPlaybackSpeed());
|
||||||
|
|
||||||
Log.d(TAG, "setupNotification: startForeground" + playerStatus);
|
|
||||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
||||||
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
|
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
|
||||||
startForegroundIfPlaying(playerStatus);
|
startForegroundIfPlaying(playerStatus);
|
||||||
|
@ -1236,10 +1230,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startForegroundIfPlaying(@NonNull PlayerStatus status) {
|
private void startForegroundIfPlaying(@NonNull PlayerStatus status) {
|
||||||
|
Log.d(TAG, "startForegroundIfPlaying: " + status);
|
||||||
if (stateManager.hasReceivedValidStartCommand()) {
|
if (stateManager.hasReceivedValidStartCommand()) {
|
||||||
if (isCasting || status == PlayerStatus.PLAYING || status == PlayerStatus.PREPARING
|
if (isCasting || status == PlayerStatus.PLAYING || status == PlayerStatus.PREPARING
|
||||||
|| status == PlayerStatus.SEEKING) {
|
|| status == PlayerStatus.SEEKING) {
|
||||||
stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build());
|
stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build());
|
||||||
|
Log.d(TAG, "foreground");
|
||||||
} else {
|
} else {
|
||||||
stateManager.stopForeground(false);
|
stateManager.stopForeground(false);
|
||||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
||||||
|
@ -1781,6 +1777,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
||||||
@Override
|
@Override
|
||||||
public void setIsCasting(boolean isCasting) {
|
public void setIsCasting(boolean isCasting) {
|
||||||
PlaybackService.isCasting = isCasting;
|
PlaybackService.isCasting = isCasting;
|
||||||
|
stateManager.validStartCommandWasReceived();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -21,6 +21,7 @@ import android.widget.ImageButton;
|
||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.core.event.MessageEvent;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
import de.danoeh.antennapod.core.util.ThemeUtils;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
|
||||||
|
@ -333,8 +334,6 @@ public class PlaybackController {
|
||||||
case PlaybackService.NOTIFICATION_TYPE_SET_SPEED_ABILITY_CHANGED:
|
case PlaybackService.NOTIFICATION_TYPE_SET_SPEED_ABILITY_CHANGED:
|
||||||
onSetSpeedAbilityChanged();
|
onSetSpeedAbilityChanged();
|
||||||
break;
|
break;
|
||||||
case PlaybackService.NOTIFICATION_TYPE_SHOW_TOAST:
|
|
||||||
postStatusMsg(code, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,11 +410,10 @@ public class PlaybackController {
|
||||||
Log.d(TAG, "status: " + status.toString());
|
Log.d(TAG, "status: " + status.toString());
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ERROR:
|
case ERROR:
|
||||||
postStatusMsg(R.string.player_error_msg, false);
|
EventBus.getDefault().post(new MessageEvent(activity.getString(R.string.player_error_msg)));
|
||||||
handleError(MediaPlayer.MEDIA_ERROR_UNKNOWN);
|
handleError(MediaPlayer.MEDIA_ERROR_UNKNOWN);
|
||||||
break;
|
break;
|
||||||
case PAUSED:
|
case PAUSED:
|
||||||
clearStatusMsg();
|
|
||||||
checkMediaInfoLoaded();
|
checkMediaInfoLoaded();
|
||||||
onPositionObserverUpdate();
|
onPositionObserverUpdate();
|
||||||
updatePlayButtonAppearance(playResource, playText);
|
updatePlayButtonAppearance(playResource, playText);
|
||||||
|
@ -425,7 +423,6 @@ public class PlaybackController {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PLAYING:
|
case PLAYING:
|
||||||
clearStatusMsg();
|
|
||||||
checkMediaInfoLoaded();
|
checkMediaInfoLoaded();
|
||||||
if (!PlaybackService.isCasting() &&
|
if (!PlaybackService.isCasting() &&
|
||||||
PlaybackService.getCurrentMediaType() == MediaType.VIDEO) {
|
PlaybackService.getCurrentMediaType() == MediaType.VIDEO) {
|
||||||
|
@ -435,7 +432,6 @@ public class PlaybackController {
|
||||||
updatePlayButtonAppearance(pauseResource, pauseText);
|
updatePlayButtonAppearance(pauseResource, pauseText);
|
||||||
break;
|
break;
|
||||||
case PREPARING:
|
case PREPARING:
|
||||||
postStatusMsg(R.string.player_preparing_msg, false);
|
|
||||||
checkMediaInfoLoaded();
|
checkMediaInfoLoaded();
|
||||||
if (playbackService != null) {
|
if (playbackService != null) {
|
||||||
if (playbackService.isStartWhenPrepared()) {
|
if (playbackService.isStartWhenPrepared()) {
|
||||||
|
@ -446,21 +442,17 @@ public class PlaybackController {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case STOPPED:
|
case STOPPED:
|
||||||
postStatusMsg(R.string.player_stopped_msg, false);
|
|
||||||
break;
|
break;
|
||||||
case PREPARED:
|
case PREPARED:
|
||||||
checkMediaInfoLoaded();
|
checkMediaInfoLoaded();
|
||||||
postStatusMsg(R.string.player_ready_msg, false);
|
|
||||||
updatePlayButtonAppearance(playResource, playText);
|
updatePlayButtonAppearance(playResource, playText);
|
||||||
onPositionObserverUpdate();
|
onPositionObserverUpdate();
|
||||||
break;
|
break;
|
||||||
case SEEKING:
|
case SEEKING:
|
||||||
onPositionObserverUpdate();
|
onPositionObserverUpdate();
|
||||||
postStatusMsg(R.string.player_seeking_msg, false);
|
|
||||||
break;
|
break;
|
||||||
case INITIALIZED:
|
case INITIALIZED:
|
||||||
checkMediaInfoLoaded();
|
checkMediaInfoLoaded();
|
||||||
clearStatusMsg();
|
|
||||||
updatePlayButtonAppearance(playResource, playText);
|
updatePlayButtonAppearance(playResource, playText);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -482,10 +474,6 @@ public class PlaybackController {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postStatusMsg(int msg, boolean showToast) {}
|
|
||||||
|
|
||||||
public void clearStatusMsg() {}
|
|
||||||
|
|
||||||
public boolean loadMediaInfo() {
|
public boolean loadMediaInfo() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -599,8 +587,8 @@ public class PlaybackController {
|
||||||
public int getPosition() {
|
public int getPosition() {
|
||||||
if (playbackService != null) {
|
if (playbackService != null) {
|
||||||
return playbackService.getCurrentPosition();
|
return playbackService.getCurrentPosition();
|
||||||
} else if (media != null) {
|
} else if (getMedia() != null) {
|
||||||
return media.getPosition();
|
return getMedia().getPosition();
|
||||||
} else {
|
} else {
|
||||||
return PlaybackService.INVALID_TIME;
|
return PlaybackService.INVALID_TIME;
|
||||||
}
|
}
|
||||||
|
@ -609,8 +597,8 @@ public class PlaybackController {
|
||||||
public int getDuration() {
|
public int getDuration() {
|
||||||
if (playbackService != null) {
|
if (playbackService != null) {
|
||||||
return playbackService.getDuration();
|
return playbackService.getDuration();
|
||||||
} else if (media != null) {
|
} else if (getMedia() != null) {
|
||||||
return media.getDuration();
|
return getMedia().getDuration();
|
||||||
} else {
|
} else {
|
||||||
return PlaybackService.INVALID_TIME;
|
return PlaybackService.INVALID_TIME;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
<dimen name="widget_margin">0dp</dimen>
|
<dimen name="widget_margin">0dp</dimen>
|
||||||
<dimen name="thumbnail_length">70dp</dimen>
|
<dimen name="thumbnail_length">70dp</dimen>
|
||||||
<dimen name="external_player_height">56dp</dimen>
|
<dimen name="external_player_height">64dp</dimen>
|
||||||
<dimen name="enc_icons_size">20dp</dimen>
|
<dimen name="enc_icons_size">20dp</dimen>
|
||||||
<dimen name="text_size_micro">12sp</dimen>
|
<dimen name="text_size_micro">12sp</dimen>
|
||||||
<dimen name="text_size_small">14sp</dimen>
|
<dimen name="text_size_small">14sp</dimen>
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
package de.danoeh.antennapod.core.cast;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import androidx.core.view.MenuItemCompat;
|
||||||
|
import de.danoeh.antennapod.core.R;
|
||||||
|
|
||||||
|
public class CastButtonVisibilityManager {
|
||||||
|
private static final String TAG = "CastBtnVisibilityMgr";
|
||||||
|
private final CastManager castManager;
|
||||||
|
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 SwitchableMediaRouteActionProvider mediaRouteActionProvider;
|
||||||
|
|
||||||
|
public CastButtonVisibilityManager(CastManager castManager) {
|
||||||
|
this.castManager = castManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,9 +26,11 @@ import java.util.concurrent.TimeoutException;
|
||||||
import de.danoeh.antennapod.core.cast.CastConsumer;
|
import de.danoeh.antennapod.core.cast.CastConsumer;
|
||||||
import de.danoeh.antennapod.core.cast.CastManager;
|
import de.danoeh.antennapod.core.cast.CastManager;
|
||||||
import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
|
import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
|
||||||
|
import de.danoeh.antennapod.core.event.MessageEvent;
|
||||||
import de.danoeh.antennapod.core.feed.MediaType;
|
import de.danoeh.antennapod.core.feed.MediaType;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.util.NetworkUtils;
|
import de.danoeh.antennapod.core.util.NetworkUtils;
|
||||||
|
import org.greenrobot.eventbus.EventBus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class intended to work along PlaybackService and provide support for different flavors.
|
* Class intended to work along PlaybackService and provide support for different flavors.
|
||||||
|
@ -106,7 +108,7 @@ public class PlaybackServiceFlavorHelper {
|
||||||
}
|
}
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case RemotePSMP.CAST_ERROR:
|
case RemotePSMP.CAST_ERROR:
|
||||||
callback.sendNotificationBroadcast(PlaybackService.NOTIFICATION_TYPE_SHOW_TOAST, resourceId);
|
EventBus.getDefault().post(new MessageEvent(context.getString(resourceId)));
|
||||||
return true;
|
return true;
|
||||||
case RemotePSMP.CAST_ERROR_PRIORITY_HIGH:
|
case RemotePSMP.CAST_ERROR_PRIORITY_HIGH:
|
||||||
Toast.makeText(context, resourceId, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, resourceId, Toast.LENGTH_SHORT).show();
|
||||||
|
|
Loading…
Reference in New Issue