Skip to content

Instantly share code, notes, and snippets.

@paulera
Last active November 11, 2020 15:15
Show Gist options
  • Select an option

  • Save paulera/af3d1a56a3612472a11dd0bee6c3812d to your computer and use it in GitHub Desktop.

Select an option

Save paulera/af3d1a56a3612472a11dd0bee6c3812d to your computer and use it in GitHub Desktop.
Google Meet Mute Indicator: Updates the favicon and shows a mute indicator on screen (Tampermonkey / Greasemonkey userscript, can also be executed directly in the browser console)
// ==UserScript==
// @name Google Meet mute indicator
// @namespace unforgivenexception.com
// @version 1.0
// @description Updates the favicon and shows a mute indicator on screen
// @author Paulo Amaral
// @match https://meet.google.com/*
// @grant none
// ==/UserScript==
(function() {
// The emojis that will indicate the state. They will replace the favicon of
// the window. The mute emoji will be displayed large on top of everything
// when on mute.
window.emojiMute = "🙊";
window.emojiSpeaking = "🔊";
// Interval (milliseconds) in which the function to update the UI will be
// repeatedly called
window.intervalUpdateUI = 500;
// Returns a Boolean indicating whether the mute state is activated
function isMute() {
// TODO: optimize me - instead of searching the DOM everytime, find
// element and check the property data-is-muted directly.
return document.querySelectorAll("div[data-is-muted]")[1].getAttribute("data-is-muted") == "true"
}
// Update the window favicon according to the mute state, replacing it with
// the emojis defined on the begining of this code.
function updateFavicon(muteState) {
// Uses a <link rel="icon"> element to set the favicon
// with a inline svg
var id = "faviconLinkElement";
var linkElement = document.getElementById(id);
if (linkElement == null) {
// <link> element not available yet, so creates one.
var headElement = document.getElementsByTagName('head')[0];
linkElement = document.createElement('link');
linkElement.id = (id);
linkElement.rel = "icon";
headElement.appendChild(linkElement);
}
// change the <link> element href to manipulate the favicon
var emoji = (muteState)?window.emojiMute:window.emojiSpeaking;
linkElement.href = "data:image/svg+xml,<svg xmlns=%22http://www.w3.org"+
"/2000/svg%22 viewBox=%220 0 100 100%22><text y=%221em%22 "+
"font-size=%2280%22>"+emoji+"</text></svg>";
}
// Show/hide the floating indicator for mute status (true = show)
function updateFloatingIndicator(muteState) {
var id = "muteIndicator";
var divElement = document.getElementById(id);
if (divElement == null) {
var bodyElement = document.getElementsByTagName('body')[0];
divElement = document.createElement("div");
divElement.id = id;
divElement.style.zIndex = 9999;
divElement.style.display = "none";
divElement.style.position = "absolute";
divElement.style.border = "4px solid black";
divElement.style.borderRadius = "50%";
divElement.style.width = "30vh";
divElement.style.height = "30vh";
divElement.style.textAlign = "center";
divElement.style.backgroundColor = "#ff0000";
divElement.style.opacity = "0.7";
divElement.style.marginLeft = "1vh"
divElement.style.marginTop = "1vh"
var spanElement = document.createElement("span");
spanElement.innerHTML = window.emojiMute;
spanElement.style.fontSize = "20vh"; // 20% of viewport height
divElement.appendChild(spanElement);
bodyElement.appendChild(divElement);
}
divElement.style.display = (muteState)?"block":"none";
}
// This is the main function, which will check the mute state and
// call UI updates accordingly. This function is idempotent.
function updateUI() {
// console.log ("mute indicator updateUI() - ping 💧");
// window.interfaceMuted is the variable used to manage idempotency.
// When it is "undefined", it means the code is running for the first
// time and a UI update is required regardless the current state.
if (isMute()) {
if (!window.interfaceMuted ||
typeof window.interfaceMuted === "undefined") {
// console.log ("YOU ARE MUTED " + window.emojiMute);
updateFavicon(true);
updateFloatingIndicator(true);
window.interfaceMuted = true;
}
} else {
if (window.interfaceMuted ||
typeof window.interfaceMuted === "undefined") {
// console.log ("YOU ARE SPEAKING " + window.emojiSpeaking);
updateFavicon(false);
updateFloatingIndicator(false);
window.interfaceMuted = false;
}
}
}
// Auxiliar function to shutdown UI updates. Must be called manually. States
// and updates won't be reverted, they will just stop happening.
function abortUpdateUI() {
if (window.timerCheckMuteState) {
clearInterval(window.timerCheckMuteState)
}
}
// Run the abort function before kicking off UI updates for idempotency
// purposes. Just in case this code was already running and it is being
// executed again.
abortUpdateUI();
// Runs UI updates repeatedly. Call the function abortUpdateUI()
// in the console to abort.
// TODO: optimize me - call UI updates a few milisseconds after mute buton
// is clicked intead of in a loop.
window.timerCheckMuteState = setInterval(updateUI, window.intervalUpdateUI);
})();
@paulera
Copy link
Copy Markdown
Author

paulera commented Nov 11, 2020

TODO: animated gradient bg to call attention more https://codepen.io/P1N2O/pen/pyBNzX

@paulera
Copy link
Copy Markdown
Author

paulera commented Nov 11, 2020

TODO: drag and drop the mute indicator. Something like that

window.muteIndicatorDeltaX = 0;
window.muteIndicatorDeltaY = 0;

function muteIndicatorMouseUp() {
  // TODO: call me if unmuted during a dragging operation
  window.removeEventListener('mousemove', divMove, true);
}

function muteIndicatorMouseDown(e) {
  var divElement = document.getElementById('muteIndicator');
  window.muteIndicatorDeltaX = e.clientX - divElement.style.left;
  window.muteIndicatorDeltaY = e.clientY - divElement.style.top;
  window.addEventListener('mousemove', muteIndicatorDivMove, true);
}

function muteIndicatorDivMove(e) {
  var divElement = document.getElementById('muteIndicator');
  divElement.style.position = 'absolute';
  divElement.style.top = (e.clientY - window.muteIndicatorDeltaY) + 'px';
  divElement.style.left = (e.clientX - window.muteIndicatorDeltaX)+ 'px';
}

function enableMuteIndicatorDrag(enabled = true) {
  if (enabled) {
    document.getElementById('muteIndicator').addEventListener('mousedown', muteIndicatorMouseDown, false);
    window.addEventListener('mouseup', muteIndicatorMouseUp, false);
  } else {
    document.getElementById('muteIndicator').removeEventListener('mousedown', muteIndicatorMouseDown, false);
    window.removeEventListener('mouseup', muteIndicatorMouseUp, false);
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment