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