Skip to content
This repository has been archived by the owner on Dec 11, 2024. It is now read-only.

Commit

Permalink
feat(YouTube - Overlay buttons): Add Mute Video button (#22)
Browse files Browse the repository at this point in the history
* feat(YouTube - Overlay buttons): Add minimal MuteVolume button

* feat(YouTube - Overlay buttons): Swap icons when audio is muted or not

* feat(YouTube - Overlay buttons): Update icon when user changes the volume

* chore: Update button id and add to animation control

---------

Co-authored-by: Aaron Veil <[email protected]>
  • Loading branch information
Francesco146 and anddea authored Jul 5, 2024
1 parent 0d2fb8d commit 4a9f4a6
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import app.revanced.integrations.shared.settings.Setting;
import app.revanced.integrations.shared.utils.Logger;
import app.revanced.integrations.shared.utils.Utils;
import app.revanced.integrations.youtube.patches.overlaybutton.MuteVolume;

@SuppressWarnings({"unused", "deprecation"})
public abstract class AbstractPreferenceFragment extends PreferenceFragment {
Expand Down Expand Up @@ -281,6 +282,7 @@ public void onResume() {
@Override
public void onDestroy() {
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(listener);
MuteVolume.destroy();
super.onDestroy();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package app.revanced.integrations.youtube.patches.overlaybutton;

import static app.revanced.integrations.shared.utils.ResourceUtils.getAnimation;
import static app.revanced.integrations.shared.utils.ResourceUtils.getInteger;
import static app.revanced.integrations.shared.utils.StringRef.str;
import static app.revanced.integrations.shared.utils.Utils.getChildView;

import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
Expand All @@ -13,16 +8,19 @@
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.ImageView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.integrations.shared.settings.BooleanSetting;
import app.revanced.integrations.shared.utils.Logger;
import app.revanced.integrations.shared.utils.Utils;

import java.lang.ref.WeakReference;
import java.util.Objects;

import app.revanced.integrations.shared.settings.BooleanSetting;
import app.revanced.integrations.shared.utils.Logger;
import app.revanced.integrations.shared.utils.Utils;
import static app.revanced.integrations.shared.utils.ResourceUtils.getAnimation;
import static app.revanced.integrations.shared.utils.ResourceUtils.getInteger;
import static app.revanced.integrations.shared.utils.StringRef.str;
import static app.revanced.integrations.shared.utils.Utils.getChildView;

public abstract class BottomControlButton {
private static final Animation fadeIn;
Expand Down Expand Up @@ -117,6 +115,10 @@ public void changeSelected(boolean selected) {
primaryInteractionSetting.save(selected);
}

public void changeActivated(boolean activated) {
buttonRef.get().setActivated(activated);
}

public void changeColorFilter() {
ImageView imageView = buttonRef.get();
if (imageView == null) return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package app.revanced.integrations.youtube.patches.overlaybutton;

import android.content.IntentFilter;
import android.view.View;
import android.view.ViewGroup;
import app.revanced.integrations.shared.utils.Logger;
import app.revanced.integrations.youtube.settings.Settings;
import app.revanced.integrations.youtube.utils.VideoUtils;
import app.revanced.integrations.youtube.utils.VolumeChangeReceiver;

import static app.revanced.integrations.shared.utils.Utils.getContext;
import static app.revanced.integrations.youtube.utils.VideoUtils.isAudioMuted;

@SuppressWarnings("unused")
public class MuteVolume extends BottomControlButton {
private static MuteVolume instance;
static VolumeChangeReceiver volumeChangeReceiver = new VolumeChangeReceiver();

public MuteVolume(ViewGroup bottomControlsViewGroup) {
super(bottomControlsViewGroup,
"mute_volume_button",
Settings.OVERLAY_BUTTON_MUTE_VOLUME,
view -> {
VideoUtils.toggleMuteVolume();
if (instance != null)
instance.changeActivated(!isAudioMuted());
},
null
);
// Set the initial state of the button
this.changeActivated(!isAudioMuted());

// Register the volume change receiver to update the button state when the volume is changed
IntentFilter filter = new IntentFilter("android.media.VOLUME_CHANGED_ACTION");
getContext().registerReceiver(volumeChangeReceiver, filter);
}

public static void initialize(View ViewGroup) {
try {
if (ViewGroup instanceof ViewGroup bottomControlsViewGroup) {
instance = new MuteVolume(bottomControlsViewGroup);
}
} catch (Exception e) {
Logger.printException(() -> "initialize failure", e);
}
}

public static void changeVisibility(boolean visible, boolean animation) {
MuteVolume muteVolume = instance;
if (muteVolume != null)
muteVolume.setVisibility(visible, animation);
}

public static void changeVisibilityNegatedImmediate() {
MuteVolume muteVolume = instance;
if (muteVolume != null)
muteVolume.setVisibilityNegatedImmediate();
}

// not used
public static void notifyVolumeChange() {
// TODO: not sure if this is implementable
// ideally we would want to change the button state when the volume is changed by the user
// by calling this method on VolumeKeysController.handleVolumeKeyEvent(). However, that method
// is not run if the volume is changed by the user in the YouTube app. A possible solution
// would be to use a global listener to detect volume changes, but I'm not sure if that's possible.
Logger.printInfo(() -> "Volume changed");
if (instance != null)
instance.changeActivated(!isAudioMuted());
}

public static void destroy() {
Logger.printInfo(() -> "Destroying MuteVolume");
getContext().unregisterReceiver(volumeChangeReceiver);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public static void initializeOverlayButtons(View bottomControlsViewGroup) {
// CopyVideoUrl.initialize(bottomControlsViewGroup);
// CopyVideoUrlTimestamp.initialize(bottomControlsViewGroup);
// ExternalDownload.initialize(bottomControlsViewGroup);
// MuteVolume.initialize(bottomControlsViewGroup);
// SpeedDialog.initialize(bottomControlsViewGroup);
// TimeOrderedPlaylist.initialize(bottomControlsViewGroup);
}
Expand Down Expand Up @@ -59,6 +60,7 @@ private static void changeVisibility(boolean showing, boolean animation) {
// CopyVideoUrl.changeVisibility(showing, animation);
// CopyVideoUrlTimestamp.changeVisibility(showing, animation);
// ExternalDownload.changeVisibility(showing, animation);
// MuteVolume.changeVisibility(showing, animation);
// SpeedDialog.changeVisibility(showing, animation);
// TimeOrderedPlaylist.changeVisibility(showing, animation);

Expand Down Expand Up @@ -107,6 +109,7 @@ private static void changeVisibilityNegatedImmediately() {
// CopyVideoUrl.changeVisibilityNegatedImmediate();
// CopyVideoUrlTimestamp.changeVisibilityNegatedImmediate();
// ExternalDownload.changeVisibilityNegatedImmediate();
// MuteVolume.changeVisibilityNegatedImmediate();
// SpeedDialog.changeVisibilityNegatedImmediate();
// TimeOrderedPlaylist.changeVisibilityNegatedImmediate();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting OVERLAY_BUTTON_ALWAYS_REPEAT = new BooleanSetting("revanced_overlay_button_always_repeat", FALSE);
public static final BooleanSetting OVERLAY_BUTTON_COPY_VIDEO_URL = new BooleanSetting("revanced_overlay_button_copy_video_url", FALSE);
public static final BooleanSetting OVERLAY_BUTTON_COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_overlay_button_copy_video_url_timestamp", FALSE);
public static final BooleanSetting OVERLAY_BUTTON_MUTE_VOLUME = new BooleanSetting("revanced_overlay_button_mute_volume", FALSE);
public static final BooleanSetting OVERLAY_BUTTON_EXTERNAL_DOWNLOADER = new BooleanSetting("revanced_overlay_button_external_downloader", FALSE);
public static final BooleanSetting OVERLAY_BUTTON_SPEED_DIALOG = new BooleanSetting("revanced_overlay_button_speed_dialog", TRUE);
public static final BooleanSetting OVERLAY_BUTTON_TIME_ORDERED_PLAYLIST = new BooleanSetting("revanced_overlay_button_time_ordered_playlist", FALSE);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
package app.revanced.integrations.youtube.utils;

import static app.revanced.integrations.shared.utils.StringRef.str;
import static app.revanced.integrations.youtube.patches.video.PlaybackSpeedPatch.userSelectedPlaybackSpeed;
import static app.revanced.integrations.youtube.settings.preference.ExternalDownloaderPreference.checkPackageIsEnabled;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.Context;
import android.media.AudioManager;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;

import app.revanced.integrations.shared.settings.BooleanSetting;
import app.revanced.integrations.shared.settings.StringSetting;
import app.revanced.integrations.shared.utils.IntentUtils;
import app.revanced.integrations.shared.utils.Logger;
import app.revanced.integrations.youtube.patches.video.CustomPlaybackSpeedPatch;
import app.revanced.integrations.youtube.settings.Settings;
import app.revanced.integrations.youtube.shared.VideoInformation;
import app.revanced.integrations.youtube.swipecontrols.controller.AudioVolumeController;

import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;

import static app.revanced.integrations.shared.utils.StringRef.str;
import static app.revanced.integrations.youtube.patches.video.PlaybackSpeedPatch.userSelectedPlaybackSpeed;
import static app.revanced.integrations.youtube.settings.preference.ExternalDownloaderPreference.checkPackageIsEnabled;

@SuppressWarnings("unused")
public class VideoUtils extends IntentUtils {
Expand All @@ -32,6 +32,28 @@ public class VideoUtils extends IntentUtils {
private static final StringSetting externalDownloaderPackageName =
Settings.EXTERNAL_DOWNLOADER_PACKAGE_NAME;
private static final AtomicBoolean isExternalDownloaderLaunched = new AtomicBoolean(false);
public static AudioVolumeController audioVolumeController = new AudioVolumeController(getContext(), AudioManager.STREAM_MUSIC);
private static Integer previousVolumeLevel = 0;


public static void toggleMuteVolume() {
int currentVolume = audioVolumeController.getVolume();
if (currentVolume > 0) {
// Mute the volume
audioVolumeController.setVolume(0);
// save the current volume level to restore later
previousVolumeLevel = currentVolume;
} else {
// Unmute the volume - restore the previous volume level
audioVolumeController.setVolume(
previousVolumeLevel > 0 ? previousVolumeLevel : audioVolumeController.getMaxVolume()
);
}
}

public static boolean isAudioMuted() {
return audioVolumeController.getVolume() == 0;
}

public static void copyUrl(boolean withTimestamp) {
StringBuilder builder = new StringBuilder("https://youtu.be/");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package app.revanced.integrations.youtube.utils;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import app.revanced.integrations.youtube.patches.overlaybutton.MuteVolume;

/**
* Receiver to notify the MuteVolume button when the volume is changed
*/
public class VolumeChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if ("android.media.VOLUME_CHANGED_ACTION".equals(action)) {
MuteVolume.notifyVolumeChange();
}
}
}

0 comments on commit 4a9f4a6

Please sign in to comment.