Fix some cases of doing I/O in main thread (#8091)

This commit is contained in:
Hans-Peter Lehmann
2025-11-14 07:53:28 +01:00
committed by GitHub
parent 458d8732c6
commit 2c65402a9b
2 changed files with 90 additions and 53 deletions

View File

@ -3,6 +3,7 @@ package de.danoeh.antennapod.ui.screen.playback;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -20,6 +21,10 @@ import de.danoeh.antennapod.event.playback.SpeedChangedEvent;
import de.danoeh.antennapod.playback.service.PlaybackController; import de.danoeh.antennapod.playback.service.PlaybackController;
import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.ui.view.ItemOffsetDecoration; import de.danoeh.antennapod.ui.view.ItemOffsetDecoration;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode; import org.greenrobot.eventbus.ThreadMode;
@ -31,12 +36,14 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
public class VariableSpeedDialog extends BottomSheetDialogFragment { public class VariableSpeedDialog extends BottomSheetDialogFragment {
private static final String TAG = "VariableSpeedDialog";
private SpeedSelectionAdapter adapter; private SpeedSelectionAdapter adapter;
private PlaybackController controller; private PlaybackController controller;
private final List<Float> selectedSpeeds; private final List<Float> selectedSpeeds;
private PlaybackSpeedSeekBar speedSeekBar; private PlaybackSpeedSeekBar speedSeekBar;
private Chip addCurrentSpeedChip; private Chip addCurrentSpeedChip;
private CheckBox skipSilenceCheckbox; private CheckBox skipSilenceCheckbox;
private Disposable disposable;
public VariableSpeedDialog() { public VariableSpeedDialog() {
DecimalFormatSymbols format = new DecimalFormatSymbols(Locale.US); DecimalFormatSymbols format = new DecimalFormatSymbols(Locale.US);
@ -50,14 +57,12 @@ public class VariableSpeedDialog extends BottomSheetDialogFragment {
controller = new PlaybackController(getActivity()) { controller = new PlaybackController(getActivity()) {
@Override @Override
public void loadMediaInfo() { public void loadMediaInfo() {
updateSpeed(new SpeedChangedEvent(controller.getCurrentPlaybackSpeedMultiplier())); VariableSpeedDialog.this.loadMediaInfo();
updateSkipSilence(controller.getCurrentPlaybackSkipSilence());
} }
}; };
controller.init(); controller.init();
EventBus.getDefault().register(this); EventBus.getDefault().register(this);
updateSpeed(new SpeedChangedEvent(controller.getCurrentPlaybackSpeedMultiplier())); loadMediaInfo();
updateSkipSilence(controller.getCurrentPlaybackSkipSilence());
} }
@Override @Override
@ -66,6 +71,32 @@ public class VariableSpeedDialog extends BottomSheetDialogFragment {
controller.release(); controller.release();
controller = null; controller = null;
EventBus.getDefault().unregister(this); EventBus.getDefault().unregister(this);
if (disposable != null) {
disposable.dispose();
}
}
private void loadMediaInfo() {
if (disposable != null) {
disposable.dispose();
}
disposable = Observable.fromCallable(() -> {
if (controller == null) {
return null;
}
// Make sure the media is loaded in case getCurrentPlaybackSpeedMultiplier has to access it
return controller.getMedia();
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(pair -> {
if (controller == null) {
return;
}
updateSpeed(new SpeedChangedEvent(controller.getCurrentPlaybackSpeedMultiplier()));
updateSkipSilence(controller.getCurrentPlaybackSkipSilence());
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)

View File

@ -68,7 +68,7 @@ import de.danoeh.antennapod.ui.screen.playback.MediaPlayerErrorDialog;
import de.danoeh.antennapod.ui.screen.playback.PlaybackControlsDialog; import de.danoeh.antennapod.ui.screen.playback.PlaybackControlsDialog;
import de.danoeh.antennapod.ui.screen.playback.SleepTimerDialog; import de.danoeh.antennapod.ui.screen.playback.SleepTimerDialog;
import de.danoeh.antennapod.ui.screen.playback.VariableSpeedDialog; import de.danoeh.antennapod.ui.screen.playback.VariableSpeedDialog;
import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Maybe;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.schedulers.Schedulers;
@ -189,7 +189,6 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.
controller = newPlaybackController(); controller = newPlaybackController();
controller.init(); controller.init();
loadMediaInfo(); loadMediaInfo();
onPositionObserverUpdate();
EventBus.getDefault().register(this); EventBus.getDefault().register(this);
} }
@ -266,24 +265,55 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.
protected void loadMediaInfo() { protected void loadMediaInfo() {
Log.d(TAG, "loadMediaInfo()"); Log.d(TAG, "loadMediaInfo()");
if (controller == null || controller.getMedia() == null) { if (disposable != null) {
return; disposable.dispose();
}
if (controller.getStatus() == PlayerStatus.PLAYING && !controller.isPlayingVideoLocally()) {
Log.d(TAG, "Closing, no longer video");
destroyingDueToReload = true;
finish();
new MainActivityStarter(this).withOpenPlayer().start();
return;
}
showTimeLeft = UserPreferences.shouldShowRemainingTime();
onPositionObserverUpdate();
checkFavorite();
Playable media = controller.getMedia();
if (media != null) {
getSupportActionBar().setSubtitle(media.getEpisodeTitle());
getSupportActionBar().setTitle(media.getFeedTitle());
} }
disposable = Maybe.<Pair<Playable, FeedItem>>create(emitter -> {
if (controller == null) {
emitter.onComplete();
return;
}
Playable media = controller.getMedia();
if (media == null) {
emitter.onComplete();
return;
}
FeedItem feedItem = getFeedItem(controller.getMedia());
if (feedItem != null) {
feedItem = DBReader.getFeedItem(feedItem.getId());
}
emitter.onSuccess(new Pair<>(media, feedItem));
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
result -> {
final Playable media = result.first;
final FeedItem item = result.second;
if (controller.getStatus() == PlayerStatus.PLAYING
&& !controller.isPlayingVideoLocally()) {
Log.d(TAG, "Closing, no longer video");
destroyingDueToReload = true;
finish();
new MainActivityStarter(this).withOpenPlayer().start();
return;
}
showTimeLeft = UserPreferences.shouldShowRemainingTime();
onPositionObserverUpdate(
new PlaybackPositionEvent(controller.getPosition(), controller.getDuration()));
if (media != null) {
getSupportActionBar().setSubtitle(media.getEpisodeTitle());
getSupportActionBar().setTitle(media.getFeedTitle());
}
boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE);
if (isFavorite != isFav) {
isFavorite = isFav;
invalidateOptionsMenu();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error))
);
} }
protected void setupView() { protected void setupView() {
@ -532,7 +562,7 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(PlaybackPositionEvent event) { public void onEventMainThread(PlaybackPositionEvent event) {
onPositionObserverUpdate(); onPositionObserverUpdate(event);
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
@ -659,24 +689,21 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.
return null; return null;
} }
void onPositionObserverUpdate() { void onPositionObserverUpdate(PlaybackPositionEvent event) {
if (controller == null) { if (controller == null) {
return; return;
} }
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier()); TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
int currentPosition = converter.convert(controller.getPosition()); int currentPosition = converter.convert(event.getPosition());
int duration = converter.convert(controller.getDuration()); int duration = converter.convert(event.getDuration());
int remainingTime = converter.convert(
controller.getDuration() - controller.getPosition());
Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition)); Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition));
if (currentPosition == Playable.INVALID_TIME if (currentPosition == Playable.INVALID_TIME || duration == Playable.INVALID_TIME) {
|| duration == Playable.INVALID_TIME) {
Log.w(TAG, "Could not react to position observer update because of invalid time"); Log.w(TAG, "Could not react to position observer update because of invalid time");
return; return;
} }
viewBinding.positionLabel.setText(Converter.getDurationStringLong(currentPosition)); viewBinding.positionLabel.setText(Converter.getDurationStringLong(currentPosition));
if (showTimeLeft) { if (showTimeLeft) {
int remainingTime = converter.convert(event.getDuration() - event.getPosition());
viewBinding.durationLabel.setText("-" + Converter.getDurationStringLong(remainingTime)); viewBinding.durationLabel.setText("-" + Converter.getDurationStringLong(remainingTime));
} else { } else {
viewBinding.durationLabel.setText(Converter.getDurationStringLong(duration)); viewBinding.durationLabel.setText(Converter.getDurationStringLong(duration));
@ -730,27 +757,6 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.
setupVideoControlsToggler(); 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 @Nullable
private static FeedItem getFeedItem(@Nullable Playable playable) { private static FeedItem getFeedItem(@Nullable Playable playable) {
if (playable instanceof FeedMedia) { if (playable instanceof FeedMedia) {