Merge pull request #5162 from ByteHamster/remove-audio-only-code
VideoPlayer rework
This commit is contained in:
commit
af6af2fb78
|
@ -31,6 +31,6 @@ public class VideoplayerActivityTest {
|
||||||
@Test
|
@Test
|
||||||
public void testStartActivity() throws Exception {
|
public void testStartActivity() throws Exception {
|
||||||
activityTestRule.launchActivity(new Intent());
|
activityTestRule.launchActivity(new Intent());
|
||||||
onView(withId(R.id.videoframe)).check(matches(isDisplayed()));
|
onView(withId(R.id.videoPlayerContainer)).check(matches(isDisplayed()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,658 +0,0 @@
|
||||||
package de.danoeh.antennapod.activity;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.graphics.PixelFormat;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.SeekBar;
|
|
||||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.event.ServiceEvent;
|
|
||||||
import de.danoeh.antennapod.view.PlayButton;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.cardview.widget.CardView;
|
|
||||||
import androidx.core.app.ActivityOptionsCompat;
|
|
||||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
|
||||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
|
||||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
|
||||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
|
||||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
|
||||||
import de.danoeh.antennapod.core.storage.DBReader;
|
|
||||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
|
||||||
import de.danoeh.antennapod.core.util.Converter;
|
|
||||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
|
||||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
|
||||||
import de.danoeh.antennapod.core.util.ShareUtils;
|
|
||||||
import de.danoeh.antennapod.core.util.StorageUtils;
|
|
||||||
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
|
|
||||||
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
|
|
||||||
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
|
|
||||||
import de.danoeh.antennapod.model.playback.Playable;
|
|
||||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
|
||||||
import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
|
|
||||||
import de.danoeh.antennapod.dialog.ShareDialog;
|
|
||||||
import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
|
|
||||||
import de.danoeh.antennapod.dialog.SleepTimerDialog;
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides general features which are both needed for playing audio and video
|
|
||||||
* files.
|
|
||||||
*/
|
|
||||||
public abstract class MediaplayerActivity extends CastEnabledActivity implements OnSeekBarChangeListener {
|
|
||||||
private static final String TAG = "MediaplayerActivity";
|
|
||||||
private static final String PREFS = "MediaPlayerActivityPreferences";
|
|
||||||
|
|
||||||
PlaybackController controller;
|
|
||||||
|
|
||||||
private TextView txtvPosition;
|
|
||||||
private TextView txtvLength;
|
|
||||||
SeekBar sbPosition;
|
|
||||||
private ImageButton butRev;
|
|
||||||
private TextView txtvRev;
|
|
||||||
private PlayButton butPlay;
|
|
||||||
private ImageButton butFF;
|
|
||||||
private TextView txtvFF;
|
|
||||||
private ImageButton butSkip;
|
|
||||||
private CardView cardViewSeek;
|
|
||||||
private TextView txtvSeek;
|
|
||||||
|
|
||||||
private boolean showTimeLeft = false;
|
|
||||||
|
|
||||||
private boolean isFavorite = false;
|
|
||||||
|
|
||||||
private Disposable disposable;
|
|
||||||
|
|
||||||
private PlaybackController newPlaybackController() {
|
|
||||||
return new PlaybackController(this) {
|
|
||||||
@Override
|
|
||||||
public void onPositionObserverUpdate() {
|
|
||||||
MediaplayerActivity.this.onPositionObserverUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBufferStart() {
|
|
||||||
MediaplayerActivity.this.onBufferStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBufferEnd() {
|
|
||||||
MediaplayerActivity.this.onBufferEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBufferUpdate(float progress) {
|
|
||||||
MediaplayerActivity.this.onBufferUpdate(progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleError(int code) {
|
|
||||||
MediaplayerActivity.this.handleError(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReloadNotification(int code) {
|
|
||||||
MediaplayerActivity.this.onReloadNotification(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSleepTimerUpdate() {
|
|
||||||
supportInvalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updatePlayButtonShowsPlay(boolean showPlay) {
|
|
||||||
butPlay.setIsShowPlay(showPlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadMediaInfo() {
|
|
||||||
MediaplayerActivity.this.loadMediaInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAwaitingVideoSurface() {
|
|
||||||
MediaplayerActivity.this.onAwaitingVideoSurface();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPlaybackEnd() {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPlaybackSpeedChange() {
|
|
||||||
MediaplayerActivity.this.onPlaybackSpeedChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setScreenOn(boolean enable) {
|
|
||||||
super.setScreenOn(enable);
|
|
||||||
MediaplayerActivity.this.setScreenOn(enable);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
|
||||||
public void onEventMainThread(PlaybackPositionEvent event) {
|
|
||||||
onPositionObserverUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
|
||||||
public void onPlaybackServiceChanged(ServiceEvent event) {
|
|
||||||
if (event.action == ServiceEvent.Action.SERVICE_SHUT_DOWN) {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onPlaybackSpeedChange() {
|
|
||||||
updatePlaybackSpeedButtonText();
|
|
||||||
}
|
|
||||||
|
|
||||||
void chooseTheme() {
|
|
||||||
setTheme(UserPreferences.getTheme());
|
|
||||||
}
|
|
||||||
|
|
||||||
void setScreenOn(boolean enable) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
chooseTheme();
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
Log.d(TAG, "onCreate()");
|
|
||||||
StorageUtils.checkStorageAvailability(this);
|
|
||||||
|
|
||||||
getWindow().setFormat(PixelFormat.TRANSPARENT);
|
|
||||||
setupGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
|
||||||
if (controller != null) {
|
|
||||||
controller.reinitServiceIfPaused();
|
|
||||||
controller.pause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should be used to switch to another player activity if the mime type is
|
|
||||||
* not the correct one for the current activity.
|
|
||||||
*/
|
|
||||||
protected abstract void onReloadNotification(int notificationCode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should be used to inform the user that the PlaybackService is currently
|
|
||||||
* buffering.
|
|
||||||
*/
|
|
||||||
protected void onBufferStart() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should be used to hide the view that was showing the 'buffering'-message.
|
|
||||||
*/
|
|
||||||
protected void onBufferEnd() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onBufferUpdate(float progress) {
|
|
||||||
if (sbPosition != null) {
|
|
||||||
sbPosition.setSecondaryProgress((int) (progress * sbPosition.getMax()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
controller = newPlaybackController();
|
|
||||||
controller.init();
|
|
||||||
loadMediaInfo();
|
|
||||||
onPositionObserverUpdate();
|
|
||||||
EventBus.getDefault().register(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
Log.d(TAG, "onStop()");
|
|
||||||
if (controller != null) {
|
|
||||||
controller.release();
|
|
||||||
controller = null; // prevent leak
|
|
||||||
}
|
|
||||||
if (disposable != null) {
|
|
||||||
disposable.dispose();
|
|
||||||
}
|
|
||||||
EventBus.getDefault().unregister(this);
|
|
||||||
super.onStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
|
||||||
@Override
|
|
||||||
public void onTrimMemory(int level) {
|
|
||||||
super.onTrimMemory(level);
|
|
||||||
Glide.get(this).trimMemory(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLowMemory() {
|
|
||||||
super.onLowMemory();
|
|
||||||
Glide.get(this).clearMemory();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
super.onCreateOptionsMenu(menu);
|
|
||||||
requestCastButton(menu);
|
|
||||||
MenuInflater inflater = getMenuInflater();
|
|
||||||
inflater.inflate(R.menu.mediaplayer, menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
|
||||||
super.onPrepareOptionsMenu(menu);
|
|
||||||
if (controller == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Playable media = controller.getMedia();
|
|
||||||
boolean isFeedMedia = (media instanceof FeedMedia);
|
|
||||||
|
|
||||||
menu.findItem(R.id.open_feed_item).setVisible(isFeedMedia); // FeedMedia implies it belongs to a Feed
|
|
||||||
|
|
||||||
boolean hasWebsiteLink = ( getWebsiteLinkWithFallback(media) != null );
|
|
||||||
menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink);
|
|
||||||
|
|
||||||
boolean isItemAndHasLink = isFeedMedia &&
|
|
||||||
ShareUtils.hasLinkToShare(((FeedMedia) media).getItem());
|
|
||||||
|
|
||||||
boolean isItemHasDownloadLink = isFeedMedia && ((FeedMedia) media).getDownload_url() != null;
|
|
||||||
|
|
||||||
menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink);
|
|
||||||
|
|
||||||
menu.findItem(R.id.add_to_favorites_item).setVisible(false);
|
|
||||||
menu.findItem(R.id.remove_from_favorites_item).setVisible(false);
|
|
||||||
if (isFeedMedia) {
|
|
||||||
menu.findItem(R.id.add_to_favorites_item).setVisible(!isFavorite);
|
|
||||||
menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite);
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.findItem(R.id.set_sleeptimer_item).setVisible(!controller.sleepTimerActive());
|
|
||||||
menu.findItem(R.id.disable_sleeptimer_item).setVisible(controller.sleepTimerActive());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (controller == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Playable media = controller.getMedia();
|
|
||||||
if (item.getItemId() == android.R.id.home) {
|
|
||||||
Intent intent = new Intent(MediaplayerActivity.this,
|
|
||||||
MainActivity.class);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
|
|
||||||
| Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
|
|
||||||
View cover = findViewById(R.id.imgvCover);
|
|
||||||
if (cover != null) {
|
|
||||||
ActivityOptionsCompat options = ActivityOptionsCompat
|
|
||||||
.makeSceneTransitionAnimation(MediaplayerActivity.this, cover, "coverTransition");
|
|
||||||
startActivity(intent, options.toBundle());
|
|
||||||
} else {
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (media != null) {
|
|
||||||
final @Nullable FeedItem feedItem = getFeedItem(media); // some options option requires FeedItem
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.add_to_favorites_item:
|
|
||||||
if (feedItem != null) {
|
|
||||||
DBWriter.addFavoriteItem(feedItem);
|
|
||||||
isFavorite = true;
|
|
||||||
invalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case R.id.remove_from_favorites_item:
|
|
||||||
if (feedItem != null) {
|
|
||||||
DBWriter.removeFavoriteItem(feedItem);
|
|
||||||
isFavorite = false;
|
|
||||||
invalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case R.id.disable_sleeptimer_item: // Fall-through
|
|
||||||
case R.id.set_sleeptimer_item:
|
|
||||||
new SleepTimerDialog().show(getSupportFragmentManager(), "SleepTimerDialog");
|
|
||||||
break;
|
|
||||||
case R.id.audio_controls:
|
|
||||||
PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance();
|
|
||||||
dialog.show(getSupportFragmentManager(), "playback_controls");
|
|
||||||
break;
|
|
||||||
case R.id.open_feed_item:
|
|
||||||
if (feedItem != null) {
|
|
||||||
Intent intent = MainActivity.getIntentToOpenFeed(this, feedItem.getFeedId());
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case R.id.visit_website_item:
|
|
||||||
IntentUtils.openInBrowser(MediaplayerActivity.this, getWebsiteLinkWithFallback(media));
|
|
||||||
break;
|
|
||||||
case R.id.share_item:
|
|
||||||
if (feedItem != null) {
|
|
||||||
ShareDialog shareDialog = ShareDialog.newInstance(feedItem);
|
|
||||||
shareDialog.show(getSupportFragmentManager(), "ShareEpisodeDialog");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getWebsiteLinkWithFallback(Playable media) {
|
|
||||||
if (media == null) {
|
|
||||||
return null;
|
|
||||||
} else if (StringUtils.isNotBlank(media.getWebsiteLink())) {
|
|
||||||
return media.getWebsiteLink();
|
|
||||||
} else if (media instanceof FeedMedia) {
|
|
||||||
return FeedItemUtil.getLinkWithFallback(((FeedMedia)media).getItem());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
Log.d(TAG, "onResume()");
|
|
||||||
StorageUtils.checkStorageAvailability(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by 'handleStatus()' when the PlaybackService is waiting for
|
|
||||||
* a video surface.
|
|
||||||
*/
|
|
||||||
protected abstract void onAwaitingVideoSurface();
|
|
||||||
|
|
||||||
void onPositionObserverUpdate() {
|
|
||||||
if (controller == null || txtvPosition == null || txtvLength == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
|
|
||||||
int currentPosition = converter.convert(controller.getPosition());
|
|
||||||
int duration = converter.convert(controller.getDuration());
|
|
||||||
int remainingTime = converter.convert(
|
|
||||||
controller.getDuration() - controller.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));
|
|
||||||
}
|
|
||||||
updateProgressbarPosition(currentPosition, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateProgressbarPosition(int position, int duration) {
|
|
||||||
Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration + ")");
|
|
||||||
if(sbPosition == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
float progress = ((float) position) / duration;
|
|
||||||
sbPosition.setProgress((int) (progress * sbPosition.getMax()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load information about the media that is going to be played or currently
|
|
||||||
* being played. This method will be called when the activity is connected
|
|
||||||
* to the PlaybackService to ensure that the activity has the right
|
|
||||||
* FeedMedia object.
|
|
||||||
*/
|
|
||||||
void loadMediaInfo() {
|
|
||||||
Log.d(TAG, "loadMediaInfo()");
|
|
||||||
if (controller == null || controller.getMedia() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showTimeLeft = UserPreferences.shouldShowRemainingTime();
|
|
||||||
onPositionObserverUpdate();
|
|
||||||
checkFavorite();
|
|
||||||
updatePlaybackSpeedButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updatePlaybackSpeedButton() {
|
|
||||||
// Only meaningful on AudioplayerActivity, where it is overridden.
|
|
||||||
}
|
|
||||||
|
|
||||||
void updatePlaybackSpeedButtonText() {
|
|
||||||
// Only meaningful on AudioplayerActivity, where it is overridden.
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupGUI() {
|
|
||||||
setContentView(getContentViewResourceId());
|
|
||||||
sbPosition = findViewById(R.id.sbPosition);
|
|
||||||
txtvPosition = findViewById(R.id.txtvPosition);
|
|
||||||
cardViewSeek = findViewById(R.id.cardViewSeek);
|
|
||||||
txtvSeek = findViewById(R.id.txtvSeek);
|
|
||||||
|
|
||||||
showTimeLeft = UserPreferences.shouldShowRemainingTime();
|
|
||||||
Log.d("timeleft", showTimeLeft ? "true" : "false");
|
|
||||||
txtvLength = findViewById(R.id.txtvLength);
|
|
||||||
if (txtvLength != null) {
|
|
||||||
txtvLength.setOnClickListener(v -> {
|
|
||||||
showTimeLeft = !showTimeLeft;
|
|
||||||
Playable media = controller.getMedia();
|
|
||||||
if (media == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
|
|
||||||
String length;
|
|
||||||
if (showTimeLeft) {
|
|
||||||
int remainingTime = converter.convert(
|
|
||||||
media.getDuration() - media.getPosition());
|
|
||||||
|
|
||||||
length = "-" + Converter.getDurationStringLong(remainingTime);
|
|
||||||
} else {
|
|
||||||
int duration = converter.convert(media.getDuration());
|
|
||||||
length = Converter.getDurationStringLong(duration);
|
|
||||||
}
|
|
||||||
txtvLength.setText(length);
|
|
||||||
|
|
||||||
UserPreferences.setShowRemainTimeSetting(showTimeLeft);
|
|
||||||
Log.d("timeleft on click", showTimeLeft ? "true" : "false");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
butRev = findViewById(R.id.butRev);
|
|
||||||
txtvRev = findViewById(R.id.txtvRev);
|
|
||||||
if (txtvRev != null) {
|
|
||||||
txtvRev.setText(NumberFormat.getInstance().format(UserPreferences.getRewindSecs()));
|
|
||||||
}
|
|
||||||
butPlay = findViewById(R.id.butPlay);
|
|
||||||
butPlay.setIsVideoScreen(true);
|
|
||||||
butFF = findViewById(R.id.butFF);
|
|
||||||
txtvFF = findViewById(R.id.txtvFF);
|
|
||||||
if (txtvFF != null) {
|
|
||||||
txtvFF.setText(NumberFormat.getInstance().format(UserPreferences.getFastForwardSecs()));
|
|
||||||
}
|
|
||||||
butSkip = findViewById(R.id.butSkip);
|
|
||||||
|
|
||||||
// SEEKBAR SETUP
|
|
||||||
|
|
||||||
sbPosition.setOnSeekBarChangeListener(this);
|
|
||||||
|
|
||||||
// BUTTON SETUP
|
|
||||||
|
|
||||||
if (butRev != null) {
|
|
||||||
butRev.setOnClickListener(v -> onRewind());
|
|
||||||
butRev.setOnLongClickListener(v -> {
|
|
||||||
SkipPreferenceDialog.showSkipPreference(MediaplayerActivity.this,
|
|
||||||
SkipPreferenceDialog.SkipDirection.SKIP_REWIND, txtvRev);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
butPlay.setOnClickListener(v -> onPlayPause());
|
|
||||||
|
|
||||||
if (butFF != null) {
|
|
||||||
butFF.setOnClickListener(v -> onFastForward());
|
|
||||||
butFF.setOnLongClickListener(v -> {
|
|
||||||
SkipPreferenceDialog.showSkipPreference(MediaplayerActivity.this,
|
|
||||||
SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (butSkip != null) {
|
|
||||||
butSkip.setOnClickListener(v ->
|
|
||||||
IntentUtils.sendLocalBroadcast(MediaplayerActivity.this, PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onRewind() {
|
|
||||||
if (controller == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int curr = controller.getPosition();
|
|
||||||
controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onPlayPause() {
|
|
||||||
if(controller == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
controller.init();
|
|
||||||
controller.playPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onFastForward() {
|
|
||||||
if (controller == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int curr = controller.getPosition();
|
|
||||||
controller.seekTo(curr + UserPreferences.getFastForwardSecs() * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract int getContentViewResourceId();
|
|
||||||
|
|
||||||
private void handleError(int errorCode) {
|
|
||||||
final AlertDialog.Builder errorDialog = new AlertDialog.Builder(this);
|
|
||||||
errorDialog.setTitle(R.string.error_label);
|
|
||||||
errorDialog.setMessage(MediaPlayerError.getErrorString(this, errorCode));
|
|
||||||
errorDialog.setNeutralButton("OK",
|
|
||||||
(dialog, which) -> {
|
|
||||||
dialog.dismiss();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
errorDialog.create().show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private float prog;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
|
||||||
if (controller == null || txtvLength == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fromUser) {
|
|
||||||
prog = progress / ((float) seekBar.getMax());
|
|
||||||
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
|
|
||||||
int position = converter.convert((int) (prog * controller.getDuration()));
|
|
||||||
txtvSeek.setText(Converter.getDurationStringLong(position));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
|
||||||
cardViewSeek.setScaleX(.8f);
|
|
||||||
cardViewSeek.setScaleY(.8f);
|
|
||||||
cardViewSeek.animate()
|
|
||||||
.setInterpolator(new FastOutSlowInInterpolator())
|
|
||||||
.alpha(1f).scaleX(1f).scaleY(1f)
|
|
||||||
.setDuration(200)
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
|
||||||
if (controller != null) {
|
|
||||||
controller.seekTo((int) (prog * controller.getDuration()));
|
|
||||||
}
|
|
||||||
cardViewSeek.setScaleX(1f);
|
|
||||||
cardViewSeek.setScaleY(1f);
|
|
||||||
cardViewSeek.animate()
|
|
||||||
.setInterpolator(new FastOutSlowInInterpolator())
|
|
||||||
.alpha(0f).scaleX(.8f).scaleY(.8f)
|
|
||||||
.setDuration(200)
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkFavorite() {
|
|
||||||
FeedItem feedItem = getFeedItem(controller.getMedia());
|
|
||||||
if (feedItem == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (disposable != null) {
|
|
||||||
disposable.dispose();
|
|
||||||
}
|
|
||||||
disposable = Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId()))
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(
|
|
||||||
item -> {
|
|
||||||
boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE);
|
|
||||||
if (isFavorite != isFav) {
|
|
||||||
isFavorite = isFav;
|
|
||||||
invalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static FeedItem getFeedItem(@Nullable Playable playable) {
|
|
||||||
if (playable instanceof FeedMedia) {
|
|
||||||
return ((FeedMedia) playable).getItem();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +1,79 @@
|
||||||
package de.danoeh.antennapod.activity;
|
package de.danoeh.antennapod.activity;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.view.Gravity;
|
import android.os.Looper;
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.animation.AlphaAnimation;
|
|
||||||
import android.view.animation.AnimationSet;
|
|
||||||
import android.view.animation.ScaleAnimation;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
import androidx.core.view.WindowCompat;
|
|
||||||
import androidx.appcompat.app.ActionBar;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.view.animation.AlphaAnimation;
|
||||||
import android.view.animation.Animation;
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.AnimationSet;
|
||||||
import android.view.animation.AnimationUtils;
|
import android.view.animation.AnimationUtils;
|
||||||
|
import android.view.animation.ScaleAnimation;
|
||||||
|
import android.widget.EditText;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import java.lang.ref.WeakReference;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import androidx.core.view.WindowCompat;
|
||||||
|
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||||
|
import de.danoeh.antennapod.core.event.ServiceEvent;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||||
|
import de.danoeh.antennapod.core.util.Converter;
|
||||||
|
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||||
|
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||||
|
import de.danoeh.antennapod.core.util.ShareUtils;
|
||||||
|
import de.danoeh.antennapod.core.util.StorageUtils;
|
||||||
|
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
|
||||||
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
|
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
|
||||||
|
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
|
||||||
|
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||||
|
import de.danoeh.antennapod.databinding.VideoplayerActivityBinding;
|
||||||
|
import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
|
||||||
|
import de.danoeh.antennapod.dialog.ShareDialog;
|
||||||
|
import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
|
||||||
|
import de.danoeh.antennapod.dialog.SleepTimerDialog;
|
||||||
|
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||||
|
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||||
import de.danoeh.antennapod.model.playback.Playable;
|
import de.danoeh.antennapod.model.playback.Playable;
|
||||||
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
|
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
|
||||||
import de.danoeh.antennapod.view.AspectRatioVideoView;
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.greenrobot.eventbus.EventBus;
|
||||||
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity for playing video files.
|
* Activity for playing video files.
|
||||||
*/
|
*/
|
||||||
public class VideoplayerActivity extends MediaplayerActivity {
|
public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.OnSeekBarChangeListener {
|
||||||
private static final String TAG = "VideoplayerActivity";
|
private static final String TAG = "VideoplayerActivity";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,36 +83,39 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
private boolean videoSurfaceCreated = false;
|
private boolean videoSurfaceCreated = false;
|
||||||
private boolean destroyingDueToReload = false;
|
private boolean destroyingDueToReload = false;
|
||||||
private long lastScreenTap = 0;
|
private long lastScreenTap = 0;
|
||||||
|
private Handler videoControlsHider = new Handler(Looper.getMainLooper());
|
||||||
private VideoControlsHider videoControlsHider = new VideoControlsHider(this);
|
private VideoplayerActivityBinding viewBinding;
|
||||||
|
private PlaybackController controller;
|
||||||
private final AtomicBoolean isSetup = new AtomicBoolean(false);
|
private boolean showTimeLeft = false;
|
||||||
|
private boolean isFavorite = false;
|
||||||
private LinearLayout controls;
|
private Disposable disposable;
|
||||||
private LinearLayout videoOverlay;
|
private float prog;
|
||||||
private AspectRatioVideoView videoview;
|
|
||||||
private ProgressBar progressIndicator;
|
|
||||||
private FrameLayout videoframe;
|
|
||||||
private ImageView skipAnimationView;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void chooseTheme() {
|
|
||||||
setTheme(R.style.Theme_AntennaPod_VideoPlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("AppCompatMethod")
|
@SuppressLint("AppCompatMethod")
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
|
||||||
supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY); // has to be called before setting layout content
|
// has to be called before setting layout content
|
||||||
|
supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY);
|
||||||
|
setTheme(R.style.Theme_AntennaPod_VideoPlayer);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Log.d(TAG, "onCreate()");
|
||||||
|
StorageUtils.checkStorageAvailability(this);
|
||||||
|
|
||||||
|
getWindow().setFormat(PixelFormat.TRANSPARENT);
|
||||||
|
viewBinding = VideoplayerActivityBinding.inflate(LayoutInflater.from(this));
|
||||||
|
setContentView(viewBinding.getRoot());
|
||||||
|
setupView();
|
||||||
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(0x80000000));
|
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(0x80000000));
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
StorageUtils.checkStorageAvailability(this);
|
||||||
if (PlaybackService.isCasting()) {
|
if (PlaybackService.isCasting()) {
|
||||||
Intent intent = PlaybackService.getPlayerActivityIntent(this);
|
Intent intent = PlaybackService.getPlayerActivityIntent(this);
|
||||||
if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) {
|
if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) {
|
||||||
|
@ -99,11 +128,20 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
|
if (controller != null) {
|
||||||
|
controller.release();
|
||||||
|
controller = null; // prevent leak
|
||||||
|
}
|
||||||
|
if (disposable != null) {
|
||||||
|
disposable.dispose();
|
||||||
|
}
|
||||||
|
EventBus.getDefault().unregister(this);
|
||||||
super.onStop();
|
super.onStop();
|
||||||
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
||||||
videoControlsHider.stop();
|
videoControlsHider.removeCallbacks(hideVideoControls);
|
||||||
}
|
}
|
||||||
progressIndicator.setVisibility(View.GONE); // Controller released; we will not receive buffering updates
|
// Controller released; we will not receive buffering updates
|
||||||
|
viewBinding.progressBar.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -114,6 +152,16 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
controller = newPlaybackController();
|
||||||
|
controller.init();
|
||||||
|
loadMediaInfo();
|
||||||
|
onPositionObserverUpdate();
|
||||||
|
EventBus.getDefault().register(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
||||||
|
@ -124,16 +172,103 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
public void onTrimMemory(int level) {
|
||||||
videoControlsHider.stop();
|
super.onTrimMemory(level);
|
||||||
videoControlsHider = null;
|
Glide.get(this).trimMemory(level);
|
||||||
super.onDestroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
public void onLowMemory() {
|
||||||
|
super.onLowMemory();
|
||||||
|
Glide.get(this).clearMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlaybackController newPlaybackController() {
|
||||||
|
return new PlaybackController(this) {
|
||||||
|
@Override
|
||||||
|
public void onPositionObserverUpdate() {
|
||||||
|
VideoplayerActivity.this.onPositionObserverUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBufferStart() {
|
||||||
|
viewBinding.progressBar.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBufferEnd() {
|
||||||
|
viewBinding.progressBar.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBufferUpdate(float progress) {
|
||||||
|
viewBinding.sbPosition.setSecondaryProgress((int) (progress * viewBinding.sbPosition.getMax()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleError(int code) {
|
||||||
|
final AlertDialog.Builder errorDialog = new AlertDialog.Builder(VideoplayerActivity.this);
|
||||||
|
errorDialog.setTitle(R.string.error_label);
|
||||||
|
errorDialog.setMessage(MediaPlayerError.getErrorString(VideoplayerActivity.this, code));
|
||||||
|
errorDialog.setNeutralButton(android.R.string.ok, (dialog, which) -> finish());
|
||||||
|
errorDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReloadNotification(int code) {
|
||||||
|
VideoplayerActivity.this.onReloadNotification(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSleepTimerUpdate() {
|
||||||
|
supportInvalidateOptionsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updatePlayButtonShowsPlay(boolean showPlay) {
|
||||||
|
viewBinding.playButton.setIsShowPlay(showPlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadMediaInfo() {
|
||||||
|
VideoplayerActivity.this.loadMediaInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAwaitingVideoSurface() {
|
||||||
|
setupVideoAspectRatio();
|
||||||
|
if (videoSurfaceCreated && controller != null) {
|
||||||
|
Log.d(TAG, "Videosurface already created, setting videosurface now");
|
||||||
|
controller.setVideoSurface(viewBinding.videoView.getHolder());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaybackEnd() {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setScreenOn(boolean enable) {
|
||||||
|
if (enable) {
|
||||||
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
|
} else {
|
||||||
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected void loadMediaInfo() {
|
protected void loadMediaInfo() {
|
||||||
super.loadMediaInfo();
|
Log.d(TAG, "loadMediaInfo()");
|
||||||
|
if (controller == null || controller.getMedia() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showTimeLeft = UserPreferences.shouldShowRemainingTime();
|
||||||
|
onPositionObserverUpdate();
|
||||||
|
checkFavorite();
|
||||||
Playable media = controller.getMedia();
|
Playable media = controller.getMedia();
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
getSupportActionBar().setSubtitle(media.getEpisodeTitle());
|
getSupportActionBar().setSubtitle(media.getEpisodeTitle());
|
||||||
|
@ -141,48 +276,79 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected void setupView() {
|
||||||
protected void setupGUI() {
|
showTimeLeft = UserPreferences.shouldShowRemainingTime();
|
||||||
if (isSetup.getAndSet(true)) {
|
Log.d("timeleft", showTimeLeft ? "true" : "false");
|
||||||
|
viewBinding.durationLabel.setOnClickListener(v -> {
|
||||||
|
showTimeLeft = !showTimeLeft;
|
||||||
|
Playable media = controller.getMedia();
|
||||||
|
if (media == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
super.setupGUI();
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
|
||||||
controls = findViewById(R.id.controls);
|
String length;
|
||||||
videoOverlay = findViewById(R.id.overlay);
|
if (showTimeLeft) {
|
||||||
videoview = findViewById(R.id.videoview);
|
int remainingTime = converter.convert(media.getDuration() - media.getPosition());
|
||||||
videoframe = findViewById(R.id.videoframe);
|
length = "-" + Converter.getDurationStringLong(remainingTime);
|
||||||
progressIndicator = findViewById(R.id.progressIndicator);
|
} else {
|
||||||
skipAnimationView = findViewById(R.id.skip_animation);
|
int duration = converter.convert(media.getDuration());
|
||||||
videoview.getHolder().addCallback(surfaceHolderCallback);
|
length = Converter.getDurationStringLong(duration);
|
||||||
videoframe.setOnTouchListener(onVideoviewTouched);
|
}
|
||||||
videoOverlay.setOnTouchListener((view, motionEvent) -> true); // To suppress touches directly below the slider
|
viewBinding.durationLabel.setText(length);
|
||||||
videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
|
||||||
videoOverlay.setFitsSystemWindows(true);
|
UserPreferences.setShowRemainTimeSetting(showTimeLeft);
|
||||||
|
Log.d("timeleft on click", showTimeLeft ? "true" : "false");
|
||||||
|
});
|
||||||
|
|
||||||
|
viewBinding.sbPosition.setOnSeekBarChangeListener(this);
|
||||||
|
viewBinding.rewindButton.setOnClickListener(v -> onRewind());
|
||||||
|
viewBinding.rewindButton.setOnLongClickListener(v -> {
|
||||||
|
SkipPreferenceDialog.showSkipPreference(VideoplayerActivity.this,
|
||||||
|
SkipPreferenceDialog.SkipDirection.SKIP_REWIND, null);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
viewBinding.playButton.setIsVideoScreen(true);
|
||||||
|
viewBinding.playButton.setOnClickListener(v -> onPlayPause());
|
||||||
|
viewBinding.fastForwardButton.setOnClickListener(v -> onFastForward());
|
||||||
|
viewBinding.fastForwardButton.setOnLongClickListener(v -> {
|
||||||
|
SkipPreferenceDialog.showSkipPreference(VideoplayerActivity.this,
|
||||||
|
SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, null);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
// To suppress touches directly below the slider
|
||||||
|
viewBinding.bottomControlsContainer.setOnTouchListener((view, motionEvent) -> true);
|
||||||
|
viewBinding.bottomControlsContainer.setFitsSystemWindows(true);
|
||||||
|
viewBinding.videoView.getHolder().addCallback(surfaceHolderCallback);
|
||||||
|
viewBinding.videoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
||||||
|
|
||||||
setupVideoControlsToggler();
|
setupVideoControlsToggler();
|
||||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
|
|
||||||
videoframe.getViewTreeObserver().addOnGlobalLayoutListener(() ->
|
viewBinding.videoPlayerContainer.setOnTouchListener(onVideoviewTouched);
|
||||||
videoview.setAvailableSize(videoframe.getWidth(), videoframe.getHeight()));
|
viewBinding.videoPlayerContainer.getViewTreeObserver().addOnGlobalLayoutListener(() ->
|
||||||
|
viewBinding.videoView.setAvailableSize(
|
||||||
|
viewBinding.videoPlayerContainer.getWidth(), viewBinding.videoPlayerContainer.getHeight()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private final Runnable hideVideoControls = () -> {
|
||||||
protected void onAwaitingVideoSurface() {
|
if (videoControlsShowing) {
|
||||||
setupVideoAspectRatio();
|
Log.d(TAG, "Hiding video controls");
|
||||||
if (videoSurfaceCreated && controller != null) {
|
getSupportActionBar().hide();
|
||||||
Log.d(TAG, "Videosurface already created, setting videosurface now");
|
hideVideoControls(true);
|
||||||
controller.setVideoSurface(videoview.getHolder());
|
videoControlsShowing = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final View.OnTouchListener onVideoviewTouched = (v, event) -> {
|
private final View.OnTouchListener onVideoviewTouched = (v, event) -> {
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
if (event.getAction() != MotionEvent.ACTION_DOWN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
if (PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
videoControlsHider.stop();
|
videoControlsHider.removeCallbacks(hideVideoControls);
|
||||||
|
|
||||||
if (System.currentTimeMillis() - lastScreenTap < 300) {
|
if (System.currentTimeMillis() - lastScreenTap < 300) {
|
||||||
if (event.getX() > v.getMeasuredWidth() / 2.0f) {
|
if (event.getX() > v.getMeasuredWidth() / 2.0f) {
|
||||||
|
@ -207,9 +373,6 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
|
|
||||||
lastScreenTap = System.currentTimeMillis();
|
lastScreenTap = System.currentTimeMillis();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private void showSkipAnimation(boolean isForward) {
|
private void showSkipAnimation(boolean isForward) {
|
||||||
|
@ -220,18 +383,18 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
skipAnimation.setFillAfter(false);
|
skipAnimation.setFillAfter(false);
|
||||||
skipAnimation.setDuration(800);
|
skipAnimation.setDuration(800);
|
||||||
|
|
||||||
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) skipAnimationView.getLayoutParams();
|
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) viewBinding.skipAnimationImage.getLayoutParams();
|
||||||
if (isForward) {
|
if (isForward) {
|
||||||
skipAnimationView.setImageResource(R.drawable.ic_fast_forward_video_white);
|
viewBinding.skipAnimationImage.setImageResource(R.drawable.ic_fast_forward_video_white);
|
||||||
params.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
|
params.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
|
||||||
} else {
|
} else {
|
||||||
skipAnimationView.setImageResource(R.drawable.ic_fast_rewind_video_white);
|
viewBinding.skipAnimationImage.setImageResource(R.drawable.ic_fast_rewind_video_white);
|
||||||
params.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
|
params.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
skipAnimationView.setVisibility(View.VISIBLE);
|
viewBinding.skipAnimationImage.setVisibility(View.VISIBLE);
|
||||||
skipAnimationView.setLayoutParams(params);
|
viewBinding.skipAnimationImage.setLayoutParams(params);
|
||||||
skipAnimationView.startAnimation(skipAnimation);
|
viewBinding.skipAnimationImage.startAnimation(skipAnimation);
|
||||||
skipAnimation.setAnimationListener(new Animation.AnimationListener() {
|
skipAnimation.setAnimationListener(new Animation.AnimationListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationStart(Animation animation) {
|
public void onAnimationStart(Animation animation) {
|
||||||
|
@ -239,7 +402,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animation animation) {
|
public void onAnimationEnd(Animation animation) {
|
||||||
skipAnimationView.setVisibility(View.GONE);
|
viewBinding.skipAnimationImage.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -249,10 +412,9 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
private void setupVideoControlsToggler() {
|
private void setupVideoControlsToggler() {
|
||||||
videoControlsHider.stop();
|
videoControlsHider.removeCallbacks(hideVideoControls);
|
||||||
videoControlsHider.start();
|
videoControlsHider.postDelayed(hideVideoControls, 2500);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupVideoAspectRatio() {
|
private void setupVideoAspectRatio() {
|
||||||
|
@ -260,7 +422,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
Pair<Integer, Integer> videoSize = controller.getVideoSize();
|
Pair<Integer, Integer> videoSize = controller.getVideoSize();
|
||||||
if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
|
if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
|
||||||
Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
|
Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
|
||||||
videoview.setVideoSize(videoSize.first, videoSize.second);
|
viewBinding.videoView.setVideoSize(videoSize.first, videoSize.second);
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Could not determine video size");
|
Log.e(TAG, "Could not determine video size");
|
||||||
}
|
}
|
||||||
|
@ -270,7 +432,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
private void toggleVideoControlsVisibility() {
|
private void toggleVideoControlsVisibility() {
|
||||||
if (videoControlsShowing) {
|
if (videoControlsShowing) {
|
||||||
getSupportActionBar().hide();
|
getSupportActionBar().hide();
|
||||||
hideVideoControls();
|
hideVideoControls(true);
|
||||||
} else {
|
} else {
|
||||||
getSupportActionBar().show();
|
getSupportActionBar().show();
|
||||||
showVideoControls();
|
showVideoControls();
|
||||||
|
@ -278,29 +440,35 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
videoControlsShowing = !videoControlsShowing;
|
videoControlsShowing = !videoControlsShowing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
void onRewind() {
|
||||||
protected void onRewind() {
|
if (controller == null) {
|
||||||
super.onRewind();
|
return;
|
||||||
|
}
|
||||||
|
int curr = controller.getPosition();
|
||||||
|
controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000);
|
||||||
setupVideoControlsToggler();
|
setupVideoControlsToggler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
void onPlayPause() {
|
||||||
protected void onPlayPause() {
|
if (controller == null) {
|
||||||
super.onPlayPause();
|
return;
|
||||||
|
}
|
||||||
|
controller.playPause();
|
||||||
setupVideoControlsToggler();
|
setupVideoControlsToggler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
void onFastForward() {
|
||||||
protected void onFastForward() {
|
if (controller == null) {
|
||||||
super.onFastForward();
|
return;
|
||||||
|
}
|
||||||
|
int curr = controller.getPosition();
|
||||||
|
controller.seekTo(curr + UserPreferences.getFastForwardSecs() * 1000);
|
||||||
setupVideoControlsToggler();
|
setupVideoControlsToggler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private final SurfaceHolder.Callback surfaceHolderCallback = new SurfaceHolder.Callback() {
|
private final SurfaceHolder.Callback surfaceHolderCallback = new SurfaceHolder.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void surfaceChanged(SurfaceHolder holder, int format, int width,
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||||
int height) {
|
|
||||||
holder.setFixedSize(width, height);
|
holder.setFixedSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,8 +494,6 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onReloadNotification(int notificationCode) {
|
protected void onReloadNotification(int notificationCode) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && PictureInPictureUtil.isInPictureInPictureMode(this)) {
|
||||||
if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO
|
if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO
|
||||||
|
@ -344,80 +510,83 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
|
||||||
super.onStartTrackingTouch(seekBar);
|
|
||||||
videoControlsHider.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
|
||||||
super.onStopTrackingTouch(seekBar);
|
|
||||||
setupVideoControlsToggler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onBufferStart() {
|
|
||||||
progressIndicator.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onBufferEnd() {
|
|
||||||
progressIndicator.setVisibility(View.INVISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
private void showVideoControls() {
|
private void showVideoControls() {
|
||||||
videoOverlay.setVisibility(View.VISIBLE);
|
viewBinding.bottomControlsContainer.setVisibility(View.VISIBLE);
|
||||||
controls.setVisibility(View.VISIBLE);
|
viewBinding.controlsContainer.setVisibility(View.VISIBLE);
|
||||||
final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_in);
|
final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_in);
|
||||||
if (animation != null) {
|
if (animation != null) {
|
||||||
videoOverlay.startAnimation(animation);
|
viewBinding.bottomControlsContainer.startAnimation(animation);
|
||||||
controls.startAnimation(animation);
|
viewBinding.controlsContainer.startAnimation(animation);
|
||||||
}
|
}
|
||||||
videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
|
viewBinding.videoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
private void hideVideoControls(boolean showAnimation) {
|
private void hideVideoControls(boolean showAnimation) {
|
||||||
if (showAnimation) {
|
if (showAnimation) {
|
||||||
final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
|
final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
|
||||||
if (animation != null) {
|
if (animation != null) {
|
||||||
videoOverlay.startAnimation(animation);
|
viewBinding.bottomControlsContainer.startAnimation(animation);
|
||||||
controls.startAnimation(animation);
|
viewBinding.controlsContainer.startAnimation(animation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
|
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
|
||||||
| View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
| View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
||||||
videoOverlay.setFitsSystemWindows(true);
|
viewBinding.bottomControlsContainer.setFitsSystemWindows(true);
|
||||||
|
|
||||||
videoOverlay.setVisibility(View.GONE);
|
viewBinding.bottomControlsContainer.setVisibility(View.GONE);
|
||||||
controls.setVisibility(View.GONE);
|
viewBinding.controlsContainer.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hideVideoControls() {
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
hideVideoControls(true);
|
public void onEventMainThread(PlaybackPositionEvent event) {
|
||||||
|
onPositionObserverUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
public void onPlaybackServiceChanged(ServiceEvent event) {
|
||||||
|
if (event.action == ServiceEvent.Action.SERVICE_SHUT_DOWN) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getContentViewResourceId() {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
return R.layout.videoplayer_activity;
|
super.onCreateOptionsMenu(menu);
|
||||||
}
|
requestCastButton(menu);
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
@Override
|
inflater.inflate(R.menu.mediaplayer, menu);
|
||||||
protected void setScreenOn(boolean enable) {
|
return true;
|
||||||
super.setScreenOn(enable);
|
|
||||||
if (enable) {
|
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
||||||
} else {
|
|
||||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
super.onPrepareOptionsMenu(menu);
|
super.onPrepareOptionsMenu(menu);
|
||||||
|
if (controller == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Playable media = controller.getMedia();
|
||||||
|
boolean isFeedMedia = (media instanceof FeedMedia);
|
||||||
|
|
||||||
|
menu.findItem(R.id.open_feed_item).setVisible(isFeedMedia); // FeedMedia implies it belongs to a Feed
|
||||||
|
|
||||||
|
boolean hasWebsiteLink = getWebsiteLinkWithFallback(media) != null;
|
||||||
|
menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink);
|
||||||
|
|
||||||
|
boolean isItemAndHasLink = isFeedMedia && ShareUtils.hasLinkToShare(((FeedMedia) media).getItem());
|
||||||
|
boolean isItemHasDownloadLink = isFeedMedia && ((FeedMedia) media).getDownload_url() != null;
|
||||||
|
menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink);
|
||||||
|
|
||||||
|
menu.findItem(R.id.add_to_favorites_item).setVisible(false);
|
||||||
|
menu.findItem(R.id.remove_from_favorites_item).setVisible(false);
|
||||||
|
if (isFeedMedia) {
|
||||||
|
menu.findItem(R.id.add_to_favorites_item).setVisible(!isFavorite);
|
||||||
|
menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.findItem(R.id.set_sleeptimer_item).setVisible(!controller.sleepTimerActive());
|
||||||
|
menu.findItem(R.id.disable_sleeptimer_item).setVisible(controller.sleepTimerActive());
|
||||||
|
|
||||||
if (PictureInPictureUtil.supportsPictureInPicture(this)) {
|
if (PictureInPictureUtil.supportsPictureInPicture(this)) {
|
||||||
menu.findItem(R.id.player_go_to_picture_in_picture).setVisible(true);
|
menu.findItem(R.id.player_go_to_picture_in_picture).setVisible(true);
|
||||||
}
|
}
|
||||||
|
@ -431,7 +600,161 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
compatEnterPictureInPicture();
|
compatEnterPictureInPicture();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
Intent intent = new Intent(VideoplayerActivity.this, MainActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controller == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Playable media = controller.getMedia();
|
||||||
|
if (media == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final @Nullable FeedItem feedItem = getFeedItem(media); // some options option requires FeedItem
|
||||||
|
if (item.getItemId() == R.id.add_to_favorites_item && feedItem != null) {
|
||||||
|
DBWriter.addFavoriteItem(feedItem);
|
||||||
|
isFavorite = true;
|
||||||
|
invalidateOptionsMenu();
|
||||||
|
} else if (item.getItemId() == R.id.remove_from_favorites_item && feedItem != null) {
|
||||||
|
DBWriter.removeFavoriteItem(feedItem);
|
||||||
|
isFavorite = false;
|
||||||
|
invalidateOptionsMenu();
|
||||||
|
} else if (item.getItemId() == R.id.disable_sleeptimer_item
|
||||||
|
|| item.getItemId() == R.id.set_sleeptimer_item) {
|
||||||
|
new SleepTimerDialog().show(getSupportFragmentManager(), "SleepTimerDialog");
|
||||||
|
} else if (item.getItemId() == R.id.audio_controls) {
|
||||||
|
PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance();
|
||||||
|
dialog.show(getSupportFragmentManager(), "playback_controls");
|
||||||
|
} else if (item.getItemId() == R.id.open_feed_item && feedItem != null) {
|
||||||
|
Intent intent = MainActivity.getIntentToOpenFeed(this, feedItem.getFeedId());
|
||||||
|
startActivity(intent);
|
||||||
|
} else if (item.getItemId() == R.id.visit_website_item) {
|
||||||
|
IntentUtils.openInBrowser(VideoplayerActivity.this, getWebsiteLinkWithFallback(media));
|
||||||
|
} else if (item.getItemId() == R.id.share_item && feedItem != null) {
|
||||||
|
ShareDialog shareDialog = ShareDialog.newInstance(feedItem);
|
||||||
|
shareDialog.show(getSupportFragmentManager(), "ShareEpisodeDialog");
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getWebsiteLinkWithFallback(Playable media) {
|
||||||
|
if (media == null) {
|
||||||
|
return null;
|
||||||
|
} else if (StringUtils.isNotBlank(media.getWebsiteLink())) {
|
||||||
|
return media.getWebsiteLink();
|
||||||
|
} else if (media instanceof FeedMedia) {
|
||||||
|
return FeedItemUtil.getLinkWithFallback(((FeedMedia) media).getItem());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onPositionObserverUpdate() {
|
||||||
|
if (controller == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
|
||||||
|
int currentPosition = converter.convert(controller.getPosition());
|
||||||
|
int duration = converter.convert(controller.getDuration());
|
||||||
|
int remainingTime = converter.convert(
|
||||||
|
controller.getDuration() - controller.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;
|
||||||
|
}
|
||||||
|
viewBinding.positionLabel.setText(Converter.getDurationStringLong(currentPosition));
|
||||||
|
if (showTimeLeft) {
|
||||||
|
viewBinding.durationLabel.setText("-" + Converter.getDurationStringLong(remainingTime));
|
||||||
|
} else {
|
||||||
|
viewBinding.durationLabel.setText(Converter.getDurationStringLong(duration));
|
||||||
|
}
|
||||||
|
updateProgressbarPosition(currentPosition, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateProgressbarPosition(int position, int duration) {
|
||||||
|
Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration + ")");
|
||||||
|
float progress = ((float) position) / duration;
|
||||||
|
viewBinding.sbPosition.setProgress((int) (progress * viewBinding.sbPosition.getMax()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||||
|
if (controller == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fromUser) {
|
||||||
|
prog = progress / ((float) seekBar.getMax());
|
||||||
|
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
|
||||||
|
int position = converter.convert((int) (prog * controller.getDuration()));
|
||||||
|
viewBinding.seekPositionLabel.setText(Converter.getDurationStringLong(position));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
viewBinding.seekCardView.setScaleX(.8f);
|
||||||
|
viewBinding.seekCardView.setScaleY(.8f);
|
||||||
|
viewBinding.seekCardView.animate()
|
||||||
|
.setInterpolator(new FastOutSlowInInterpolator())
|
||||||
|
.alpha(1f).scaleX(1f).scaleY(1f)
|
||||||
|
.setDuration(200)
|
||||||
|
.start();
|
||||||
|
videoControlsHider.removeCallbacks(hideVideoControls);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
|
if (controller != null) {
|
||||||
|
controller.seekTo((int) (prog * controller.getDuration()));
|
||||||
|
}
|
||||||
|
viewBinding.seekCardView.setScaleX(1f);
|
||||||
|
viewBinding.seekCardView.setScaleY(1f);
|
||||||
|
viewBinding.seekCardView.animate()
|
||||||
|
.setInterpolator(new FastOutSlowInInterpolator())
|
||||||
|
.alpha(0f).scaleX(.8f).scaleY(.8f)
|
||||||
|
.setDuration(200)
|
||||||
|
.start();
|
||||||
|
setupVideoControlsToggler();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkFavorite() {
|
||||||
|
FeedItem feedItem = getFeedItem(controller.getMedia());
|
||||||
|
if (feedItem == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (disposable != null) {
|
||||||
|
disposable.dispose();
|
||||||
|
}
|
||||||
|
disposable = Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId()))
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(
|
||||||
|
item -> {
|
||||||
|
boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE);
|
||||||
|
if (isFavorite != isFav) {
|
||||||
|
isFavorite = isFav;
|
||||||
|
invalidateOptionsMenu();
|
||||||
|
}
|
||||||
|
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static FeedItem getFeedItem(@Nullable Playable playable) {
|
||||||
|
if (playable instanceof FeedMedia) {
|
||||||
|
return ((FeedMedia) playable).getItem();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void compatEnterPictureInPicture() {
|
private void compatEnterPictureInPicture() {
|
||||||
|
@ -442,43 +765,6 @@ public class VideoplayerActivity extends MediaplayerActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class VideoControlsHider extends Handler {
|
|
||||||
|
|
||||||
private static final int DELAY = 2500;
|
|
||||||
|
|
||||||
private WeakReference<VideoplayerActivity> activity;
|
|
||||||
|
|
||||||
VideoControlsHider(VideoplayerActivity activity) {
|
|
||||||
this.activity = new WeakReference<>(activity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Runnable hideVideoControls = () -> {
|
|
||||||
VideoplayerActivity vpa = activity != null ? activity.get() : null;
|
|
||||||
if (vpa == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (vpa.videoControlsShowing) {
|
|
||||||
Log.d(TAG, "Hiding video controls");
|
|
||||||
ActionBar actionBar = vpa.getSupportActionBar();
|
|
||||||
if (actionBar != null) {
|
|
||||||
actionBar.hide();
|
|
||||||
}
|
|
||||||
vpa.hideVideoControls();
|
|
||||||
vpa.videoControlsShowing = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public void start() {
|
|
||||||
this.postDelayed(hideVideoControls, DELAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop() {
|
|
||||||
this.removeCallbacks(hideVideoControls);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//Hardware keyboard support
|
//Hardware keyboard support
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
|
|
|
@ -6,16 +6,16 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:background="@color/black"
|
android:background="@color/black"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:id="@+id/videoframe">
|
android:id="@+id/videoPlayerContainer">
|
||||||
|
|
||||||
<de.danoeh.antennapod.view.AspectRatioVideoView
|
<de.danoeh.antennapod.view.AspectRatioVideoView
|
||||||
android:id="@+id/videoview"
|
android:id="@+id/videoView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center" />
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progressIndicator"
|
android:id="@+id/progressBar"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
android:visibility="invisible" />
|
android:visibility="invisible" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/controls"
|
android:id="@+id/controlsContainer"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/butRev"
|
android:id="@+id/rewindButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
app:srcCompat="@drawable/ic_fast_rewind_video_white" />
|
app:srcCompat="@drawable/ic_fast_rewind_video_white" />
|
||||||
|
|
||||||
<de.danoeh.antennapod.view.PlayButton
|
<de.danoeh.antennapod.view.PlayButton
|
||||||
android:id="@+id/butPlay"
|
android:id="@+id/playButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
app:srcCompat="@drawable/ic_pause_video_white" />
|
app:srcCompat="@drawable/ic_pause_video_white" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/butFF"
|
android:id="@+id/fastForwardButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/skip_animation"
|
android:id="@+id/skipAnimationImage"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
@ -70,14 +70,14 @@
|
||||||
android:layout_gravity="center"/>
|
android:layout_gravity="center"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/overlay"
|
android:id="@+id/bottomControlsContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|center"
|
android:layout_gravity="bottom|center"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/cardViewSeek"
|
android:id="@+id/seekCardView"
|
||||||
android:alpha="0"
|
android:alpha="0"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -89,7 +89,7 @@
|
||||||
tools:alpha="1">
|
tools:alpha="1">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtvSeek"
|
android:id="@+id/seekPositionLabel"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -104,7 +104,6 @@
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/timecontrol"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:background="#80000000"
|
android:background="#80000000"
|
||||||
|
@ -112,7 +111,7 @@
|
||||||
android:paddingTop="8dp">
|
android:paddingTop="8dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtvPosition"
|
android:id="@+id/positionLabel"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
|
@ -129,7 +128,7 @@
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtvLength"
|
android:id="@+id/durationLabel"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
|
@ -149,10 +148,10 @@
|
||||||
android:id="@+id/sbPosition"
|
android:id="@+id/sbPosition"
|
||||||
android:layout_width="0px"
|
android:layout_width="0px"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_toLeftOf="@+id/txtvLength"
|
android:layout_toLeftOf="@+id/durationLabel"
|
||||||
android:layout_toStartOf="@+id/txtvLength"
|
android:layout_toStartOf="@+id/durationLabel"
|
||||||
android:layout_toRightOf="@+id/txtvPosition"
|
android:layout_toRightOf="@+id/positionLabel"
|
||||||
android:layout_toEndOf="@+id/txtvPosition"
|
android:layout_toEndOf="@+id/positionLabel"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:max="500" />
|
android:max="500" />
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue