mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-30 23:03:00 +00:00 
			
		
		
		
	PlayerService: Convert to kotlin (mechanical)
This commit is contained in:
		| @@ -16,103 +16,101 @@ | |||||||
|  * You should have received a copy of the GNU General Public License |  * You should have received a copy of the GNU General Public License | ||||||
|  * along with this program. If not, see <http://www.gnu.org/licenses/>. |  * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
|  | package org.schabi.newpipe.player | ||||||
| 
 | 
 | ||||||
| package org.schabi.newpipe.player; | import android.content.Context | ||||||
| 
 | import android.content.Intent | ||||||
| import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; | import android.os.Binder | ||||||
| 
 | import android.os.Bundle | ||||||
| import android.content.Context; | import android.os.IBinder | ||||||
| import android.content.Intent; | import android.support.v4.media.MediaBrowserCompat | ||||||
| import android.os.Binder; | import android.support.v4.media.session.MediaSessionCompat | ||||||
| import android.os.Bundle; | import android.util.Log | ||||||
| import android.os.IBinder; | import androidx.core.app.ServiceCompat | ||||||
| import android.support.v4.media.MediaBrowserCompat; | import androidx.media.MediaBrowserServiceCompat | ||||||
| import android.support.v4.media.session.MediaSessionCompat; | import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector | ||||||
| import android.util.Log; | import org.schabi.newpipe.ktx.toDebugString | ||||||
| 
 | import org.schabi.newpipe.player.mediabrowser.MediaBrowserImpl | ||||||
| import androidx.annotation.NonNull; | import org.schabi.newpipe.player.mediabrowser.MediaBrowserPlaybackPreparer | ||||||
| import androidx.annotation.Nullable; | import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi | ||||||
| import androidx.core.app.ServiceCompat; | import org.schabi.newpipe.player.notification.NotificationPlayerUi | ||||||
| import androidx.media.MediaBrowserServiceCompat; | import org.schabi.newpipe.util.Localization | ||||||
| 
 | import org.schabi.newpipe.util.ThemeHelper | ||||||
| import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; | import java.lang.ref.WeakReference | ||||||
| 
 | import java.util.function.BiConsumer | ||||||
| import org.schabi.newpipe.ktx.BundleKt; | import java.util.function.Consumer | ||||||
| import org.schabi.newpipe.player.mediabrowser.MediaBrowserImpl; |  | ||||||
| import org.schabi.newpipe.player.mediabrowser.MediaBrowserPlaybackPreparer; |  | ||||||
| import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi; |  | ||||||
| import org.schabi.newpipe.player.notification.NotificationPlayerUi; |  | ||||||
| import org.schabi.newpipe.util.ThemeHelper; |  | ||||||
| 
 |  | ||||||
| import java.lang.ref.WeakReference; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.function.Consumer; |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * One service for all players. |  * One service for all players. | ||||||
|  */ |  */ | ||||||
| public final class PlayerService extends MediaBrowserServiceCompat { | class PlayerService : MediaBrowserServiceCompat() { | ||||||
|     private static final String TAG = PlayerService.class.getSimpleName(); |  | ||||||
|     private static final boolean DEBUG = Player.DEBUG; |  | ||||||
| 
 |  | ||||||
|     public static final String SHOULD_START_FOREGROUND_EXTRA = "should_start_foreground_extra"; |  | ||||||
|     public static final String BIND_PLAYER_HOLDER_ACTION = "bind_player_holder_action"; |  | ||||||
| 
 |  | ||||||
|     // These objects are used to cleanly separate the Service implementation (in this file) and the |     // 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 |     // media browser and playback preparer implementations. At the moment the playback preparer is | ||||||
|     // only used in conjunction with the media browser. |     // only used in conjunction with the media browser. | ||||||
|     private MediaBrowserImpl mediaBrowserImpl; |     private var mediaBrowserImpl: MediaBrowserImpl? = null | ||||||
|     private MediaBrowserPlaybackPreparer mediaBrowserPlaybackPreparer; |     private var mediaBrowserPlaybackPreparer: MediaBrowserPlaybackPreparer? = null | ||||||
| 
 | 
 | ||||||
|     // these are instantiated in onCreate() as per |     // these are instantiated in onCreate() as per | ||||||
|     // https://developer.android.com/training/cars/media#browser_workflow |     // https://developer.android.com/training/cars/media#browser_workflow | ||||||
|     private MediaSessionCompat mediaSession; |     private var mediaSession: MediaSessionCompat? = null | ||||||
|     private MediaSessionConnector sessionConnector; |     private var sessionConnector: MediaSessionConnector? = null | ||||||
| 
 |  | ||||||
|     @Nullable |  | ||||||
|     private Player player; |  | ||||||
| 
 |  | ||||||
|     private final IBinder mBinder = new PlayerService.LocalBinder(this); |  | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * The parameter taken by this {@link Consumer} can be null to indicate the player is being |      * @return the current active player instance. May be null, since the player service can outlive | ||||||
|  |      * the player e.g. to respond to Android Auto media browser queries. | ||||||
|  |      */ | ||||||
|  |     var player: Player? = null | ||||||
|  |         private set | ||||||
|  | 
 | ||||||
|  |     private val mBinder: IBinder = LocalBinder(this) | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * The parameter taken by this [Consumer] can be null to indicate the player is being | ||||||
|      * stopped. |      * stopped. | ||||||
|      */ |      */ | ||||||
|     @Nullable |     private var onPlayerStartedOrStopped: Consumer<Player?>? = null | ||||||
|     private Consumer<Player> onPlayerStartedOrStopped = null; |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|     //region Service lifecycle |     //region Service lifecycle | ||||||
|     @Override |     override fun onCreate() { | ||||||
|     public void onCreate() { |         super.onCreate() | ||||||
|         super.onCreate(); |  | ||||||
| 
 | 
 | ||||||
|         if (DEBUG) { |         if (DEBUG) { | ||||||
|             Log.d(TAG, "onCreate() called"); |             Log.d(TAG, "onCreate() called") | ||||||
|         } |         } | ||||||
|         assureCorrectAppLanguage(this); |         Localization.assureCorrectAppLanguage(this) | ||||||
|         ThemeHelper.setTheme(this); |         ThemeHelper.setTheme(this) | ||||||
| 
 | 
 | ||||||
|         mediaBrowserImpl = new MediaBrowserImpl(this, this::notifyChildrenChanged); |         mediaBrowserImpl = MediaBrowserImpl( | ||||||
|  |             this, | ||||||
|  |             Consumer { parentId: String? -> | ||||||
|  |                 this.notifyChildrenChanged( | ||||||
|  |                     parentId!! | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
|         // see https://developer.android.com/training/cars/media#browser_workflow |         // see https://developer.android.com/training/cars/media#browser_workflow | ||||||
|         mediaSession = new MediaSessionCompat(this, "MediaSessionPlayerServ"); |         mediaSession = MediaSessionCompat(this, "MediaSessionPlayerServ") | ||||||
|         setSessionToken(mediaSession.getSessionToken()); |         setSessionToken(mediaSession!!.getSessionToken()) | ||||||
|         sessionConnector = new MediaSessionConnector(mediaSession); |         sessionConnector = MediaSessionConnector(mediaSession!!) | ||||||
|         sessionConnector.setMetadataDeduplicationEnabled(true); |         sessionConnector!!.setMetadataDeduplicationEnabled(true) | ||||||
| 
 | 
 | ||||||
|         mediaBrowserPlaybackPreparer = new MediaBrowserPlaybackPreparer( |         mediaBrowserPlaybackPreparer = MediaBrowserPlaybackPreparer( | ||||||
|             this, |             this, | ||||||
|                 sessionConnector::setCustomErrorMessage, |             BiConsumer { message: String?, code: Int? -> | ||||||
|                 () -> sessionConnector.setCustomErrorMessage(null), |                 sessionConnector!!.setCustomErrorMessage( | ||||||
|                 (playWhenReady) -> { |                     message, | ||||||
|  |                     code!! | ||||||
|  |                 ) | ||||||
|  |             }, | ||||||
|  |             Runnable { sessionConnector!!.setCustomErrorMessage(null) }, | ||||||
|  |             Consumer { playWhenReady: Boolean? -> | ||||||
|                 if (player != null) { |                 if (player != null) { | ||||||
|                         player.onPrepare(); |                     player!!.onPrepare() | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         ); |         ) | ||||||
|         sessionConnector.setPlaybackPreparer(mediaBrowserPlaybackPreparer); |         sessionConnector!!.setPlaybackPreparer(mediaBrowserPlaybackPreparer) | ||||||
| 
 | 
 | ||||||
|         // Note: you might be tempted to create the player instance and call startForeground here, |         // Note: you might be tempted to create the player instance and call startForeground here, | ||||||
|         // but be aware that the Android system might start the service just to perform media |         // but be aware that the Android system might start the service just to perform media | ||||||
| @@ -123,22 +121,26 @@ public final class PlayerService extends MediaBrowserServiceCompat { | |||||||
|         // useless notification. |         // useless notification. | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { | ||||||
|     public int onStartCommand(final Intent intent, final int flags, final int startId) { |  | ||||||
|         if (DEBUG) { |         if (DEBUG) { | ||||||
|             Log.d(TAG, "onStartCommand() called with: intent = [" + intent |             Log.d( | ||||||
|                     + "], extras = [" + BundleKt.toDebugString(intent.getExtras()) |                 TAG, | ||||||
|                     + "], flags = [" + flags + "], startId = [" + startId + "]"); |                 ( | ||||||
|  |                     "onStartCommand() called with: intent = [" + intent + | ||||||
|  |                         "], extras = [" + intent.getExtras().toDebugString() + | ||||||
|  |                         "], flags = [" + flags + "], startId = [" + startId + "]" | ||||||
|  |                     ) | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // All internal NewPipe intents used to interact with the player, that are sent to the |         // All internal NewPipe intents used to interact with the player, that are sent to the | ||||||
|         // PlayerService using startForegroundService(), will have SHOULD_START_FOREGROUND_EXTRA, |         // PlayerService using startForegroundService(), will have SHOULD_START_FOREGROUND_EXTRA, | ||||||
|         // to ensure startForeground() is called (otherwise Android will force-crash the app). |         // to ensure startForeground() is called (otherwise Android will force-crash the app). | ||||||
|         if (intent.getBooleanExtra(SHOULD_START_FOREGROUND_EXTRA, false)) { |         if (intent.getBooleanExtra(SHOULD_START_FOREGROUND_EXTRA, false)) { | ||||||
|             final boolean playerWasNull = (player == null); |             val playerWasNull = (player == null) | ||||||
|             if (playerWasNull) { |             if (playerWasNull) { | ||||||
|                 // make sure the player exists, in case the service was resumed |                 // make sure the player exists, in case the service was resumed | ||||||
|                 player = new Player(this, mediaSession, sessionConnector); |                 player = Player(this, mediaSession!!, sessionConnector!!) | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Be sure that the player notification is set and the service is started in foreground, |             // Be sure that the player notification is set and the service is started in foreground, | ||||||
| @@ -148,107 +150,112 @@ public final class PlayerService extends MediaBrowserServiceCompat { | |||||||
|             // no one already and starting the service in foreground should not create any issues. |             // 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 |             // If the service is already started in foreground, requesting it to be started | ||||||
|             // shouldn't do anything. |             // shouldn't do anything. | ||||||
|             player.UIs().getOpt(NotificationPlayerUi.class) |             player!!.UIs().getOpt<NotificationPlayerUi>(NotificationPlayerUi::class.java) | ||||||
|                     .ifPresent(NotificationPlayerUi::createNotificationAndStartForeground); |                 .ifPresent(Consumer { obj: NotificationPlayerUi? -> obj!!.createNotificationAndStartForeground() }) | ||||||
| 
 | 
 | ||||||
|             if (playerWasNull && onPlayerStartedOrStopped != null) { |             if (playerWasNull && onPlayerStartedOrStopped != null) { | ||||||
|                 // notify that a new player was created (but do it after creating the foreground |                 // 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 |                 // notification just to make sure we don't incur, due to slowness, in | ||||||
|                 // "Context.startForegroundService() did not then call Service.startForeground()") |                 // "Context.startForegroundService() did not then call Service.startForeground()") | ||||||
|                 onPlayerStartedOrStopped.accept(player); |                 onPlayerStartedOrStopped!!.accept(player) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction()) |         if (Intent.ACTION_MEDIA_BUTTON == intent.getAction() && | ||||||
|                 && (player == null || player.getPlayQueue() == null)) { |             (player == null || player!!.getPlayQueue() == null) | ||||||
|  |         ) { | ||||||
|             /* |             /* | ||||||
|             No need to process media button's actions if the player is not working, otherwise |             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 |             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 |             Stop the service in this case, which will be removed from the foreground and its | ||||||
|             notification cancelled in its destruction |             notification cancelled in its destruction | ||||||
|              */ |              */ | ||||||
|             destroyPlayerAndStopService(); |             destroyPlayerAndStopService() | ||||||
|             return START_NOT_STICKY; |             return START_NOT_STICKY | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (player != null) { |         if (player != null) { | ||||||
|             player.handleIntent(intent); |             player!!.handleIntent(intent) | ||||||
|             player.UIs().getOpt(MediaSessionPlayerUi.class) |             player!!.UIs().getOpt<MediaSessionPlayerUi>(MediaSessionPlayerUi::class.java) | ||||||
|                     .ifPresent(ui -> ui.handleMediaButtonIntent(intent)); |                 .ifPresent( | ||||||
|  |                     Consumer { ui: MediaSessionPlayerUi? -> | ||||||
|  |                         ui!!.handleMediaButtonIntent( | ||||||
|  |                             intent | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                 ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return START_NOT_STICKY; |         return START_NOT_STICKY | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public void stopForImmediateReusing() { |     fun stopForImmediateReusing() { | ||||||
|         if (DEBUG) { |         if (DEBUG) { | ||||||
|             Log.d(TAG, "stopForImmediateReusing() called"); |             Log.d(TAG, "stopForImmediateReusing() called") | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (player != null && !player.exoPlayerIsNull()) { |         if (player != null && !player!!.exoPlayerIsNull()) { | ||||||
|             // Releases wifi & cpu, disables keepScreenOn, etc. |             // Releases wifi & cpu, disables keepScreenOn, etc. | ||||||
|             // We can't just pause the player here because it will make transition |             // We can't just pause the player here because it will make transition | ||||||
|             // from one stream to a new stream not smooth |             // from one stream to a new stream not smooth | ||||||
|             player.smoothStopForImmediateReusing(); |             player!!.smoothStopForImmediateReusing() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     override fun onTaskRemoved(rootIntent: Intent?) { | ||||||
|     public void onTaskRemoved(final Intent rootIntent) { |         super.onTaskRemoved(rootIntent) | ||||||
|         super.onTaskRemoved(rootIntent); |         if (player != null && !player!!.videoPlayerSelected()) { | ||||||
|         if (player != null && !player.videoPlayerSelected()) { |             return | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
|         onDestroy(); |         onDestroy() | ||||||
|         // Unload from memory completely |         // Unload from memory completely | ||||||
|         Runtime.getRuntime().halt(0); |         Runtime.getRuntime().halt(0) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     override fun onDestroy() { | ||||||
|     public void onDestroy() { |  | ||||||
|         if (DEBUG) { |         if (DEBUG) { | ||||||
|             Log.d(TAG, "destroy() called"); |             Log.d(TAG, "destroy() called") | ||||||
|         } |         } | ||||||
|         super.onDestroy(); |         super.onDestroy() | ||||||
| 
 | 
 | ||||||
|         cleanup(); |         cleanup() | ||||||
| 
 | 
 | ||||||
|         mediaBrowserPlaybackPreparer.dispose(); |         mediaBrowserPlaybackPreparer!!.dispose() | ||||||
|         mediaSession.release(); |         mediaSession!!.release() | ||||||
|         mediaBrowserImpl.dispose(); |         mediaBrowserImpl!!.dispose() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void cleanup() { |     private fun cleanup() { | ||||||
|         if (player != null) { |         if (player != null) { | ||||||
|             if (onPlayerStartedOrStopped != null) { |             if (onPlayerStartedOrStopped != null) { | ||||||
|                 // notify that the player is being destroyed |                 // notify that the player is being destroyed | ||||||
|                 onPlayerStartedOrStopped.accept(null); |                 onPlayerStartedOrStopped!!.accept(null) | ||||||
|             } |             } | ||||||
|             player.destroy(); |             player!!.destroy() | ||||||
|             player = null; |             player = null | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Should already be handled by MediaSessionPlayerUi, but just to be sure. |         // 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 |         // Should already be handled by NotificationUtil.cancelNotificationAndStopForeground() in | ||||||
|         // NotificationPlayerUi, but let's make sure that the foreground service is stopped. |         // NotificationPlayerUi, but let's make sure that the foreground service is stopped. | ||||||
|         ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE); |         ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Destroys the player and allows the player instance to be garbage collected. Sets the media |      * Destroys the player and allows the player instance to be garbage collected. Sets the media | ||||||
|      * session to inactive. Stops the foreground service and removes the player notification |      * session to inactive. Stops the foreground service and removes the player notification | ||||||
|      * associated with it. Tries to stop the {@link PlayerService} completely, but this step will |      * associated with it. Tries to stop the [PlayerService] completely, but this step will | ||||||
|      * have no effect in case some service connection still uses the service (e.g. the Android Auto |      * have no effect in case some service connection still uses the service (e.g. the Android Auto | ||||||
|      * system accesses the media browser even when no player is running). |      * system accesses the media browser even when no player is running). | ||||||
|      */ |      */ | ||||||
|     public void destroyPlayerAndStopService() { |     fun destroyPlayerAndStopService() { | ||||||
|         if (DEBUG) { |         if (DEBUG) { | ||||||
|             Log.d(TAG, "destroyPlayerAndStopService() called"); |             Log.d(TAG, "destroyPlayerAndStopService() called") | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         cleanup(); |         cleanup() | ||||||
| 
 | 
 | ||||||
|         // This only really stops the service if there are no other service connections (see docs): |         // This only really stops the service if there are no other service connections (see docs): | ||||||
|         // for example the (Android Auto) media browser binder will block stopService(). |         // for example the (Android Auto) media browser binder will block stopService(). | ||||||
| @@ -256,95 +263,96 @@ public final class PlayerService extends MediaBrowserServiceCompat { | |||||||
|         // If we were to call stopSelf(), then the service would be surely stopped (regardless of |         // If we were to call stopSelf(), then the service would be surely stopped (regardless of | ||||||
|         // other service connections), but this would be a waste of resources since the service |         // other service connections), but this would be a waste of resources since the service | ||||||
|         // would be immediately restarted by those same connections to perform the queries. |         // would be immediately restarted by those same connections to perform the queries. | ||||||
|         stopService(new Intent(this, PlayerService.class)); |         stopService(Intent(this, PlayerService::class.java)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     override fun attachBaseContext(base: Context?) { | ||||||
|     protected void attachBaseContext(final Context base) { |         super.attachBaseContext(AudioServiceLeakFix.preventLeakOf(base)) | ||||||
|         super.attachBaseContext(AudioServiceLeakFix.preventLeakOf(base)); |  | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     //endregion |     //endregion | ||||||
| 
 |  | ||||||
|     //region Bind |     //region Bind | ||||||
|     @Override |     override fun onBind(intent: Intent): IBinder? { | ||||||
|     public IBinder onBind(final Intent intent) { |  | ||||||
|         if (DEBUG) { |         if (DEBUG) { | ||||||
|             Log.d(TAG, "onBind() called with: intent = [" + intent |             Log.d( | ||||||
|                     + "], extras = [" + BundleKt.toDebugString(intent.getExtras()) + "]"); |                 TAG, | ||||||
|  |                 ( | ||||||
|  |                     "onBind() called with: intent = [" + intent + | ||||||
|  |                         "], extras = [" + intent.getExtras().toDebugString() + "]" | ||||||
|  |                     ) | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (BIND_PLAYER_HOLDER_ACTION.equals(intent.getAction())) { |         if (BIND_PLAYER_HOLDER_ACTION == intent.getAction()) { | ||||||
|             // Note that this binder might be reused multiple times while the service is alive, even |             // 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 . |             // after unbind() has been called: https://stackoverflow.com/a/8794930 . | ||||||
|             return mBinder; |             return mBinder | ||||||
| 
 |         } else if (SERVICE_INTERFACE == intent.getAction()) { | ||||||
|         } else if (MediaBrowserServiceCompat.SERVICE_INTERFACE.equals(intent.getAction())) { |  | ||||||
|             // MediaBrowserService also uses its own binder, so for actions related to the media |             // MediaBrowserService also uses its own binder, so for actions related to the media | ||||||
|             // browser service, pass the onBind to the superclass. |             // browser service, pass the onBind to the superclass. | ||||||
|             return super.onBind(intent); |             return super.onBind(intent) | ||||||
| 
 |  | ||||||
|         } else { |         } else { | ||||||
|             // This is an unknown request, avoid returning any binder to not leak objects. |             // This is an unknown request, avoid returning any binder to not leak objects. | ||||||
|             return null; |             return null | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static class LocalBinder extends Binder { |     class LocalBinder internal constructor(playerService: PlayerService?) : Binder() { | ||||||
|         private final WeakReference<PlayerService> playerService; |         private val playerService: WeakReference<PlayerService?> | ||||||
| 
 | 
 | ||||||
|         LocalBinder(final PlayerService playerService) { |         init { | ||||||
|             this.playerService = new WeakReference<>(playerService); |             this.playerService = WeakReference<PlayerService?>(playerService) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public PlayerService getService() { |         val service: PlayerService? | ||||||
|             return playerService.get(); |             get() = playerService.get() | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return the current active player instance. May be null, since the player service can outlive |  | ||||||
|      * the player e.g. to respond to Android Auto media browser queries. |  | ||||||
|      */ |  | ||||||
|     @Nullable |  | ||||||
|     public Player getPlayer() { |  | ||||||
|         return player; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Sets the listener that will be called when the player is started or stopped. If a |      * Sets the listener that will be called when the player is started or stopped. If a | ||||||
|      * {@code null} listener is passed, then the current listener will be unset. The parameter taken |      * `null` listener is passed, then the current listener will be unset. The parameter taken | ||||||
|      * by the {@link Consumer} can be null to indicate that the player is stopping. |      * by the [Consumer] can be null to indicate that the player is stopping. | ||||||
|      * @param listener the listener to set or unset |      * @param listener the listener to set or unset | ||||||
|      */ |      */ | ||||||
|     public void setPlayerListener(@Nullable final Consumer<Player> listener) { |     fun setPlayerListener(listener: Consumer<Player?>?) { | ||||||
|         this.onPlayerStartedOrStopped = listener; |         this.onPlayerStartedOrStopped = listener | ||||||
|         if (listener != null) { |         if (listener != null) { | ||||||
|             // if there is no player, then `null` will be sent here, to ensure the state is synced |             // if there is no player, then `null` will be sent here, to ensure the state is synced | ||||||
|             listener.accept(player); |             listener.accept(player) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     //endregion |  | ||||||
| 
 | 
 | ||||||
|  |     //endregion | ||||||
|     //region Media browser |     //region Media browser | ||||||
|     @Override |     override fun onGetRoot( | ||||||
|     public BrowserRoot onGetRoot(@NonNull final String clientPackageName, |         clientPackageName: String, | ||||||
|                                  final int clientUid, |         clientUid: Int, | ||||||
|                                  @Nullable final Bundle rootHints) { |         rootHints: Bundle? | ||||||
|  |     ): BrowserRoot { | ||||||
|         // TODO check if the accessing package has permission to view data |         // TODO check if the accessing package has permission to view data | ||||||
|         return mediaBrowserImpl.onGetRoot(clientPackageName, clientUid, rootHints); |         return mediaBrowserImpl!!.onGetRoot(clientPackageName, clientUid, rootHints) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     override fun onLoadChildren( | ||||||
|     public void onLoadChildren(@NonNull final String parentId, |         parentId: String, | ||||||
|                                @NonNull final Result<List<MediaBrowserCompat.MediaItem>> result) { |         result: Result<List<MediaBrowserCompat.MediaItem>> | ||||||
|         mediaBrowserImpl.onLoadChildren(parentId, result); |     ) { | ||||||
|  |         mediaBrowserImpl!!.onLoadChildren(parentId, result) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     override fun onSearch( | ||||||
|     public void onSearch(@NonNull final String query, |         query: String, | ||||||
|                          final Bundle extras, |         extras: Bundle?, | ||||||
|                          @NonNull final Result<List<MediaBrowserCompat.MediaItem>> result) { |         result: Result<List<MediaBrowserCompat.MediaItem>> | ||||||
|         mediaBrowserImpl.onSearch(query, result); |     ) { | ||||||
|  |         mediaBrowserImpl!!.onSearch(query, result) | ||||||
|  |     } //endregion | ||||||
|  | 
 | ||||||
|  |     companion object { | ||||||
|  |         private val TAG: String = PlayerService::class.java.getSimpleName() | ||||||
|  |         private val DEBUG = Player.DEBUG | ||||||
|  | 
 | ||||||
|  |         const val SHOULD_START_FOREGROUND_EXTRA: String = "should_start_foreground_extra" | ||||||
|  |         const val BIND_PLAYER_HOLDER_ACTION: String = "bind_player_holder_action" | ||||||
|     } |     } | ||||||
|     //endregion |  | ||||||
| } | } | ||||||
		Reference in New Issue
	
	Block a user
	 Profpatsch
					Profpatsch