TimerHooker Android MD3 Version

Speeds up countdown timers with a fully movable UI designed for Android screens.

Versione datata 03/04/2025. Vedi la nuova versione l'ultima versione.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name            TimerHooker Android MD3 Version
// @version         2.3.0
// @description     Speeds up countdown timers with a fully movable UI designed for Android screens.
// @include         *
// @author          Govindarajulu
// @match           http://*/*
// @run-at          document-start
// @grant           none
// @license         GPL-3.0-or-later
// @namespace https://greasyfork.org/users/1356925
// ==/UserScript==

(function (global) {
    let isSpeedActive = false;
    const speedMultiplier = 50;
    let autoHideTimeout;

    function overrideTimers(factor) {
        ["setTimeout", "setInterval"].forEach((method) => {
            window[method] = ((original) => (fn, time) => original(fn, time / factor))(window[method]);
        });
    }

    const TimerHooker = {
        toggleSpeed: function () {
            isSpeedActive = !isSpeedActive;
            overrideTimers(isSpeedActive ? speedMultiplier : 1);

            const btn = document.getElementById("toggleSpeedBtn");
            if (btn) btn.textContent = isSpeedActive ? "Stop" : "Speed";

            console.log(`[TimerHooker] Countdown timers accelerated: x${isSpeedActive ? speedMultiplier : 1}`);
            TimerHooker.resetAutoHide(); // Reset hide timer when toggled
        },

        createUI: function () {
            if (document.getElementById("timerHookerUI")) return;

            const speedControl = document.createElement("div");
            speedControl.id = "timerHookerUI";
            speedControl.style = `
                position: fixed; top: 50%; left: -35px; z-index: 99999;
                background: rgba(0,0,0,0.3); color: white; padding: 6px 12px; border-radius: 40px;
                font-size: 12px; font-weight: 500; text-align: center; cursor: grab;
                backdrop-filter: blur(8px); box-shadow: 0px 3px 8px rgba(0,0,0,0.2);
                user-select: none; transition: left 0.3s ease, top 0.1s ease, background 0.3s ease, color 0.3s ease;
                touch-action: none;
            `;

            speedControl.textContent = "Speed";
            speedControl.id = "toggleSpeedBtn";
            speedControl.addEventListener("click", () => {
                speedControl.style.left = "10px"; // Bring button fully into view
                TimerHooker.toggleSpeed();
            });

            // Enable **touch-based dragging** across Android screens
            let startX, startY, isDragging = false;

            speedControl.addEventListener("touchstart", (e) => {
                isDragging = true;
                clearTimeout(autoHideTimeout); // Stop auto-hide when dragged
                const touch = e.touches[0];
                startX = touch.clientX - speedControl.getBoundingClientRect().left;
                startY = touch.clientY - speedControl.getBoundingClientRect().top;
                speedControl.style.cursor = "grabbing";
            });

            document.addEventListener("touchmove", (e) => {
                if (!isDragging) return;
                const touch = e.touches[0];

                speedControl.style.left = `${Math.min(window.innerWidth - speedControl.offsetWidth, Math.max(0, touch.clientX - startX))}px`;
                speedControl.style.top = `${Math.min(window.innerHeight - speedControl.offsetHeight, Math.max(0, touch.clientY - startY))}px`;
            });

            document.addEventListener("touchend", () => {
                isDragging = false;
                speedControl.style.cursor = "grab";
                TimerHooker.resetAutoHide(); // Start hiding after movement stops
            });

            document.body.appendChild(speedControl);
            TimerHooker.resetAutoHide();
            console.log("[TimerHooker] UI optimized for Android successfully.");
        },

        resetAutoHide: function () {
            clearTimeout(autoHideTimeout);
            autoHideTimeout = setTimeout(() => {
                const speedControl = document.getElementById("timerHookerUI");
                if (!isDragging) speedControl.style.left = "-35px"; // Move button back to sideline
            }, 3000);
        },

        handleFullscreen: function () {
            document.addEventListener("fullscreenchange", () => {
                const speedControl = document.getElementById("timerHookerUI");
                if (document.fullscreenElement) {
                    speedControl.style.display = "none"; // Hide in fullscreen mode
                } else {
                    speedControl.style.display = "block"; // Show when fullscreen exits
                }
            });
        },

        init: function () {
            console.log("[TimerHooker] Android MD3 version activated");
            this.createUI();
            this.handleFullscreen();
        }
    };

    if (document.readyState === "complete") {
        TimerHooker.init();
    } else {
        window.addEventListener("load", () => TimerHooker.init());
    }
})(window);