Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion app/src/main/java/org/schabi/newpipe/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
import org.schabi.newpipe.player.event.PlayerServiceEventListener;
import org.schabi.newpipe.player.helper.AudioReactor;
import org.schabi.newpipe.player.helper.CustomRenderersFactory;
import org.schabi.newpipe.player.helper.DisplayAwareContextWrapper;
import org.schabi.newpipe.player.helper.LoadController;
import org.schabi.newpipe.player.helper.PlayerDataSource;
import org.schabi.newpipe.player.helper.PlayerHelper;
Expand Down Expand Up @@ -581,7 +582,12 @@ private void initUIsForCurrentPlayerType() {
if (playerType == PlayerType.AUDIO) {
return null;
} else {
return PlayerBinding.inflate(LayoutInflater.from(context));
final DisplayAwareContextWrapper viewContext =
new DisplayAwareContextWrapper(context);
final PlayerBinding newBinding = PlayerBinding.inflate(
LayoutInflater.from(context).cloneInContext(viewContext));
viewContext.setAnchor(newBinding.getRoot());
return newBinding;
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* SPDX-FileCopyrightText: 2026 NewPipe contributors <https://newpipe.net>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package org.schabi.newpipe.player.helper

import android.content.Context
import android.content.ContextWrapper
import android.view.View

/**
* Context for the player views, which are inflated with the player service context. That context
* is bound to the default display, so windows added on behalf of a view (e.g. popup menus and
* hover tooltips) would be added to the wrong display when the player is shown on a secondary
* display, like Samsung DeX, causing a [android.view.WindowManager.BadTokenException]. This
* wrapper returns the [android.view.WindowManager] of the display the anchor view is on.
*/
class DisplayAwareContextWrapper(base: Context) : ContextWrapper(base) {
var anchor: View? = null

override fun getSystemService(name: String): Any? {
if (name == WINDOW_SERVICE) {
anchor?.display?.let { display ->
return baseContext.createDisplayContext(display).getSystemService(name)
}
}
return super.getSystemService(name)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import static org.schabi.newpipe.player.helper.PlayerHelper.nextResizeModeAndSaveToPrefs;
import static org.schabi.newpipe.player.helper.PlayerHelper.retrieveSeekDurationFromPreferences;

import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
Expand Down Expand Up @@ -186,12 +187,15 @@ private void initViews() {
binding.playbackSeekBar.getProgressDrawable()
.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY));

final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context,
// use the context of the views, so that the popup menus are shown on the display
// the player is on (see DisplayAwareContextWrapper)
final Context viewContext = binding.getRoot().getContext();
final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(viewContext,
R.style.DarkPopupMenu);

qualityPopupMenu = new PopupMenu(themeWrapper, binding.qualityTextView);
audioTrackPopupMenu = new PopupMenu(themeWrapper, binding.audioTrackTextView);
playbackSpeedPopupMenu = new PopupMenu(context, binding.playbackSpeed);
playbackSpeedPopupMenu = new PopupMenu(viewContext, binding.playbackSpeed);
captionPopupMenu = new PopupMenu(themeWrapper, binding.captionTextView);

binding.progressBarLoadingPanel.getIndeterminateDrawable()
Expand Down
Loading