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

feat(YouTube - Overlay buttons): Add Mute Video button #22

Merged
merged 5 commits into from
Jul 5, 2024
Merged
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
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();
}
}
}
Loading