Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some simple refactors & beginning of kotlin conversions of the player classes #11965

Open
wants to merge 12 commits into
base: refactor
Choose a base branch
from

Conversation

Profpatsch
Copy link
Contributor

@Profpatsch Profpatsch commented Jan 27, 2025

What is it?

  • Bugfix (user facing)
  • Feature (user facing)
  • Codebase improvement (dev facing)
  • Meta improvement to the project (dev facing)

Description of the changes in your PR

First few commits are from #11829 (merge first & rebase)
This partially conflicts with #9592

APK testing

The APK can be found by going to the "Checks" tab below the title. On the left pane, click on "CI", scroll down to "artifacts" and click "app" to download the zip file which contains the debug APK of this PR. You can find more info and a video demonstration on this wiki page.

Due diligence

And simplify the code a little
In Kotlin, dealing with nulls works better so we don’t need optional.
The new implementation would throw `ConcurrentModificationExceptions`
when destroying the UIs. So let’s play it safe and put the list behind
a mutex.

Adds a helper class `GuardedByMutex` that can be wrapped around a
property to force all use-sites to acquire the lock before doing
anything with the data.
No adjustments done yet; only change is that I added an upper bound
on `PlayerUi` to the `PlayerUiList` functions, so “everything” is now
`null` in `destroyAll`.
This removes a bunch of unncessary checks.

We forgo setting player to `null` in onDestroy, because shutting it
down and when the service stops should be enough to free it for
garbage collection.
Because the class is final, protected does not make sense (Android
Studio auto-suggestions)
The `R.id` link from the comment cannot be resolved, so let’s not link
it for now.

We are using some exoplayer2 resources, let’s silence the warning.
It’s only used in this one place.
@github-actions github-actions bot added the size/giant PRs with more than 750 changed lines label Jan 27, 2025
@ShareASmile ShareASmile added codequality Improvements to the codebase to improve the code quality rewrite Issues and PRs related to rewrite player Issues related to any player (main, popup and background) labels Jan 27, 2025
@Isira-Seneviratne
Copy link
Member

Might be a good idea to look into migrating to Media3: https://developer.android.com/media/media3/exoplayer

@Profpatsch
Copy link
Contributor Author

@Isira-Seneviratne that’s exactly what newPlayer does. This is the initial work to integrate NewPlayer into NewPipe

@Profpatsch
Copy link
Contributor Author

i.e. adjust all the interfaces in a way that we can slot in NewPlayer easily into the existing player logic

Copy link
Member

@Stypox Stypox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good to me, thanks! You can probably ignore my comments in PlayerService since it will be quite different after the Android Auto PR anyway.

* Saves the stream progress, sets recovery.
* Then destroys the player in all UIs and destroys the UIs as well.
*/
public void saveAndShutdown() {
if (DEBUG) {
Log.d(TAG, "destroy() called");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Log.d(TAG, "destroy() called");
Log.d(TAG, "saveAndShutdown() called");

@@ -1970,6 +1976,9 @@ public void setFragmentListener(final PlayerServiceEventListener listener) {
triggerProgressUpdate();
}

/** Remove the listener, if it was set.
* @param listener listener to remove
* */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you write Javadocs this way? In my Android Studio when I type /** I already get the correctly formatted Javadoc. It's not a big issue, but it's strange you are deviating from the style used throughout the project, especially if it's also the standard style in Android Studio

Comment on lines +68 to +74
Log.d(
TAG,
(
"onStartCommand() called with: intent = [" + intent +
"], flags = [" + flags + "], startId = [" + startId + "]"
)
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Log.d(
TAG,
(
"onStartCommand() called with: intent = [" + intent +
"], flags = [" + flags + "], startId = [" + startId + "]"
)
)
Log.d(TAG, "onStartCommand() called with: intent = [$intent], flags = [$flags], startId = [$startId]")

If the service is already started in foreground, requesting it to be started shouldn't
do anything
*/
player.UIs().get<NotificationPlayerUi>(NotificationPlayerUi::class.java)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
player.UIs().get<NotificationPlayerUi>(NotificationPlayerUi::class.java)
player.UIs().get(NotificationPlayerUi::class.java)

}

player.handleIntent(intent)
player.UIs().get<MediaSessionPlayerUi>(MediaSessionPlayerUi::class.java)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
player.UIs().get<MediaSessionPlayerUi>(MediaSessionPlayerUi::class.java)
player.UIs().get(MediaSessionPlayerUi::class.java)

import java.util.Optional

class PlayerUiList(vararg initialPlayerUis: PlayerUi) {
var playerUis = GuardedByMutex(mutableListOf<PlayerUi>())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var playerUis = GuardedByMutex(mutableListOf<PlayerUi>())
private val playerUis = GuardedByMutex(mutableListOf<PlayerUi>())

@@ -761,7 +762,7 @@ public boolean isFullscreen() {
}

/**
* Update the play/pause button ({@link R.id.playPauseButton}) to reflect the action
* Update the play/pause button (`R.id.playPauseButton`) to reflect the action
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Update the play/pause button (`R.id.playPauseButton`) to reflect the action
* Update the play/pause button ({@code R.id.playPauseButton}) to reflect the action

Copy link
Contributor

@snaik20 snaik20 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few minor comments to look into

@@ -244,7 +244,7 @@ public void onServiceConnected(final PlayerService connectedPlayerService,
// It will do nothing if the player is not in fullscreen mode
hideSystemUiIfNeeded();

final Optional<MainPlayerUi> playerUi = player.UIs().get(MainPlayerUi.class);
final Optional<MainPlayerUi> playerUi = player.UIs().getOpt(MainPlayerUi.class);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest the following rather than creating a deprecated getOpt function

Suggested change
final Optional<MainPlayerUi> playerUi = player.UIs().getOpt(MainPlayerUi.class);
@Nullable final MainPlayerUi playerUi = player.UIs().get(MainPlayerUi.class);

* destroyed and removed
* @param T the class type parameter </T>
* */
fun <T : PlayerUi> destroyAllOfType(playerUiType: Class<T?>? = null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this type be Class<T>? instead of Class<T?>?

import android.os.Binder
import android.os.IBinder
import android.util.Log
import org.schabi.newpipe.player.PlayerService.LocalBinder
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to import.

* Allows us this [PlayerService] over the Service boundary
* back to our [org.schabi.newpipe.player.helper.PlayerHolder].
*/
class LocalBinder internal constructor(playerService: PlayerService?) : Binder() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class LocalBinder internal constructor(playerService: PlayerService?) : Binder() {
class LocalBinder internal constructor(playerService: PlayerService) : Binder() {

@@ -947,6 +948,7 @@ public void onShuffleClicked() {
player.toggleShuffleModeEnabled();
}

@SuppressLint("PrivateResource")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shold probably stop relying on the exoplayer's private resources.

Can you add a TODO here to replace the exo_controls_repeat_all, exo_controls_repeat_one and exo_controls_repeat_off drawables.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
codequality Improvements to the codebase to improve the code quality player Issues related to any player (main, popup and background) rewrite Issues and PRs related to rewrite size/giant PRs with more than 750 changed lines
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants