Swich playback to an ExoPlayer based backend
This commit is contained in:
parent
13ea8dd04d
commit
4648e56b15
7 changed files with 246 additions and 55 deletions
|
@ -161,6 +161,8 @@ dependencies {
|
||||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
|
implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
|
||||||
implementation 'cat.ereza:customactivityoncrash:2.3.0'
|
implementation 'cat.ereza:customactivityoncrash:2.3.0'
|
||||||
debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6'
|
debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6'
|
||||||
|
|
||||||
|
implementation 'com.google.android.exoplayer:exoplayer-core:2.16.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: '../spotless.gradle'
|
apply from: '../spotless.gradle'
|
||||||
|
|
|
@ -82,18 +82,18 @@ class RealSongRepository(private val context: Context) : SongRepository {
|
||||||
return listOf(
|
return listOf(
|
||||||
Song(
|
Song(
|
||||||
1,
|
1,
|
||||||
"example",
|
"river-lake convergence",
|
||||||
1,
|
1,
|
||||||
2021,
|
2021,
|
||||||
23723478,
|
23723478,
|
||||||
"uuh idk what goes here yet, i think the file path or something",
|
"https://versary.town/river-lake-convergence.mp3",
|
||||||
1639589623,
|
1639589623,
|
||||||
1,
|
1,
|
||||||
"example",
|
"river-lake convergence",
|
||||||
1,
|
1,
|
||||||
"example",
|
"annieversary",
|
||||||
"example",
|
null,
|
||||||
"example"
|
"annieversary"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,7 +166,7 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion
|
||||||
player.setAudioAttributes(
|
player.setAudioAttributes(
|
||||||
AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_MUSIC).build()
|
AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_MUSIC).build()
|
||||||
)
|
)
|
||||||
player.prepare()
|
player.prepareAsync()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -23,10 +23,8 @@ import android.net.Uri;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import code.name.monkey.retromusic.R;
|
import code.name.monkey.retromusic.R;
|
||||||
import code.name.monkey.retromusic.service.playback.Playback;
|
import code.name.monkey.retromusic.service.playback.Playback;
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
@ -83,7 +81,7 @@ public class MultiPlayer
|
||||||
player.setDataSource(path);
|
player.setDataSource(path);
|
||||||
}
|
}
|
||||||
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||||
player.prepare();
|
player.prepareAsync();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,27 +14,11 @@
|
||||||
|
|
||||||
package code.name.monkey.retromusic.service;
|
package code.name.monkey.retromusic.service;
|
||||||
|
|
||||||
import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_PLAYABLE;
|
|
||||||
import static androidx.media.MediaBrowserServiceCompat.BrowserRoot.EXTRA_RECENT;
|
|
||||||
import static org.koin.java.KoinJavaComponent.get;
|
|
||||||
import static code.name.monkey.retromusic.ConstantsKt.ALBUM_ART_ON_LOCK_SCREEN;
|
|
||||||
import static code.name.monkey.retromusic.ConstantsKt.BLURRED_ALBUM_ART;
|
|
||||||
import static code.name.monkey.retromusic.ConstantsKt.CLASSIC_NOTIFICATION;
|
|
||||||
import static code.name.monkey.retromusic.ConstantsKt.COLORED_NOTIFICATION;
|
|
||||||
import static code.name.monkey.retromusic.ConstantsKt.CROSS_FADE_DURATION;
|
|
||||||
import static code.name.monkey.retromusic.ConstantsKt.TOGGLE_HEADSET;
|
|
||||||
import static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator;
|
|
||||||
|
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.appwidget.AppWidgetManager;
|
import android.appwidget.AppWidgetManager;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.*;
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.pm.ServiceInfo;
|
import android.content.pm.ServiceInfo;
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
@ -42,16 +26,10 @@ import android.graphics.Point;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.media.audiofx.AudioEffect;
|
import android.media.audiofx.AudioEffect;
|
||||||
import android.os.Binder;
|
import android.os.*;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Build.VERSION;
|
import android.os.Build.VERSION;
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.HandlerThread;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.PowerManager;
|
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
import android.os.Build.VERSION_CODES;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.support.v4.media.MediaBrowserCompat;
|
import android.support.v4.media.MediaBrowserCompat;
|
||||||
import android.support.v4.media.MediaDescriptionCompat;
|
import android.support.v4.media.MediaDescriptionCompat;
|
||||||
|
@ -62,30 +40,14 @@ import android.telephony.PhoneStateListener;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media.MediaBrowserServiceCompat;
|
import androidx.media.MediaBrowserServiceCompat;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.bumptech.glide.RequestBuilder;
|
|
||||||
import com.bumptech.glide.request.target.SimpleTarget;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils;
|
import code.name.monkey.appthemehelper.util.VersionUtils;
|
||||||
import code.name.monkey.retromusic.R;
|
import code.name.monkey.retromusic.R;
|
||||||
import code.name.monkey.retromusic.activities.LockScreenActivity;
|
import code.name.monkey.retromusic.activities.LockScreenActivity;
|
||||||
import code.name.monkey.retromusic.appwidgets.AppWidgetBig;
|
import code.name.monkey.retromusic.appwidgets.*;
|
||||||
import code.name.monkey.retromusic.appwidgets.AppWidgetCard;
|
|
||||||
import code.name.monkey.retromusic.appwidgets.AppWidgetClassic;
|
|
||||||
import code.name.monkey.retromusic.appwidgets.AppWidgetMD3;
|
|
||||||
import code.name.monkey.retromusic.appwidgets.AppWidgetSmall;
|
|
||||||
import code.name.monkey.retromusic.appwidgets.AppWidgetText;
|
|
||||||
import code.name.monkey.retromusic.auto.AutoMediaIDHelper;
|
import code.name.monkey.retromusic.auto.AutoMediaIDHelper;
|
||||||
import code.name.monkey.retromusic.auto.AutoMusicProvider;
|
import code.name.monkey.retromusic.auto.AutoMusicProvider;
|
||||||
import code.name.monkey.retromusic.glide.BlurTransformation;
|
import code.name.monkey.retromusic.glide.BlurTransformation;
|
||||||
|
@ -108,7 +70,18 @@ import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
import code.name.monkey.retromusic.util.RetroUtil;
|
import code.name.monkey.retromusic.util.RetroUtil;
|
||||||
import code.name.monkey.retromusic.volume.AudioVolumeObserver;
|
import code.name.monkey.retromusic.volume.AudioVolumeObserver;
|
||||||
import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener;
|
import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener;
|
||||||
|
import com.bumptech.glide.RequestBuilder;
|
||||||
|
import com.bumptech.glide.request.target.SimpleTarget;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
|
import software.lavender.music.player.ExoPlayerPlayback;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_PLAYABLE;
|
||||||
|
import static androidx.media.MediaBrowserServiceCompat.BrowserRoot.EXTRA_RECENT;
|
||||||
|
import static code.name.monkey.retromusic.ConstantsKt.*;
|
||||||
|
import static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator;
|
||||||
|
import static org.koin.java.KoinJavaComponent.get;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Karim Abou Zeid (kabouzeid), Andrew Neal
|
* @author Karim Abou Zeid (kabouzeid), Andrew Neal
|
||||||
|
@ -162,6 +135,7 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
public static final int DUCK = 7;
|
public static final int DUCK = 7;
|
||||||
public static final int UNDUCK = 8;
|
public static final int UNDUCK = 8;
|
||||||
public static final int RESTORE_QUEUES = 9;
|
public static final int RESTORE_QUEUES = 9;
|
||||||
|
|
||||||
public static final int SHUFFLE_MODE_NONE = 0;
|
public static final int SHUFFLE_MODE_NONE = 0;
|
||||||
public static final int SHUFFLE_MODE_SHUFFLE = 1;
|
public static final int SHUFFLE_MODE_SHUFFLE = 1;
|
||||||
public static final int REPEAT_MODE_NONE = 0;
|
public static final int REPEAT_MODE_NONE = 0;
|
||||||
|
@ -384,7 +358,7 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getTrackUri(@NonNull Song song) {
|
private static String getTrackUri(@NonNull Song song) {
|
||||||
return MusicUtil.INSTANCE.getSongFileUri(song.getId()).toString();
|
return song.getData();//MusicUtil.INSTANCE.getSongFileUri(song.getId()).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -407,8 +381,9 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
playerHandler = new PlaybackHandler(this, musicPlayerHandlerThread.getLooper());
|
playerHandler = new PlaybackHandler(this, musicPlayerHandlerThread.getLooper());
|
||||||
|
|
||||||
// Set MultiPlayer when crossfade duration is 0 i.e. off
|
// Set MultiPlayer when crossfade duration is 0 i.e. off
|
||||||
|
// TODO: do crossfading in exoplayer or remove the feature entirely for now, crossfadeplayer will NOT work right now
|
||||||
if (PreferenceUtil.INSTANCE.getCrossFadeDuration() == 0) {
|
if (PreferenceUtil.INSTANCE.getCrossFadeDuration() == 0) {
|
||||||
playback = new MultiPlayer(this);
|
playback = new ExoPlayerPlayback(this);
|
||||||
} else {
|
} else {
|
||||||
playback = new CrossFadePlayer(this);
|
playback = new CrossFadePlayer(this);
|
||||||
}
|
}
|
||||||
|
@ -904,7 +879,7 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
playback.release();
|
playback.release();
|
||||||
}
|
}
|
||||||
playback = null;
|
playback = null;
|
||||||
playback = new MultiPlayer(this);
|
playback = new ExoPlayerPlayback(this);
|
||||||
playback.setCallbacks(this);
|
playback.setCallbacks(this);
|
||||||
if (openTrackAndPrepareNextAt(position)) {
|
if (openTrackAndPrepareNextAt(position)) {
|
||||||
seek(progress);
|
seek(progress);
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package software.lavender.music.extensions
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Looper
|
||||||
|
|
||||||
|
inline val Looper.isCurrentThreadCompat: Boolean
|
||||||
|
get() = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) isCurrentThread else Thread.currentThread() == thread
|
|
@ -0,0 +1,209 @@
|
||||||
|
package software.lavender.music.player
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.media.audiofx.AudioEffect
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.HandlerThread
|
||||||
|
import android.os.PowerManager
|
||||||
|
import android.widget.Toast
|
||||||
|
import code.name.monkey.retromusic.service.playback.Playback
|
||||||
|
import com.google.android.exoplayer2.ExoPlayer
|
||||||
|
import com.google.android.exoplayer2.MediaItem
|
||||||
|
import com.google.android.exoplayer2.PlaybackException
|
||||||
|
import com.google.android.exoplayer2.Player
|
||||||
|
import kotlinx.coroutines.android.asCoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import software.lavender.music.extensions.isCurrentThreadCompat
|
||||||
|
|
||||||
|
// TODO: implement audio offloading, see: https://exoplayer.dev/battery-consumption.html
|
||||||
|
class ExoPlayerPlayback(private val context: Context) : Playback, Player.Listener {
|
||||||
|
private val handler = Handler(HandlerThread("ExoPlayerHandler").apply { start() }.looper)
|
||||||
|
private val dispatcher = handler.asCoroutineDispatcher("ExoPlayerDispatcher")
|
||||||
|
private val player =
|
||||||
|
ExoPlayer.Builder(context).setWakeMode(PowerManager.PARTIAL_WAKE_LOCK).setLooper(handler.looper).build()
|
||||||
|
|
||||||
|
private var initialized: Boolean = false
|
||||||
|
override val isInitialized: Boolean
|
||||||
|
get() = initialized
|
||||||
|
private var playing = false
|
||||||
|
override val isPlaying: Boolean
|
||||||
|
get() = isInitialized && ensurePlayerThread { player.isPlaying }
|
||||||
|
private var sessionId = -1
|
||||||
|
override val audioSessionId: Int
|
||||||
|
get() = ensurePlayerThread { player.audioSessionId }
|
||||||
|
|
||||||
|
private var callbacks: Playback.PlaybackCallbacks? = null
|
||||||
|
private var hasNext = false
|
||||||
|
|
||||||
|
override fun setDataSource(path: String): Boolean {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
path,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
val success = ensurePlayerThread {
|
||||||
|
initialized = false
|
||||||
|
try {
|
||||||
|
player.stop()
|
||||||
|
player.clearMediaItems()
|
||||||
|
player.setMediaItem(MediaItem.fromUri(path))
|
||||||
|
player.prepare()
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
e.message,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
e.printStackTrace()
|
||||||
|
initialized = false
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!success) {
|
||||||
|
return initialized
|
||||||
|
}
|
||||||
|
// TODO: listeners?
|
||||||
|
ensurePlayerThread {
|
||||||
|
player.addListener(this)
|
||||||
|
}
|
||||||
|
val intent = Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)
|
||||||
|
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionId)
|
||||||
|
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.packageName)
|
||||||
|
intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC)
|
||||||
|
context.sendBroadcast(intent)
|
||||||
|
initialized = true
|
||||||
|
return initialized
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setNextDataSource(path: String?) = ensurePlayerThread {
|
||||||
|
if (path == null) {
|
||||||
|
return@ensurePlayerThread
|
||||||
|
}
|
||||||
|
player.addMediaItem(MediaItem.fromUri(path))
|
||||||
|
hasNext = true
|
||||||
|
|
||||||
|
// TODO: i think we need to do more stuff here
|
||||||
|
// TODO: gapless?
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setCallbacks(callbacks: Playback.PlaybackCallbacks) {
|
||||||
|
this.callbacks = callbacks
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun start() = ensurePlayerThread {
|
||||||
|
try {
|
||||||
|
player.play()
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stop() = ensurePlayerThread {
|
||||||
|
player.stop()
|
||||||
|
player.clearMediaItems()
|
||||||
|
initialized = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun release() = ensurePlayerThread {
|
||||||
|
player.stop()
|
||||||
|
player.clearMediaItems()
|
||||||
|
initialized = false
|
||||||
|
player.release()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pause() = ensurePlayerThread {
|
||||||
|
try {
|
||||||
|
player.pause()
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun duration() = if (!initialized) -1 else ensurePlayerThread {
|
||||||
|
try {
|
||||||
|
player.duration.toInt()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun position() = if (!initialized) -1 else ensurePlayerThread {
|
||||||
|
try {
|
||||||
|
player.currentPosition.toInt()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: do seeking via
|
||||||
|
override fun seek(whereto: Int) = ensurePlayerThread {
|
||||||
|
try {
|
||||||
|
player.seekTo(whereto.toLong())
|
||||||
|
player.currentPosition.toInt()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setVolume(vol: Float) = ensurePlayerThread {
|
||||||
|
try {
|
||||||
|
player.volume = vol
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setAudioSessionId(sessionId: Int) = ensurePlayerThread {
|
||||||
|
try {
|
||||||
|
player.audioSessionId = sessionId
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setCrossFadeDuration(duration: Int) {
|
||||||
|
// TODO: can we do crossfading in exoplayer directly??
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlayerError(error: PlaybackException) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
error.message,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: tbh with first class playlist support in exoplayer we should probably get rid of the weird track switching behaviour asap
|
||||||
|
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
||||||
|
if (hasNext) {
|
||||||
|
hasNext = false
|
||||||
|
callbacks?.onTrackWentToNext()
|
||||||
|
} else {
|
||||||
|
callbacks?.onTrackEnded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
|
playing = isPlaying
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAudioSessionIdChanged(audioSessionId: Int) {
|
||||||
|
sessionId = audioSessionId
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> ensurePlayerThread(block: () -> T): T =
|
||||||
|
if (player.playbackLooper.isCurrentThreadCompat) block() else runBlocking(dispatcher) { block() }
|
||||||
|
}
|
Loading…
Reference in a new issue