diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 9e23d9145..4eaa2a73b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -30,16 +30,16 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.os.Build; import android.os.IBinder; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.core.app.NotificationCompat; - import android.preference.PreferenceManager; import android.util.Log; import android.view.View; import android.widget.RemoteViews; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.core.app.NotificationCompat; + import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.source.MediaSource; @@ -341,7 +341,7 @@ public final class BackgroundPlayer extends Service { @Override public void handleIntent(final Intent intent) { super.handleIntent(intent); - + resetNotification(); if (bigNotRemoteView != null) bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false); @@ -389,7 +389,6 @@ public final class BackgroundPlayer extends Service { @Override public void onPrepared(boolean playWhenReady) { super.onPrepared(playWhenReady); - simpleExoPlayer.setVolume(1f); } @Override @@ -398,6 +397,12 @@ public final class BackgroundPlayer extends Service { updatePlayback(); } + @Override + public void onMuteUnmuteButtonClicked() { + super.onMuteUnmuteButtonClicked(); + updatePlayback(); + } + @Override public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) { updateProgress(currentProgress, duration, bufferPercent); diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index 46ca3921d..08fdb9258 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -153,6 +153,8 @@ public abstract class BasePlayer implements public static final String START_PAUSED = "start_paused"; @NonNull public static final String SELECT_ON_APPEND = "select_on_append"; + @NonNull + public static final String IS_MUTED = "is_muted"; /*////////////////////////////////////////////////////////////////////////// // Playback @@ -275,6 +277,7 @@ public abstract class BasePlayer implements final float playbackPitch = intent.getFloatExtra(PLAYBACK_PITCH, getPlaybackPitch()); final boolean playbackSkipSilence = intent.getBooleanExtra(PLAYBACK_SKIP_SILENCE, getPlaybackSkipSilence()); + final boolean isMuted = intent.getBooleanExtra(IS_MUTED, simpleExoPlayer == null ? false : isMuted()); // seek to timestamp if stream is already playing if (simpleExoPlayer != null @@ -283,7 +286,7 @@ public abstract class BasePlayer implements && playQueue.getItem() != null && queue.getItem().getUrl().equals(playQueue.getItem().getUrl()) && queue.getItem().getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET - ) { + ) { simpleExoPlayer.seekTo(playQueue.getIndex(), queue.getItem().getRecoveryPosition()); return; @@ -293,7 +296,7 @@ public abstract class BasePlayer implements stateLoader = recordManager.loadStreamState(item) .observeOn(AndroidSchedulers.mainThread()) .doFinally(() -> initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, - /*playOnInit=*/true)) + /*playOnInit=*/true, isMuted)) .subscribe( state -> queue.setRecovery(queue.getIndex(), state.getProgressTime()), error -> { @@ -306,7 +309,7 @@ public abstract class BasePlayer implements } // Good to go... initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, - /*playOnInit=*/!intent.getBooleanExtra(START_PAUSED, false)); + /*playOnInit=*/!intent.getBooleanExtra(START_PAUSED, false), isMuted); } protected void initPlayback(@NonNull final PlayQueue queue, @@ -314,7 +317,8 @@ public abstract class BasePlayer implements final float playbackSpeed, final float playbackPitch, final boolean playbackSkipSilence, - final boolean playOnReady) { + final boolean playOnReady, + final boolean isMuted) { destroyPlayer(); initPlayer(playOnReady); setRepeatMode(repeatMode); @@ -327,6 +331,8 @@ public abstract class BasePlayer implements if (playQueueAdapter != null) playQueueAdapter.dispose(); playQueueAdapter = new PlayQueueAdapter(context, playQueue); + + simpleExoPlayer.setVolume(isMuted ? 0 : 1); } public void destroyPlayer() { @@ -532,6 +538,18 @@ public abstract class BasePlayer implements if (simpleExoPlayer == null) return; simpleExoPlayer.setShuffleModeEnabled(!simpleExoPlayer.getShuffleModeEnabled()); } + /*////////////////////////////////////////////////////////////////////////// + // Mute / Unmute + //////////////////////////////////////////////////////////////////////////*/ + + public void onMuteUnmuteButtonClicked() { + if (DEBUG) Log.d(TAG, "onMuteUnmuteButtonClicled() called"); + simpleExoPlayer.setVolume(isMuted() ? 1 : 0); + } + + public boolean isMuted() { + return simpleExoPlayer.getVolume() == 0; + } /*////////////////////////////////////////////////////////////////////////// // Progress Updates diff --git a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java index f779c7bf9..51dff970f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java @@ -34,14 +34,17 @@ import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.provider.Settings; + import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.content.res.AppCompatResources; +import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.ItemTouchHelper; + import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; @@ -116,7 +119,8 @@ public final class MainVideoPlayer extends AppCompatActivity private SharedPreferences defaultPreferences; - @Nullable private PlayerState playerState; + @Nullable + private PlayerState playerState; private boolean isInMultiWindow; private boolean isBackPressed; @@ -130,11 +134,13 @@ public final class MainVideoPlayer extends AppCompatActivity protected void onCreate(@Nullable Bundle savedInstanceState) { assureCorrectAppLanguage(this); super.onCreate(savedInstanceState); - if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]"); + if (DEBUG) + Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]"); defaultPreferences = PreferenceManager.getDefaultSharedPreferences(this); ThemeHelper.setTheme(this); getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK)); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) getWindow().setStatusBarColor(Color.BLACK); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + getWindow().setStatusBarColor(Color.BLACK); setVolumeControlStream(AudioManager.STREAM_MUSIC); WindowManager.LayoutParams lp = getWindow().getAttributes(); @@ -143,7 +149,7 @@ public final class MainVideoPlayer extends AppCompatActivity hideSystemUi(); setContentView(R.layout.activity_main_player); - playerImpl = new VideoPlayerImpl(this); + playerImpl = new VideoPlayerImpl(this); playerImpl.setup(findViewById(android.R.id.content)); if (savedInstanceState != null && savedInstanceState.get(KEY_SAVED_STATE) != null) { @@ -220,7 +226,7 @@ public final class MainVideoPlayer extends AppCompatActivity playerImpl.setPlaybackQuality(playerState.getPlaybackQuality()); playerImpl.initPlayback(playerState.getPlayQueue(), playerState.getRepeatMode(), playerState.getPlaybackSpeed(), playerState.getPlaybackPitch(), - playerState.isPlaybackSkipSilence(), playerState.wasPlaying()); + playerState.isPlaybackSkipSilence(), playerState.wasPlaying(), playerImpl.isMuted()); } } @@ -248,7 +254,7 @@ public final class MainVideoPlayer extends AppCompatActivity if (playerImpl == null) return; playerImpl.setRecovery(); - if(!playerImpl.gotDestroyed()) { + if (!playerImpl.gotDestroyed()) { playerState = createPlayerState(); } StateSaver.tryToSave(isChangingConfigurations(), null, outState, this); @@ -396,6 +402,11 @@ public final class MainVideoPlayer extends AppCompatActivity shuffleButton.setImageAlpha(shuffleAlpha); } + protected void setMuteButton(final ImageButton muteButton, final boolean isMuted) { + muteButton.setColorFilter(ContextCompat.getColor(getApplicationContext(), isMuted ? R.color.white : R.color.gray)); + } + + private boolean isInMultiWindow() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && isInMultiWindowMode(); } @@ -448,6 +459,7 @@ public final class MainVideoPlayer extends AppCompatActivity private ImageButton toggleOrientationButton; private ImageButton switchPopupButton; private ImageButton switchBackgroundButton; + private ImageButton muteButton; private RelativeLayout windowRootLayout; private View secondaryControls; @@ -484,6 +496,7 @@ public final class MainVideoPlayer extends AppCompatActivity this.shareButton = rootView.findViewById(R.id.share); this.toggleOrientationButton = rootView.findViewById(R.id.toggleOrientation); this.switchBackgroundButton = rootView.findViewById(R.id.switchBackground); + this.muteButton = rootView.findViewById(R.id.switchMute); this.switchPopupButton = rootView.findViewById(R.id.switchPopup); this.queueLayout = findViewById(R.id.playQueuePanel); @@ -493,7 +506,7 @@ public final class MainVideoPlayer extends AppCompatActivity titleTextView.setSelected(true); channelTextView.setSelected(true); boolean showKodiButton = PreferenceManager.getDefaultSharedPreferences(this.context).getBoolean( - this.context.getString(R.string.show_play_with_kodi_key), false); + this.context.getString(R.string.show_play_with_kodi_key), false); kodiButton.setVisibility(showKodiButton ? View.VISIBLE : View.GONE); getRootView().setKeepScreenOn(true); @@ -535,6 +548,7 @@ public final class MainVideoPlayer extends AppCompatActivity shareButton.setOnClickListener(this); toggleOrientationButton.setOnClickListener(this); switchBackgroundButton.setOnClickListener(this); + muteButton.setOnClickListener(this); switchPopupButton.setOnClickListener(this); getRootView().addOnLayoutChangeListener((view, l, t, r, b, ol, ot, or, ob) -> { @@ -653,7 +667,8 @@ public final class MainVideoPlayer extends AppCompatActivity this.getPlaybackSkipSilence(), this.getPlaybackQuality(), false, - !isPlaying() + !isPlaying(), + isMuted() ); context.startService(intent); @@ -677,7 +692,8 @@ public final class MainVideoPlayer extends AppCompatActivity this.getPlaybackSkipSilence(), this.getPlaybackQuality(), false, - !isPlaying() + !isPlaying(), + isMuted() ); context.startService(intent); @@ -686,6 +702,12 @@ public final class MainVideoPlayer extends AppCompatActivity finish(); } + @Override + public void onMuteUnmuteButtonClicked() { + super.onMuteUnmuteButtonClicked(); + setMuteButton(muteButton, playerImpl.isMuted()); + } + @Override public void onClick(View v) { @@ -723,11 +745,14 @@ public final class MainVideoPlayer extends AppCompatActivity } else if (v.getId() == switchBackgroundButton.getId()) { onPlayBackgroundButtonClicked(); + } else if (v.getId() == muteButton.getId()) { + onMuteUnmuteButtonClicked(); + } else if (v.getId() == closeButton.getId()) { onPlaybackShutdown(); return; } else if (v.getId() == kodiButton.getId()) { - onKodiShare(); + onKodiShare(); } if (getCurrentState() != STATE_COMPLETED) { @@ -770,13 +795,14 @@ public final class MainVideoPlayer extends AppCompatActivity animateView(secondaryControls, SLIDE_AND_ALPHA, !isMoreControlsVisible, DEFAULT_CONTROLS_DURATION); showControls(DEFAULT_CONTROLS_DURATION); + setMuteButton(muteButton, playerImpl.isMuted()); } private void onShareClicked() { // share video at the current time (youtube.com/watch?v=ID&t=SECONDS) ShareUtils.shareUrl(MainVideoPlayer.this, playerImpl.getVideoTitle(), - playerImpl.getVideoUrl() + "&t=" + String.valueOf(playerImpl.getPlaybackSeekBar().getProgress()/1000)); + playerImpl.getVideoUrl() + "&t=" + String.valueOf(playerImpl.getPlaybackSeekBar().getProgress() / 1000)); } private void onScreenRotationClicked() { @@ -1009,7 +1035,7 @@ public final class MainVideoPlayer extends AppCompatActivity @Override public void onSwiped(int index) { - if(index != -1) playQueue.remove(index); + if (index != -1) playQueue.remove(index); } }; } @@ -1074,6 +1100,10 @@ public final class MainVideoPlayer extends AppCompatActivity return repeatButton; } + public ImageButton getMuteButton() { + return muteButton; + } + public ImageButton getPlayPauseButton() { return playPauseButton; } @@ -1088,7 +1118,8 @@ public final class MainVideoPlayer extends AppCompatActivity @Override public boolean onDoubleTap(MotionEvent e) { - if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); + if (DEBUG) + Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); if (e.getX() > playerImpl.getRootView().getWidth() * 2 / 3) { playerImpl.onFastForward(); @@ -1184,7 +1215,8 @@ public final class MainVideoPlayer extends AppCompatActivity layoutParams.screenBrightness = currentProgressPercent; getWindow().setAttributes(layoutParams); - if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentProgressPercent); + if (DEBUG) + Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentProgressPercent); final int resId = currentProgressPercent < 0.25 ? R.drawable.ic_brightness_low_white_72dp @@ -1223,7 +1255,8 @@ public final class MainVideoPlayer extends AppCompatActivity @Override public boolean onTouch(View v, MotionEvent event) { //noinspection PointlessBooleanExpression - if (DEBUG && false) Log.d(TAG, "onTouch() called with: v = [" + v + "], event = [" + event + "]"); + if (DEBUG && false) + Log.d(TAG, "onTouch() called with: v = [" + v + "], event = [" + event + "]"); gestureDetector.onTouchEvent(event); if (event.getAction() == MotionEvent.ACTION_UP && isMoving) { isMoving = false; diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java index fc14e8d51..b7638eda7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java @@ -571,7 +571,8 @@ public final class PopupVideoPlayer extends Service { this.getPlaybackSkipSilence(), this.getPlaybackQuality(), false, - !isPlaying() + !isPlaying(), + isMuted() ); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); @@ -607,6 +608,12 @@ public final class PopupVideoPlayer extends Service { updatePlayback(); } + @Override + public void onMuteUnmuteButtonClicked() { + super.onMuteUnmuteButtonClicked(); + updatePlayback(); + } + @Override public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) { updateProgress(currentProgress, duration, bufferPercent); diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java index 7aa2be05d..e7700351e 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java @@ -3,14 +3,19 @@ package org.schabi.newpipe.player; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.IBinder; import android.provider.Settings; + import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.appcompat.widget.Toolbar; import androidx.recyclerview.widget.ItemTouchHelper; + import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -92,6 +97,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity private TextView playbackSpeedButton; private TextView playbackPitchButton; + private Menu menu; + //////////////////////////////////////////////////////////////////////////// // Abstracts //////////////////////////////////////////////////////////////////////////// @@ -145,8 +152,10 @@ public abstract class ServicePlayerActivity extends AppCompatActivity @Override public boolean onCreateOptionsMenu(Menu menu) { + this.menu = menu; getMenuInflater().inflate(R.menu.menu_play_queue, menu); getMenuInflater().inflate(getPlayerOptionMenuResource(), menu); + onMaybeMuteChanged(); return true; } @@ -162,6 +171,9 @@ public abstract class ServicePlayerActivity extends AppCompatActivity case R.id.action_append_playlist: appendAllToPlaylist(); return true; + case R.id.action_mute: + player.onMuteUnmuteButtonClicked(); + return true; case R.id.action_system_audio: startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS)); return true; @@ -169,8 +181,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity this.player.setRecovery(); getApplicationContext().sendBroadcast(getPlayerShutdownIntent()); getApplicationContext().startActivity( - getSwitchIntent(MainVideoPlayer.class) - .putExtra(BasePlayer.START_PAUSED, !this.player.isPlaying()) + getSwitchIntent(MainVideoPlayer.class) + .putExtra(BasePlayer.START_PAUSED, !this.player.isPlaying()) ); return true; } @@ -194,7 +206,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity this.player.getPlaybackSkipSilence(), null, false, - false + false, + this.player.isMuted() ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(BasePlayer.START_PAUSED, !this.player.isPlaying()); } @@ -212,7 +225,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity } private void unbind() { - if(serviceBound) { + if (serviceBound) { unbindService(serviceConnection); serviceBound = false; stopPlayerListener(); @@ -554,6 +567,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity onPlayModeChanged(repeatMode, shuffled); onPlaybackParameterChanged(parameters); onMaybePlaybackAdapterChanged(); + onMaybeMuteChanged(); } @Override @@ -676,4 +690,21 @@ public abstract class ServicePlayerActivity extends AppCompatActivity itemsList.setAdapter(maybeNewAdapter); } } + + private void onMaybeMuteChanged() { + if (menu != null && player != null) { + MenuItem item = menu.findItem(R.id.action_mute); + + //Change the mute-button item in ActionBar + //1) Text change: + item.setTitle(player.isMuted() ? R.string.unmute : R.string.mute); + + //2) Icon change accordingly to current App Theme + TypedArray a = getTheme().obtainStyledAttributes(R.style.Theme_AppCompat, new int[]{R.attr.volume_off}); + int attributeResourceId = a.getResourceId(0, 0); + Drawable drawableMuted = getResources().getDrawable(attributeResourceId); + Drawable drawableUnmuted = getResources().getDrawable(R.drawable.ic_volume_off_gray_24dp); + item.setIcon(player.isMuted() ? drawableMuted : drawableUnmuted); + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index a19aa92ae..98264e1bf 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -110,13 +110,15 @@ public class NavigationHelper { final boolean playbackSkipSilence, @Nullable final String playbackQuality, final boolean resumePlayback, - final boolean startPaused) { + final boolean startPaused, + final boolean isMuted) { return getPlayerIntent(context, targetClazz, playQueue, playbackQuality, resumePlayback) .putExtra(BasePlayer.REPEAT_MODE, repeatMode) .putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed) .putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch) .putExtra(BasePlayer.PLAYBACK_SKIP_SILENCE, playbackSkipSilence) - .putExtra(BasePlayer.START_PAUSED, startPaused); + .putExtra(BasePlayer.START_PAUSED, startPaused) + .putExtra(BasePlayer.IS_MUTED, isMuted); } public static void playOnMainPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) { diff --git a/app/src/main/res/drawable/ic_volume_off_gray_24dp.xml b/app/src/main/res/drawable/ic_volume_off_gray_24dp.xml new file mode 100644 index 000000000..ade6bfec2 --- /dev/null +++ b/app/src/main/res/drawable/ic_volume_off_gray_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml index 8e11b99f3..10fda556a 100644 --- a/app/src/main/res/layout-large-land/activity_main_player.xml +++ b/app/src/main/res/layout-large-land/activity_main_player.xml @@ -389,6 +389,24 @@ android:background="?attr/selectableItemBackground" android:contentDescription="@string/switch_to_background" tools:ignore="RtlHardcoded"/> + + + + + + + Rename Name Add To Playlist + Mute + Unmute Set as Playlist Thumbnail Bookmark Playlist Remove Bookmark