Last active
November 11, 2020 15:15
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==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); | |
| })(); |
Author
Author
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
TODO: animated gradient bg to call attention more https://codepen.io/P1N2O/pen/pyBNzX