2
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-08-22 01:58:16 +00:00

Improve Kotlin converted from java in various places

This commit is contained in:
Stypox 2025-06-09 15:22:17 +02:00
parent 7330541499
commit 3f62ec7e53
No known key found for this signature in database
GPG Key ID: 4BDF1B40A49FDD23
5 changed files with 750 additions and 1206 deletions

View File

@ -37,7 +37,6 @@ import org.schabi.newpipe.player.notification.NotificationPlayerUi
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.ThemeHelper
import java.lang.ref.WeakReference
import java.util.function.BiConsumer
import java.util.function.Consumer
/**
@ -47,13 +46,13 @@ class PlayerService : MediaBrowserServiceCompat() {
// These objects are used to cleanly separate the Service implementation (in this file) and the
// media browser and playback preparer implementations. At the moment the playback preparer is
// only used in conjunction with the media browser.
private var mediaBrowserImpl: MediaBrowserImpl? = null
private var mediaBrowserPlaybackPreparer: MediaBrowserPlaybackPreparer? = null
private lateinit var mediaBrowserImpl: MediaBrowserImpl
private lateinit var mediaBrowserPlaybackPreparer: MediaBrowserPlaybackPreparer
// these are instantiated in onCreate() as per
// https://developer.android.com/training/cars/media#browser_workflow
private var mediaSession: MediaSessionCompat? = null
private var sessionConnector: MediaSessionConnector? = null
private lateinit var mediaSession: MediaSessionCompat
private lateinit var sessionConnector: MediaSessionConnector
/**
* @return the current active player instance. May be null, since the player service can outlive
@ -68,7 +67,7 @@ class PlayerService : MediaBrowserServiceCompat() {
* The parameter taken by this [Consumer] can be null to indicate the player is being
* stopped.
*/
private var onPlayerStartedOrStopped: Consumer<Player?>? = null
private var onPlayerStartedOrStopped: ((player: Player?) -> Unit)? = null
//region Service lifecycle
override fun onCreate() {
@ -80,14 +79,7 @@ class PlayerService : MediaBrowserServiceCompat() {
Localization.assureCorrectAppLanguage(this)
ThemeHelper.setTheme(this)
mediaBrowserImpl = MediaBrowserImpl(
this,
Consumer { parentId: String ->
this.notifyChildrenChanged(
parentId
)
}
)
mediaBrowserImpl = MediaBrowserImpl(this, this::notifyChildrenChanged)
// see https://developer.android.com/training/cars/media#browser_workflow
val session = MediaSessionCompat(this, "MediaSessionPlayerServ")
@ -98,17 +90,10 @@ class PlayerService : MediaBrowserServiceCompat() {
connector.setMetadataDeduplicationEnabled(true)
mediaBrowserPlaybackPreparer = MediaBrowserPlaybackPreparer(
this,
BiConsumer { message: String, code: Int ->
connector.setCustomErrorMessage(
message,
code
)
},
Runnable { connector.setCustomErrorMessage(null) },
Consumer { playWhenReady: Boolean? ->
player?.onPrepare()
}
context = this,
setMediaSessionError = connector::setCustomErrorMessage,
clearMediaSessionError = { connector.setCustomErrorMessage(null) },
onPrepare = { player?.onPrepare() }
)
connector.setPlaybackPreparer(mediaBrowserPlaybackPreparer)
@ -125,11 +110,8 @@ class PlayerService : MediaBrowserServiceCompat() {
if (DEBUG) {
Log.d(
TAG,
(
"onStartCommand() called with: intent = [" + intent +
"], extras = [" + intent.extras.toDebugString() +
"], flags = [" + flags + "], startId = [" + startId + "]"
)
"onStartCommand() called with: intent = [$intent], extras = [${
intent.extras.toDebugString()}], flags = [$flags], startId = [$startId]"
)
}
@ -140,7 +122,7 @@ class PlayerService : MediaBrowserServiceCompat() {
val playerWasNull = (player == null)
if (playerWasNull) {
// make sure the player exists, in case the service was resumed
player = Player(this, mediaSession!!, sessionConnector!!)
player = Player(this, mediaSession, sessionConnector)
}
// Be sure that the player notification is set and the service is started in foreground,
@ -150,35 +132,29 @@ class PlayerService : MediaBrowserServiceCompat() {
// no one already and starting the service in foreground should not create any issues.
// If the service is already started in foreground, requesting it to be started
// shouldn't do anything.
player!!.UIs().get(NotificationPlayerUi::class.java)
?.createNotificationAndStartForeground()
player?.UIs()?.get(NotificationPlayerUi::class)?.createNotificationAndStartForeground()
val startedOrStopped = onPlayerStartedOrStopped
if (playerWasNull && startedOrStopped != null) {
if (playerWasNull) {
// notify that a new player was created (but do it after creating the foreground
// notification just to make sure we don't incur, due to slowness, in
// "Context.startForegroundService() did not then call Service.startForeground()")
startedOrStopped.accept(player)
onPlayerStartedOrStopped?.invoke(player)
}
}
val p = player
if (Intent.ACTION_MEDIA_BUTTON == intent.action &&
(p == null || p.playQueue == null)
) {
/*
No need to process media button's actions if the player is not working, otherwise
the player service would strangely start with nothing to play
Stop the service in this case, which will be removed from the foreground and its
notification cancelled in its destruction
*/
if (Intent.ACTION_MEDIA_BUTTON == intent.action && p?.playQueue == null) {
// No need to process media button's actions if the player is not working, otherwise
// the player service would strangely start with nothing to play
// Stop the service in this case, which will be removed from the foreground and its
// notification cancelled in its destruction
destroyPlayerAndStopService()
return START_NOT_STICKY
}
if (p != null) {
p.handleIntent(intent)
p.UIs().get(MediaSessionPlayerUi::class.java)
p.UIs().get(MediaSessionPlayerUi::class)
?.handleMediaButtonIntent(intent)
}
@ -218,22 +194,22 @@ class PlayerService : MediaBrowserServiceCompat() {
cleanup()
mediaBrowserPlaybackPreparer?.dispose()
mediaSession?.release()
mediaBrowserImpl?.dispose()
mediaBrowserPlaybackPreparer.dispose()
mediaSession.release()
mediaBrowserImpl.dispose()
}
private fun cleanup() {
val p = player
if (p != null) {
// notify that the player is being destroyed
onPlayerStartedOrStopped?.accept(null)
onPlayerStartedOrStopped?.invoke(null)
p.saveAndShutdown()
player = null
}
// Should already be handled by MediaSessionPlayerUi, but just to be sure.
mediaSession?.setActive(false)
mediaSession.setActive(false)
// Should already be handled by NotificationUtil.cancelNotificationAndStopForeground() in
// NotificationPlayerUi, but let's make sure that the foreground service is stopped.
@ -273,24 +249,22 @@ class PlayerService : MediaBrowserServiceCompat() {
if (DEBUG) {
Log.d(
TAG,
(
"onBind() called with: intent = [" + intent +
"], extras = [" + intent.extras.toDebugString() + "]"
)
"onBind() called with: intent = [$intent], extras = [${
intent.extras.toDebugString()}]"
)
}
if (BIND_PLAYER_HOLDER_ACTION == intent.action) {
return if (BIND_PLAYER_HOLDER_ACTION == intent.action) {
// Note that this binder might be reused multiple times while the service is alive, even
// after unbind() has been called: https://stackoverflow.com/a/8794930 .
return mBinder
mBinder
} else if (SERVICE_INTERFACE == intent.action) {
// MediaBrowserService also uses its own binder, so for actions related to the media
// browser service, pass the onBind to the superclass.
return super.onBind(intent)
super.onBind(intent)
} else {
// This is an unknown request, avoid returning any binder to not leak objects.
return null
null
}
}
@ -307,9 +281,9 @@ class PlayerService : MediaBrowserServiceCompat() {
* by the [Consumer] can be null to indicate that the player is stopping.
* @param listener the listener to set or unset
*/
fun setPlayerListener(listener: Consumer<Player?>?) {
fun setPlayerListener(listener: ((player: Player?) -> Unit)?) {
this.onPlayerStartedOrStopped = listener
listener?.accept(player)
listener?.invoke(player)
}
//endregion
@ -320,14 +294,14 @@ class PlayerService : MediaBrowserServiceCompat() {
rootHints: Bundle?
): BrowserRoot? {
// TODO check if the accessing package has permission to view data
return mediaBrowserImpl?.onGetRoot(clientPackageName, clientUid, rootHints)
return mediaBrowserImpl.onGetRoot(clientPackageName, clientUid, rootHints)
}
override fun onLoadChildren(
parentId: String,
result: Result<List<MediaBrowserCompat.MediaItem>>
) {
mediaBrowserImpl?.onLoadChildren(parentId, result)
mediaBrowserImpl.onLoadChildren(parentId, result)
}
override fun onSearch(
@ -335,7 +309,7 @@ class PlayerService : MediaBrowserServiceCompat() {
extras: Bundle?,
result: Result<List<MediaBrowserCompat.MediaItem>>
) {
mediaBrowserImpl?.onSearch(query, result)
mediaBrowserImpl.onSearch(query, result)
} //endregion
companion object {

View File

@ -20,7 +20,6 @@ import org.schabi.newpipe.player.event.PlayerServiceEventListener
import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener
import org.schabi.newpipe.player.playqueue.PlayQueue
import org.schabi.newpipe.util.NavigationHelper
import java.util.function.Consumer
private val DEBUG = MainActivity.DEBUG
private val TAG: String = PlayerHolder::class.java.getSimpleName()
@ -40,9 +39,9 @@ object PlayerHolder {
private val player: Player?
get() = playerService?.player
// player play queue might be null e.g. while player is starting
private val playQueue: PlayQueue?
get() = // player play queue might be null e.g. while player is starting
this.player?.playQueue
get() = this.player?.playQueue
val type: PlayerType?
/**
@ -78,8 +77,8 @@ object PlayerHolder {
// Force reload data from service
newListener?.let { listener ->
playerService?.let {
listener.onServiceConnected(it)
playerService?.let { service ->
listener.onServiceConnected(service)
startPlayerListener()
// ^ will call listener.onPlayerConnected() down the line if there is an active player
}
@ -103,7 +102,7 @@ object PlayerHolder {
newListener: PlayerServiceExtendedEventListener?
) {
if (DEBUG) {
Log.d(TAG, "startService() called with playAfterConnect=" + playAfterConnect)
Log.d(TAG, "startService() called with playAfterConnect=$playAfterConnect")
}
val context = this.commonContext
setListener(newListener)
@ -162,21 +161,15 @@ object PlayerHolder {
val s = localBinder.service
requireNotNull(s) {
(
"PlayerService.LocalBinder.getService() must never be" +
"null after the service connects"
)
"PlayerService.LocalBinder.getService() must never be" +
"null after the service connects"
}
playerService = s
val l = listener
if (l != null) {
listener?.let { l ->
l.onServiceConnected(s)
player?.let {
l.onPlayerConnected(it, playAfterConnect)
}
player?.let { l.onPlayerConnected(it, playAfterConnect) }
}
startPlayerListener()
// ^ will call listener.onPlayerConnected() down the line if there is an active player
// notify the main activity that binding the service has completed, so that it can
@ -305,9 +298,8 @@ object PlayerHolder {
* or stopping. This is necessary since the service outlives the player e.g. to answer Android
* Auto media browser queries.
*/
private val playerStateListener = Consumer { player: Player? ->
val l = listener
if (l != null) {
private val playerStateListener: (Player?) -> Unit = { player: Player? ->
listener?.let { l ->
if (player == null) {
// player.fragmentListener=null is already done by player.stopActivityBinding(),
// which is called by player.destroy(), which is in turn called by PlayerService

View File

@ -36,7 +36,6 @@ import org.schabi.newpipe.local.playlist.RemotePlaylistManager
import org.schabi.newpipe.util.ExtractorHelper
import org.schabi.newpipe.util.ServiceHelper
import org.schabi.newpipe.util.image.ImageStrategy
import java.util.function.Consumer
/**
* This class is used to cleanly separate the Service implementation (in
@ -46,16 +45,14 @@ import java.util.function.Consumer
*/
class MediaBrowserImpl(
private val context: Context,
notifyChildrenChanged: Consumer<String>, // parentId
notifyChildrenChanged: (parentId: String) -> Unit,
) {
private val database = NewPipeDatabase.getInstance(context)
private var disposables = CompositeDisposable()
init {
// this will listen to changes in the bookmarks until this MediaBrowserImpl is dispose()d
disposables.add(
getMergedPlaylists().subscribe { notifyChildrenChanged.accept(ID_BOOKMARKS) }
)
disposables.add(getMergedPlaylists().subscribe { notifyChildrenChanged(ID_BOOKMARKS) })
}
//region Cleanup
@ -204,6 +201,7 @@ class MediaBrowserImpl(
val builder = MediaDescriptionCompat.Builder()
builder.setMediaId(createMediaIdForInfoItem(item))
.setTitle(item.name)
.setIconUri(ImageStrategy.choosePreferredImage(item.thumbnails)?.toUri())
when (item.infoType) {
InfoType.STREAM -> builder.setSubtitle((item as StreamInfoItem).uploaderName)
@ -212,10 +210,6 @@ class MediaBrowserImpl(
else -> return null
}
ImageStrategy.choosePreferredImage(item.thumbnails)?.let {
builder.setIconUri(imageUriOrNullIfDisabled(it))
}
return MediaBrowserCompat.MediaItem(
builder.build(),
MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
@ -276,10 +270,7 @@ class MediaBrowserImpl(
builder.setMediaId(createMediaIdForPlaylistIndex(true, playlistId, index))
.setTitle(item.name)
.setSubtitle(item.uploaderName)
ImageStrategy.choosePreferredImage(item.thumbnails)?.let {
builder.setIconUri(imageUriOrNullIfDisabled(it))
}
.setIconUri(ImageStrategy.choosePreferredImage(item.thumbnails)?.toUri())
return MediaBrowserCompat.MediaItem(
builder.build(),

View File

@ -1,6 +1,8 @@
package org.schabi.newpipe.player.ui
import org.schabi.newpipe.util.GuardedByMutex
import kotlin.reflect.KClass
import kotlin.reflect.safeCast
/**
* Creates a [PlayerUiList] starting with the provided player uis. The provided player uis
@ -84,20 +86,20 @@ class PlayerUiList(vararg initialPlayerUis: PlayerUi) {
* @param T the class type parameter
* @return the first player UI of the required type found in the list, or null
</T> */
fun <T : PlayerUi> get(playerUiType: Class<T>): T? =
fun <T : PlayerUi> get(playerUiType: KClass<T>): T? =
playerUis.runWithLockSync {
for (ui in lockData) {
if (playerUiType.isInstance(ui)) {
when (val r = playerUiType.cast(ui)) {
// try all UIs before returning null
null -> continue
else -> return@runWithLockSync r
}
// try all UIs before returning null
playerUiType.safeCast(ui)?.let { return@runWithLockSync it }
}
}
return@runWithLockSync null
}
fun <T : PlayerUi> get(playerUiType: Class<T>): T? =
get(playerUiType.kotlin)
/**
* Calls the provided consumer on all player UIs in the list, in order of addition.
* @param consumer the consumer to call with player UIs