Merge pull request #3165 from karkaminski/mute_button

Mute button
This commit is contained in:
Stypox 2020-03-08 10:29:25 +01:00 committed by GitHub
commit 8fa29ffc19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 185 additions and 33 deletions

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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) {

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#4EFFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/gray"
android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z" />
</vector>

View File

@ -389,6 +389,24 @@
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/switch_to_background"
tools:ignore="RtlHardcoded"/>
<ImageButton
android:id="@+id/switchMute"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_toLeftOf="@id/switchBackground"
android:layout_centerVertical="true"
android:clickable="true"
android:focusable="true"
android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/ic_volume_off_white_24dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/switch_to_background"
tools:ignore="RtlHardcoded"
android:tint="@color/gray"/>
</RelativeLayout>
<LinearLayout

View File

@ -387,6 +387,25 @@
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/switch_to_background"
tools:ignore="RtlHardcoded"/>
<ImageButton
android:id="@+id/switchMute"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_toLeftOf="@id/switchBackground"
android:layout_centerVertical="true"
android:clickable="true"
android:focusable="true"
android:padding="5dp"
android:scaleType="fitXY"
app:srcCompat="@drawable/ic_volume_off_white_72dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/switch_to_background"
tools:ignore="RtlHardcoded"
android:tint="@color/gray"/>
</RelativeLayout>
<LinearLayout

View File

@ -10,6 +10,13 @@
android:visible="true"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_mute"
android:icon="?attr/volume_off"
android:title="@string/mute"
android:visible="true"
app:showAsAction="ifRoom"/>
<item android:id="@+id/action_settings"
android:orderInCategory="1"
android:title="@string/settings"

View File

@ -448,6 +448,8 @@
<string name="rename_playlist">Rename</string>
<string name="playlist_name_input">Name</string>
<string name="append_playlist">Add To Playlist</string>
<string name="mute">Mute</string>
<string name="unmute">Unmute</string>
<string name="set_as_playlist_thumbnail">Set as Playlist Thumbnail</string>
<string name="bookmark_playlist">Bookmark Playlist</string>
<string name="unbookmark_playlist">Remove Bookmark</string>