2
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-08-22 10:09:39 +00:00

PlayerHolder: use object class to implement singleton pattern

This commit is contained in:
Profpatsch 2025-01-01 17:46:48 +01:00 committed by Stypox
parent cc3ecd4169
commit 38ed1da79e
No known key found for this signature in database
GPG Key ID: 4BDF1B40A49FDD23
7 changed files with 43 additions and 53 deletions

View File

@ -849,7 +849,7 @@ public class MainActivity extends AppCompatActivity {
return;
}
if (PlayerHolder.Companion.getInstance().isPlayerOpen()) {
if (PlayerHolder.INSTANCE.isPlayerOpen()) {
// if the player is already open, no need for a broadcast receiver
openMiniPlayerIfMissing();
} else {
@ -859,7 +859,7 @@ public class MainActivity extends AppCompatActivity {
public void onReceive(final Context context, final Intent intent) {
if (Objects.equals(intent.getAction(),
VideoDetailFragment.ACTION_PLAYER_STARTED)
&& PlayerHolder.Companion.getInstance().isPlayerOpen()) {
&& PlayerHolder.INSTANCE.isPlayerOpen()) {
openMiniPlayerIfMissing();
// At this point the player is added 100%, we can unregister. Other actions
// are useless since the fragment will not be removed after that.
@ -874,7 +874,7 @@ public class MainActivity extends AppCompatActivity {
// If the PlayerHolder is not bound yet, but the service is running, try to bind to it.
// Once the connection is established, the ACTION_PLAYER_STARTED will be sent.
PlayerHolder.Companion.getInstance().tryBindIfNeeded(this);
PlayerHolder.INSTANCE.tryBindIfNeeded(this);
}
}

View File

@ -701,7 +701,7 @@ public class RouterActivity extends AppCompatActivity {
}
// ...the player is not running or in normal Video-mode/type
final PlayerType playerType = PlayerHolder.Companion.getInstance().getType();
final PlayerType playerType = PlayerHolder.INSTANCE.getType();
return playerType == null || playerType == PlayerType.MAIN;
}

View File

@ -100,7 +100,7 @@ import org.schabi.newpipe.player.PlayerType
import org.schabi.newpipe.player.event.OnKeyDownListener
import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener
import org.schabi.newpipe.player.helper.PlayerHelper
import org.schabi.newpipe.player.helper.PlayerHolder.Companion.getInstance
import org.schabi.newpipe.player.helper.PlayerHolder
import org.schabi.newpipe.player.playqueue.PlayQueue
import org.schabi.newpipe.player.playqueue.SinglePlayQueue
import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent
@ -212,7 +212,6 @@ class VideoDetailFragment :
private var settingsContentObserver: ContentObserver? = null
private var playerService: PlayerService? = null
private var player: Player? = null
private val playerHolder = getInstance()
/*//////////////////////////////////////////////////////////////////////////
// Service management
@ -367,9 +366,9 @@ class VideoDetailFragment :
// Stop the service when user leaves the app with double back press
// if video player is selected. Otherwise unbind
if (activity.isFinishing() && this.isPlayerAvailable && player!!.videoPlayerSelected()) {
playerHolder.stopService()
PlayerHolder.stopService()
} else {
playerHolder.setListener(null)
PlayerHolder.setListener(null)
}
PreferenceManager.getDefaultSharedPreferences(activity)
@ -768,10 +767,10 @@ class VideoDetailFragment :
)
setupBottomPlayer()
if (!playerHolder.isBound) {
if (!PlayerHolder.isBound) {
setHeightThumbnail()
} else {
playerHolder.startService(false, this)
PlayerHolder.startService(false, this)
}
}
@ -1175,7 +1174,7 @@ class VideoDetailFragment :
// See UI changes while remote playQueue changes
if (!this.isPlayerAvailable) {
playerHolder.startService(false, this)
PlayerHolder.startService(false, this)
} else {
// FIXME Workaround #7427
player!!.setRecovery()
@ -1245,7 +1244,7 @@ class VideoDetailFragment :
private fun openNormalBackgroundPlayer(append: Boolean) {
// See UI changes while remote playQueue changes
if (!this.isPlayerAvailable) {
playerHolder.startService(false, this)
PlayerHolder.startService(false, this)
}
val queue = setupPlayQueueForIntent(append)
@ -1263,7 +1262,7 @@ class VideoDetailFragment :
private fun openMainPlayer() {
if (noPlayerServiceAvailable()) {
playerHolder.startService(autoPlayEnabled, this)
PlayerHolder.startService(autoPlayEnabled, this)
return
}
if (currentInfo == null) {
@ -1298,7 +1297,7 @@ class VideoDetailFragment :
playerService!!.stopForImmediateReusing()
root.ifPresent(Consumer { view: View -> view.setVisibility(View.GONE) })
} else {
playerHolder.stopService()
PlayerHolder.stopService()
}
}
@ -1551,8 +1550,8 @@ class VideoDetailFragment :
bottomSheetBehavior!!.setState(BottomSheetBehavior.STATE_COLLAPSED)
}
// Rebound to the service if it was closed via notification or mini player
if (!playerHolder.isBound) {
playerHolder.startService(
if (!PlayerHolder.isBound) {
PlayerHolder.startService(
false, this@VideoDetailFragment
)
}
@ -2472,7 +2471,7 @@ class VideoDetailFragment :
if (currentWorker != null) {
currentWorker!!.dispose()
}
playerHolder.stopService()
PlayerHolder.stopService()
setInitialData(0, null, "", null)
currentInfo = null
updateOverlayData(null, null, mutableListOf<Image>())

View File

@ -252,7 +252,7 @@ public final class InfoItemDialog {
* @return the current {@link Builder} instance
*/
public Builder addEnqueueEntriesIfNeeded() {
final PlayerHolder holder = PlayerHolder.Companion.getInstance();
final PlayerHolder holder = PlayerHolder.INSTANCE;
if (holder.isPlayQueueReady()) {
addEntry(StreamDialogDefaultEntry.ENQUEUE);

View File

@ -22,12 +22,19 @@ import org.schabi.newpipe.player.playqueue.PlayQueue
import org.schabi.newpipe.util.NavigationHelper
import java.util.function.Consumer
class PlayerHolder private constructor() {
private val DEBUG = MainActivity.DEBUG
private val TAG: String = PlayerHolder::class.java.getSimpleName()
/**
* Singleton that manages a `PlayerService`
* and can be used to control the player instance through the service.
*/
object PlayerHolder {
private var listener: PlayerServiceExtendedEventListener? = null
private val serviceConnection = PlayerServiceConnection()
var isBound: Boolean = false
private set
private var playerService: PlayerService? = null
private val player: Player?
@ -110,7 +117,7 @@ class PlayerHolder private constructor() {
val intent = Intent(context, PlayerService::class.java)
intent.putExtra(PlayerService.SHOULD_START_FOREGROUND_EXTRA, true)
ContextCompat.startForegroundService(context, intent)
serviceConnection.doPlayAfterConnect(playAfterConnect)
PlayerServiceConnection.doPlayAfterConnect(playAfterConnect)
bind(context)
}
@ -126,7 +133,7 @@ class PlayerHolder private constructor() {
context.stopService(Intent(context, PlayerService::class.java))
}
internal inner class PlayerServiceConnection : ServiceConnection {
internal object PlayerServiceConnection : ServiceConnection {
internal var playAfterConnect = false
/**
@ -185,7 +192,7 @@ class PlayerHolder private constructor() {
// BIND_AUTO_CREATE starts the service if it's not already running
this.isBound = bind(context, Context.BIND_AUTO_CREATE)
if (!this.isBound) {
context.unbindService(serviceConnection)
context.unbindService(PlayerServiceConnection)
}
}
@ -201,7 +208,7 @@ class PlayerHolder private constructor() {
private fun bind(context: Context, flags: Int): Boolean {
val serviceIntent = Intent(context, PlayerService::class.java)
serviceIntent.setAction(PlayerService.BIND_PLAYER_HOLDER_ACTION)
return context.bindService(serviceIntent, serviceConnection, flags)
return context.bindService(serviceIntent, PlayerServiceConnection, flags)
}
private fun unbind(context: Context) {
@ -210,7 +217,7 @@ class PlayerHolder private constructor() {
}
if (this.isBound) {
context.unbindService(serviceConnection)
context.unbindService(PlayerServiceConnection)
this.isBound = false
stopPlayerListener()
playerService = null
@ -223,18 +230,18 @@ class PlayerHolder private constructor() {
// setting the player listener will take care of calling relevant callbacks if the
// player in the service is (not) already active, also see playerStateListener below
playerService?.setPlayerListener(playerStateListener)
this.player?.setFragmentListener(internalListener)
this.player?.setFragmentListener(HolderPlayerServiceEventListener)
}
private fun stopPlayerListener() {
playerService?.setPlayerListener(null)
this.player?.removeFragmentListener(internalListener)
this.player?.removeFragmentListener(HolderPlayerServiceEventListener)
}
/**
* This listener will be held by the players created by [PlayerService].
*/
private val internalListener: PlayerServiceEventListener = object : PlayerServiceEventListener {
private object HolderPlayerServiceEventListener : PlayerServiceEventListener {
override fun onViewCreated() {
listener?.onViewCreated()
}
@ -307,26 +314,11 @@ class PlayerHolder private constructor() {
// before setting its player to null
l.onPlayerDisconnected()
} else {
l.onPlayerConnected(player, serviceConnection.playAfterConnect)
l.onPlayerConnected(player, PlayerServiceConnection.playAfterConnect)
// reset the value of playAfterConnect: if it was true before, it is now "consumed"
serviceConnection.playAfterConnect = false;
player.setFragmentListener(internalListener)
PlayerServiceConnection.playAfterConnect = false
player.setFragmentListener(HolderPlayerServiceEventListener)
}
}
}
companion object {
private var instance: PlayerHolder? = null
@Synchronized
fun getInstance(): PlayerHolder {
if (instance == null) {
instance = PlayerHolder()
}
return instance!!
}
private val DEBUG = MainActivity.DEBUG
private val TAG: String = PlayerHolder::class.java.getSimpleName()
}
}

View File

@ -28,10 +28,9 @@ fun StreamMenu(
) {
val context = LocalContext.current
val streamViewModel = viewModel<StreamViewModel>()
val playerHolder = PlayerHolder.Companion.getInstance()
DropdownMenu(expanded = expanded, onDismissRequest = onDismissRequest) {
if (playerHolder.isPlayQueueReady) {
if (PlayerHolder.isPlayQueueReady) {
DropdownMenuItem(
text = { Text(text = stringResource(R.string.enqueue_stream)) },
onClick = {
@ -42,7 +41,7 @@ fun StreamMenu(
}
)
if (playerHolder.queuePosition < playerHolder.queueSize - 1) {
if (PlayerHolder.queuePosition < PlayerHolder.queueSize - 1) {
DropdownMenuItem(
text = { Text(text = stringResource(R.string.enqueue_next_stream)) },
onClick = {

View File

@ -200,7 +200,7 @@ public final class NavigationHelper {
}
public static void enqueueOnPlayer(final Context context, final PlayQueue queue) {
PlayerType playerType = PlayerHolder.Companion.getInstance().getType();
PlayerType playerType = PlayerHolder.INSTANCE.getType();
if (playerType == null) {
Log.e(TAG, "Enqueueing but no player is open; defaulting to background player");
playerType = PlayerType.AUDIO;
@ -211,7 +211,7 @@ public final class NavigationHelper {
/* ENQUEUE NEXT */
public static void enqueueNextOnPlayer(final Context context, final PlayQueue queue) {
PlayerType playerType = PlayerHolder.Companion.getInstance().getType();
PlayerType playerType = PlayerHolder.INSTANCE.getType();
if (playerType == null) {
Log.e(TAG, "Enqueueing next but no player is open; defaulting to background player");
playerType = PlayerType.AUDIO;
@ -421,13 +421,13 @@ public final class NavigationHelper {
final boolean switchingPlayers) {
final boolean autoPlay;
@Nullable final PlayerType playerType = PlayerHolder.Companion.getInstance().getType();
@Nullable final PlayerType playerType = PlayerHolder.INSTANCE.getType();
if (playerType == null) {
// no player open
autoPlay = PlayerHelper.isAutoplayAllowedByUser(context);
} else if (switchingPlayers) {
// switching player to main player
autoPlay = PlayerHolder.Companion.getInstance().isPlaying(); // keep play/pause state
autoPlay = PlayerHolder.INSTANCE.isPlaying(); // keep play/pause state
} else if (playerType == PlayerType.MAIN) {
// opening new stream while already playing in main player
autoPlay = PlayerHelper.isAutoplayAllowedByUser(context);