Konubinix' opinionated web of thoughts

Interval Timer

Evergreen

Useful for kung fu training.

pour se brosser les dents

Adding wakelock support and offline mode

https://konubinix.eu/ipfs/bafybeiexbmasbybxs23rd2djeghls3i2guzsbugr3szni76oinz6tkjt7u

https://konubinix.eu/ipns/k51qzi5uqu5dmddig1cynyicnt0upc70dyh1zfeh5khdsrpkircc6717afabrz

From the original.

ipfs get https://konubinix.eu/ipfs/bafybeiapftob6e7j7pjv72auxamh522sivapb6fq2tx3yjuvmsmeertr5i -o /tmp/brush
Saving file(s) to /tmp/brush

To do so, I changed the service workers to cache the data

// taken from https://googlechrome.github.io/samples/service-worker/basic/

const PRECACHE = 'precache-v1';
const RUNTIME = 'runtime';

// A list of local resources we always want to be cached.
const PRECACHE_URLS = [
    'index.html',
    'js/main.js',
    "img/icon.png",
    "img/icon-48x48.png",
    "img/icon-96x96.png",
    "img/icon-144x144.png",
    "img/icon-192x192.png",
    "img/icon-512x512.png",
    "manifest.json",
    "css/framework.min.css",
    "sound/alert.ogg",
    "sound/end.ogg",
    "sound/gong.ogg",
    "sound/gong2.ogg",
];


self.addEventListener('install', event => {
    event.waitUntil(
        caches.open(PRECACHE)
            .then(cache => cache.addAll(PRECACHE_URLS))
            .then(self.skipWaiting())
    );
});

// clean up old caches
self.addEventListener('activate', event => {
    const currentCaches = [PRECACHE, RUNTIME];
    event.waitUntil(
        caches.keys().then(cacheNames => {
            return cacheNames.filter(cacheName => !currentCaches.includes(cacheName));
        }).then(cachesToDelete => {
            return Promise.all(cachesToDelete.map(cacheToDelete => {
                return caches.delete(cacheToDelete);
            }));
        }).then(() => self.clients.claim())
    );
});

self.addEventListener('fetch', event => {
    // Skip cross-origin requests, like those for Google Analytics.
    if (event.request.url.startsWith(self.location.origin)) {
        event.respondWith(
            caches.match(event.request).then(cachedResponse => {
                if (cachedResponse) {
                    return cachedResponse;
                }

                return caches.open(RUNTIME).then(cache => {
                    return fetch(event.request).then(response => {
                        // Put a copy of the response in the runtime cache.
                        return cache.put(event.request, response.clone()).then(() => {
                            return response;
                        });
                    });
                });
            })
        );
    }
});

And I registered it in the main.js application. Also I changed a bit the logic and the naming to indicate that it was about brushing the teeths.

document.addEventListener('DOMContentLoaded', function () {
    app.init();
});

var app = {

    play: document.getElementById('play'),
    pause: document.getElementById('pause'),
    reset: document.getElementById('reset'),
    setDiv: document.getElementById('setDiv'),
    chronoDiv: document.getElementById('chronoDiv'),
    setsValue: document.getElementById('setsValue'),
    workValue: document.getElementById('workValue'),
    workValueInt: 20,
    workFinish: 0,
    restValue: document.getElementById('restValue'),
    restValueInt: 10,
    timeReset: 3000,
    positionNavigator: 0,
    state: 1, // 1 -> config, 2 -> chrono
    stateChrono: 1,  // 1 -> rest, 2 -> work
    cycle: document.getElementById('cycle'),
    timeRest: document.getElementById('timeRest'),
    timeRestInt: 0,
    textChrono: document.getElementById('textChrono'),
    interval: null,
    intervalButton: null,
    audio: new Audio(),
    isKaiOS: false,
    isPaused: false,
    modalPause: document.getElementById('modalPause'),
    modalReset: document.getElementById('modalReset'),
    noSleepVideo: document.getElementById('noSleepVideo'),

    wakelock: null,

    workTimeElement: document.getElementById('workTime'),
    workTime: 0,

    playSound: function(srcSound) {
        app.audio.src = "./sound/" + srcSound + ".ogg";
        app.audio.play();
    },

    init: function() {
        app.noSleepVideo.pause();
        document.getElementById("setsLess").focus();
        document.addEventListener('keydown', app.manejarTeclado);

        app.play.addEventListener('click', app.initChrono);
        app.reset.addEventListener('click', app.resetChrono);
        app.pause.addEventListener('click', app.pauseChrono);

        let classnameLess = document.getElementsByClassName('lessBtn');
        for (var i = 0; i < classnameLess.length; i++) {
            classnameLess[i].addEventListener('click', app.lessValue);
            classnameLess[i].addEventListener('mousedown', app.lessValueCont);
            classnameLess[i].addEventListener('touchstart', app.lessValueCont);
            classnameLess[i].addEventListener('mouseup', app.contEnd);
            classnameLess[i].addEventListener('touchend', app.contEnd);
        }

        let classnameMore = document.getElementsByClassName('moreBtn');
        for (var i = 0; i < classnameMore.length; i++) {
            classnameMore[i].addEventListener('click', app.moreValue);
            classnameMore[i].addEventListener('mousedown', app.moreValueCont);
            classnameMore[i].addEventListener('touchstart', app.moreValueCont);
            classnameMore[i].addEventListener('mouseup', app.contEnd);
            classnameMore[i].addEventListener('touchend', app.contEnd);
        }

        app.updateWorkTime();

        if ('serviceWorker' in navigator) {
            navigator.serviceWorker
                .register('./sw.js')
                .then(function() {
                    console.log('Service Worker Registered');
                });
        }
    },

    initChrono: function() {
        app.noSleepVideo.play();
        setDiv.classList.add("hide");
        chronoDiv.classList.remove("hide");
        app.state = 2;
        app.positionNavigator = 0;
        /* TODO refactorizar */
        document.getElementsByClassName('nv-select')[0].classList.remove('nv-select');
        document.getElementsByClassName('navigatorChrono')[0].classList.add('nv-select');
        /* TODO refactorizar */
        cycle.innerText = (app.workFinish + 1) + "/" + app.setsValue.innerText;
        //app.timeRest.innerText =  10; // app.restValue.innerText;
        document.getElementById('principalDiv').classList.add('rest');

        //init clock
        app.textChrono.innerText = "Attends"; /* TODO refactorizar */
        /* TODO refactorizar */
        document.getElementsByTagName("html")[0].classList.add('rest');
        document.getElementsByClassName("jumbotron")[0].classList.add('rest');
        /* TODO refactorizar */
        let restTime = 5; //app.restValue.innerText;
        app.timeRestInt = restTime;
        app.timeRest.innerText = app.formatTime(app.timeRestInt);
        app.interval = setInterval(function() {
            if (!app.isPaused) {
                if (restTime === 0) {
                    if (app.stateChrono === 1) {
                        restTime = app.workValueInt;
                        app.stateChrono = 2;
                        app.textChrono.innerText = "Brosse les dents"; /* TODO refactorizar */
                        /* TODO refactorizar */
                        document.getElementsByTagName("html")[0].classList.remove('rest');
                        document.getElementsByClassName("jumbotron")[0].classList.remove('rest');
                        document.getElementsByTagName("html")[0].classList.add('work');
                        document.getElementsByClassName("jumbotron")[0].classList.add('work');
                        /* TODO refactorizar */
                        app.playSound('alert');
                    } else {
                        restTime = app.restValueInt;
                        app.stateChrono = 1;
                        app.textChrono.innerText = "C'est la pause"; /* TODO refactorizar */
                        app.workFinish += 1;
                        if (app.workFinish >= app.setsValue.innerText){
                            clearInterval(app.interval);
                            app.playSound('gong');
                            app.textChrono.innerText = "Finish !!!"; /* TODO refactorizar */
                            clearInterval(app.interval);
                            restTime = 0;
                            setTimeout(function () {
                                app.showConfig();
                            }, app.timeReset);
                        } else {
                            cycle.innerText = (app.workFinish + 1) + "/" + app.setsValue.innerText;
                            /* TODO refactorizar */
                            document.getElementsByTagName("html")[0].classList.remove('work');
                            document.getElementsByClassName("jumbotron")[0].classList.remove('work');
                            document.getElementsByTagName("html")[0].classList.add('rest');
                            document.getElementsByClassName("jumbotron")[0].classList.add('rest');
                            /* TODO refactorizar */
                            app.playSound('gong2');
                        }
                    }
                } else {
                    restTime -=1;
                }
                app.timeRestInt = restTime;
                app.timeRest.innerText = app.formatTime(app.timeRestInt);
                if (restTime === 3) {
                    app.playSound('end');
                }
            }
        }, 1000);
        navigator.wakeLock.request("screen").then(
            (w) => {
                app.wakelock = w;
                // alert("acquired")
            }
        ).catch((err) => {
            alert(`${err.name}, ${err.message}`);
        });
    },

    resetChrono: function() {
        if(app.isKaiOS) {
            if ( (app.workFinish >= app.setsValue.innerText) || confirm('¿Reset chrono?')) { //TODO no preguntar
                clearInterval(app.interval);
                app.stateChrono = 1;
                app.workFinish = 0;
                app.showConfig();
            }
        } else {
            app.isPaused = true;
            app.modalReset.classList.remove('hide');

            let okReset = document.getElementById("okReset");
            let closeReset = document.getElementById("closeReset");
            let closeResetFunction;
            let okResetFunction = () => {
                app.isPaused = false;
                app.modalReset.classList.add('hide');
                okReset.removeEventListener("click", okResetFunction);
                closeReset.removeEventListener("click", closeResetFunction);
                clearInterval(app.interval);
                app.stateChrono = 1;
                app.workFinish = 0;
                app.showConfig();
            }

            closeResetFunction = () => {
                app.isPaused = false;
                app.modalReset.classList.add('hide');
                okReset.removeEventListener("click", okResetFunction);
                closeReset.removeEventListener("click", closeResetFunction);
            }

            okReset.addEventListener("click", okResetFunction);
            closeReset.addEventListener("click", closeResetFunction);
            /*
              document.getElementById('okReset').addEventListener('click', () => {
              app.isPaused = false;
              app.modalReset.classList.add('hide');
              document.getElementById('okReset').removeEventListener('click', ()=> {});
              clearInterval(app.interval);
              app.stateChrono = 1;
              app.workFinish = 0;
              app.showConfig();
              }, false);
              document.getElementById('closeReset').addEventListener('click', () => {
              app.isPaused = false;
              app.modalReset.classList.add('hide');
              document.getElementById('closeReset').removeEventListener('click', ()=> {});
              }, false);
            */
        }

    },

    updateWorkTime: function() {
        app.workTime = (app.workValueInt * parseInt(app.setsValue.innerText) ) + (app.restValueInt * (parseInt(app.setsValue.innerText) - 1) );
        app.workTimeElement.innerText = app.formatTime(app.workTime);
    },

    showConfig: function() {
        app.noSleepVideo.pause();
        /* TODO refactorizar */
        document.getElementsByTagName("html")[0].classList.remove('work');
        document.getElementsByClassName("jumbotron")[0].classList.remove('work');
        document.getElementsByTagName("html")[0].classList.remove('rest');
        document.getElementsByClassName("jumbotron")[0].classList.remove('rest');
        /* TODO refactorizar */
        app.setDiv.classList.remove("hide");
        app.chronoDiv.classList.add("hide");
        app.state = 1;
        app.positionNavigator = 0;
        app.workFinish = 0;
        /* TODO refactorizar */
        document.getElementsByClassName('nv-select')[0].classList.remove('nv-select');
        document.getElementsByClassName('navigatorConfig')[0].classList.add('nv-select');
        /* TODO refactorizar */
        if (app.wakelock != null) {
            app.wakelock.release().then(() => {
                app.wakelock = null;
                // alert("released");
            }).catch((err) => {
                alert(`${err.name}, ${err.message}`);
            });
        }

    },

    pauseChrono: function() {
        if(app.isKaiOS) {
            alert('Pause');
        } else {
            app.isPaused = true;
            app.modalPause.classList.remove('hide');
            document.getElementById('closePause').addEventListener('click', () => {
                app.isPaused = false;
                app.modalPause.classList.add('hide');
                document.getElementById('closePause').removeEventListener('click', ()=> {});
            });
        }

    },

    lessValue: function(type) {
        if (typeof type === "object"){
            type = type.target.getAttribute('data-type');
        }
        switch(type) {
        case 'sets':
            app.setsValue.innerText = parseInt(app.setsValue.innerText) - 1;
            if (parseInt(app.setsValue.innerText) < 1) {
                app.setsValue.innerText = 1;
            }
            break;
        case 'work':
            if (parseInt(app.workValueInt) < 60) {
                app.workValueInt = parseInt(app.workValueInt) - 5;
            } else if (parseInt(app.workValueInt) < 120) {
                app.workValueInt = parseInt(app.workValueInt) - 10;
            } else if (parseInt(app.workValueInt) < 180) {
                app.workValueInt = parseInt(app.workValueInt) - 15;
            } else if (parseInt(app.workValueInt) <= 300) {
                app.workValueInt = parseInt(app.workValueInt) - 30;
            }
            if (parseInt(app.workValueInt) < 5) {
                app.workValueInt = 5;
            }
            app.workValue.innerText = app.formatTime(app.workValueInt);
            break;
        case 'rest':
            if (parseInt(app.restValueInt) < 60) {
                app.restValueInt = parseInt(app.restValueInt) - 5;
            } else if (parseInt(app.restValueInt) <= 120) {
                app.restValueInt = parseInt(app.restValueInt) - 10;
            }
            if (parseInt(app.restValueInt) < 5) {
                app.restValueInt = 3;
            }
            app.restValue.innerText = app.formatTime(app.restValueInt);
            break;
        }
        app.updateWorkTime();
    },

    lessValueCont: function(type) {
        if (typeof type === "object") {
            type = type.target.getAttribute('data-type');
        }
        app.intervalButton = setInterval( function() {
            switch(type) {
            case 'sets':
                app.setsValue.innerText = parseInt(app.setsValue.innerText) - 1;
                if (parseInt(app.setsValue.innerText) < 1) {
                    app.setsValue.innerText = 1;
                }
                break;
            case 'work':
                if (parseInt(app.workValueInt) < 60) {
                    app.workValueInt = parseInt(app.workValueInt) - 5;
                } else if (parseInt(app.workValueInt) < 120) {
                    app.workValueInt = parseInt(app.workValueInt) - 10;
                } else if (parseInt(app.workValueInt) < 180) {
                    app.workValueInt = parseInt(app.workValueInt) - 15;
                } else if (parseInt(app.workValueInt) <= 300) {
                    app.workValueInt = parseInt(app.workValueInt) - 30;
                }
                if (parseInt(app.workValueInt) < 5) {
                    app.workValueInt = 3;
                }
                app.workValue.innerText = app.formatTime(app.workValueInt);
                break;
            case 'rest':
                if (parseInt(app.restValueInt) < 60) {
                    app.restValueInt = parseInt(app.restValueInt) - 5;
                } else if (parseInt(app.restValueInt) <= 120) {
                    app.restValueInt = parseInt(app.restValueInt) - 10;
                }
                if (parseInt(app.restValueInt) < 5) {
                    app.restValueInt = 5;
                }
                app.restValue.innerText = app.formatTime(app.restValueInt);
                break;
            }
            app.updateWorkTime();
        }, 250);
    },

    contEnd: function() {
        for (var i = 0; i <= app.intervalButton; i ++) {
            clearInterval(i);
        }
    },

    moreValue: function(type) {
        if (typeof type === "object"){
            type = type.target.getAttribute('data-type');
        }
        switch(type) {
        case 'sets':
            if (parseInt(app.setsValue.innerText) < 30) {
                app.setsValue.innerText = parseInt(app.setsValue.innerText) + 1;
                if (parseInt(app.setsValue.innerText) < 1) {
                    app.setsValue.innerText = 1;
                }
            }
            break;
        case 'work':
            if (parseInt(app.workValueInt) < 300) {
                if (parseInt(app.workValueInt) < 60) {
                    app.workValueInt = parseInt(app.workValueInt) + 5;
                } else if (parseInt(app.workValueInt) < 120) {
                    app.workValueInt = parseInt(app.workValueInt) + 10;
                } else if (parseInt(app.workValueInt) < 180) {
                    app.workValueInt = parseInt(app.workValueInt) + 15;
                } else if (parseInt(app.workValueInt) < 300) {
                    app.workValueInt = parseInt(app.workValueInt) + 30;
                }
                if (parseInt(app.workValueInt) < 5) {
                    app.workValueInt = 5;
                }
                app.workValue.innerText = app.formatTime(app.workValueInt);
            }
            break;
        case 'rest':
            if (parseInt(app.restValueInt) < 120) {
                if (parseInt(app.restValueInt) < 60) {
                    if (parseInt(app.restValueInt) < 5) {
                        app.restValueInt = 5;
                    } else {
                        app.restValueInt = parseInt(app.restValueInt) + 5;
                    }
                } else if (parseInt(app.restValueInt) < 120) {
                    app.restValueInt = parseInt(app.restValueInt) + 10;
                }
                if (parseInt(app.restValueInt) < 5) {
                    app.restValueInt = 3;
                }
            }
            app.restValue.innerText = app.formatTime(app.restValueInt);
            break;
        }
        app.updateWorkTime();
    },

    moreValueCont: function(type) {
        if (typeof type === "object"){
            type = type.target.getAttribute('data-type');
        }
        app.intervalButton = setInterval( function() {
            switch(type) {
            case 'sets':
                if (parseInt(app.setsValue.innerText) < 30) {
                    app.setsValue.innerText = parseInt(app.setsValue.innerText) + 1;
                    if (parseInt(app.setsValue.innerText) < 1) {
                        app.setsValue.innerText = 1;
                    }
                }
                break;
            case 'work':
                if (parseInt(app.workValue.innerText) < 300) {
                    if (parseInt(app.workValueInt) < 60) {
                        app.workValueInt = parseInt(app.workValueInt) + 5;
                    } else if (parseInt(app.workValueInt) < 120) {
                        app.workValueInt = parseInt(app.workValueInt) + 10;
                    } else if (parseInt(app.workValueInt) < 180) {
                        app.workValueInt = parseInt(app.workValueInt) + 15;
                    } else if (parseInt(app.workValueInt) < 300) {
                        app.workValueInt = parseInt(app.workValueInt) + 30;
                    }
                    if (parseInt(app.workValueInt) < 5) {
                        app.workValueInt = 3;
                    }
                    app.workValue.innerText = app.formatTime(app.workValueInt);
                }
                break;
            case 'rest':
                if (parseInt(app.restValueInt) < 120) {
                    if (parseInt(app.restValueInt) < 60) {
                        if (parseInt(app.restValueInt) < 5) {
                            app.restValueInt = 5;
                        } else {
                            app.restValueInt = parseInt(app.restValueInt) + 5;
                        }
                    } else if (parseInt(app.restValueInt) < 120) {
                        app.restValueInt = parseInt(app.restValueInt) + 10;
                    }
                    if (parseInt(app.restValueInt) < 5) {
                        app.restValueInt = 5;
                    }
                }
                app.restValue.innerText = app.formatTime(app.restValueInt);
                break;
            }
            app.updateWorkTime();
        }, 250);
    },

    manejarTeclado: function(e) {

        if (e.key === "Enter") {
            switch(document.getElementsByClassName('nv-select')[0].id) {
            case 'play':
                app.initChrono();
                break;
            case 'reset':
                app.resetChrono();
                break;
            case 'pause':
                app.pauseChrono();
                break;
            case 'setsLess':
                app.lessValue('sets');
                break;
            case 'workLess':
                app.lessValue('work');
                break;
            case 'restLess':
                app.lessValue('rest');
                break;
            case 'setsMore':
                app.moreValue('sets');
                break;
            case 'workMore':
                app.moreValue('work');
                break;
            case 'restMore':
                app.moreValue('rest');
                break;
            }
        }
        if (e.key === "ArrowDown" || e.key === "ArrowRight") {
            e.preventDefault();
            app.changeFocus(1);
        }
        if (e.key === "ArrowUp" || e.key === "ArrowLeft") {
            e.preventDefault();
            app.changeFocus(-1);
        }

    },

    changeFocus: function(changePosicion) {
        let navigator = document.getElementsByClassName('navigatorConfig');
        if (app.state === 2) {
            navigator = document.getElementsByClassName('navigatorChrono');
        }
        navigator[app.positionNavigator].classList.remove('nv-select');
        app.positionNavigator += changePosicion;
        if(navigator[app.positionNavigator] === undefined) {
            app.positionNavigator = 0;
        }
        navigator[app.positionNavigator].classList.add('nv-select');
    },

    formatTime: function(time) {
        let seg = time%60;
        if (seg < 10) {
            seg = "0" + seg;
        }
        let min = parseInt(time/60);
        return min+":"+seg;
    }
};

In the end, I also needed to change a bit the html to make it about brushing the teeth.

<html lang="es"><head>
    <meta charset="UTF-8">
    <title>Brossage Dents</title>
    <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, maximum-scale=2, user-scalable=no">
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <meta name="theme-color" content="#CCC">
    <link rel="icon" href="img/icon.png">

    <style type="text/css">
      html{
      width: 100%;
      height: 100%;
      }

      html.rest,
      .jumbotron.rest {
      background: pink;
      }

      html.work,
      .jumbotron.work {
      background: cyan;
      }

      body {
      font-family: Helvetica;
      font-size: 16px;
      min-width: 240px;
      }

      header {
      background-color: #ccc;
      border-bottom: 1px solid #666;
      }

      header h3 {
      width: 200px;
      margin: 0 auto;
      font-size: 20px;
      font-weight: bold;
      padding: 5px 0;
      padding-left: 3%;
      padding-right: 3%;
      padding-bottom: 2px;
      padding-top: 4px;
      }

      header h3 img {
      margin-right: 15px;
      }

      .jumbotron {
      background-color: white;
      }

      .jumbotron.container {
      margin-right: 0px;
      margin-left: 0px;
      padding-left: 0px;
      padding-right: 5px;
      }

      .principal {
      margin-top: 5px;
      text-align: center;
      }


      .principal .title {
      display: inline-block;
      width: 50px;
      text-align: left;
      }

      .principal .value {
      width: 25px;
      height: 20px;
      font-size: 20px;
      margin-left: 10px;
      margin-right: 10px;
      }

      .principal a,
      .principal a:focus,
      .principal a:hover,
      .principal a.nv-select {
      height: 20px;
      font-size: 20px;
      background-color: #0000FF;
      color: #FFFFFF;
      border-radius: 10px;

      }


      .principal a.nv-select {
      border: 2px solid cyan;
      }

      .principal #setsValue {
      width: 48px;
      display: inline-block;
      }

      .chronoDiv span{
      width: 50px;
      height: 20px;
      font-size: 30px;
      margin-left: 10px;
      margin-right: 10px;
      }

      .chronoDiv span#timeRest {
      font-weight: bold;
      }

      .modalPause,
      .modalReset {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      position: fixed;
      width: 100%;
      height: 100%;
      background: grey;
      color: #FFF;
      font-size: 24px;
      opacity: 0.8;
      }
      .modalPause .close,
      .modalReset .closeReset,
      .modalReset .okReset {
      height: 20px;
      margin-top: 10px;
      font-size: 20px;
      background-color: #0000FF;
      color: #FFFFFF;
      padding: 10px;
      border-radius: 10px;
      opacity: 1;
      text-align: center;
      }

      .modalPause .pauseText,
      .modalReset .resetText {
      color: #000;
      opacity: 1;
      background: lightgrey;
      padding: 10px;
      border-radius: 10px;
      }

      .modalReset .buttonDiv {
      display: inline-flex;
      justify-content: space-between;
      min-width: 100px;
      }

      div.clear {
      height: 5px;
      }

      div.clearx5 {
      height: 25px;
      }

      .hide {
      display: none;
      }
    </style>

    <link rel="stylesheet" href="css/framework.min.css">

    <link rel="manifest" href="./manifest.json">

    <meta name="description" content="Brosser les dents">
    <meta name="keywords" content="interval timer, timer, tabata, chronometer, chrono">

    <meta name="author" content="Salvador Camacho">

  </head>

  <body>

    <header>
      <h3>
        <img src="img/icon.png" alt="icon" height="24">Brossage Dents
      </h3>
    </header>

    <div id="modalPause" class="hide modalPause">
      <div class="pauseText">Pause</div>
      <div id="closePause" class="close">X</div>
    </div>

    <div id="modalReset" class="hide modalReset">
      <div class="resetText">Are sure?</div>
      <div class="buttonDiv">
        <div id="okReset" class="okReset">Ok</div>
        <div id="closeReset" class="closeReset">X</div>
      </div>
    </div>

    <div class="container principal jumbotron" id="principalDiv">

      <div id="setDiv" class="setDiv">
        <div>
          <span class="title">Côtés</span>
          <a id="setsLess" class="lessBtn btn btn-sm navigatorConfig nv-select" data-type="sets" tabindex="0">-</a>
          <span id="setsValue" class="value">4</span>
          <a id="setsMore" class="moreBtn btn btn-sm navigatorConfig" data-type="sets" tabindex="0">+</a>
        </div>
        <div class="clear"></div>
        <div>
          <span class="title">Temps</span>
          <a id="workLess" class="lessBtn btn btn-sm navigatorConfig" data-type="work" tabindex="0">-</a>
          <span id="workValue" class="value">0:20</span>
          <a id="workMore" class="moreBtn btn btn-sm navigatorConfig" data-type="work" tabindex="0">+</a>
        </div>
        <div class="clear"></div>
        <div>
          <span class="title">Pause</span>
          <a id="restLess" class="lessBtn btn btn-sm navigatorConfig" data-type="rest" tabindex="0">-</a>
          <span id="restValue" class="value">0:10</span>
          <a id="restMore" class="moreBtn btn btn-sm navigatorConfig" data-type="rest" tabindex="0">+</a>
        </div>
        <div class="clear"></div>
        <a id="play" class="btn btn-b btn-sm navigatorConfig" tabindex="0">Go !</a>

        <div class="clearx5"></div>
        <div>
          <span class="titleWork">Total</span>
          <span id="workTime" class="value">1:50</span>
        </div>

      </div>

      <div id="chronoDiv" class="chronoDiv hide">
        <span id="cycle">1/10</span>
        <div class="clear"></div>
        <span id="timeRest">10</span>
        <div class="clear"></div>
        <span id="textChrono">Rest</span>
        <div class="clear"></div>
        <a id="pause" class="btn btn-a btn-sm navigatorChrono" tabindex="0">Pause</a>
        <a id="reset" class="btn btn-c btn-sm navigatorChrono" tabindex="0">Reset</a> <!-- pause and confirm -->
      </div>
    </div>

    <video id="noSleepVideo" width="320" height="240" autoplay="" muted="" playsinline="" loop="" class="hide">
      <source src="data:video/mp4;base64,AAAAIGZ0eXBtcDQyAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAACKBtZGF0AAAC8wYF///v3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0MiByMjQ3OSBkZDc5YTYxIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTEgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9MiBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIgYl9hZGFwdD0xIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MSBvcGVuX2dvcD0wIHdlaWdodHA9MSBrZXlpbnQ9MzAwIGtleWludF9taW49MzAgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD0xMCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIwLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IHZidl9tYXhyYXRlPTIwMDAwIHZidl9idWZzaXplPTI1MDAwIGNyZl9tYXg9MC4wIG5hbF9ocmQ9bm9uZSBmaWxsZXI9MCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAOWWIhAA3//p+C7v8tDDSTjf97w55i3SbRPO4ZY+hkjD5hbkAkL3zpJ6h/LR1CAABzgB1kqqzUorlhQAAAAxBmiQYhn/+qZYADLgAAAAJQZ5CQhX/AAj5IQAD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" type="video/mp4">
    </video>
    <script type="text/javascript" charset="utf-8" src="js/main.js"></script>
</body></html>

Same for the manifest

{
  "lang": "en",
  "name": "Brossage Dents",
  "short_name": "Brossage Dents",
  "icons": [{
        "src": "./img/icon-48x48.png",
        "sizes": "48x48",
        "type": "image/png"
      },{
        "src": "./img/icon-96x96.png",
        "sizes": "96x96",
        "type": "image/png"
      },{
        "src": "./img/icon-144x144.png",
        "sizes": "144x144",
        "type": "image/png"
      },{
        "src": "./img/icon-192x192.png",
        "sizes": "192x192",
        "type": "image/png"
      },{
        "src": "./img/icon-512x512.png",
        "sizes": "512x512",
        "type": "image/png"
      }],
  "start_url": ".",
  "display": "fullscreen",
  "orientation": "portrait",
  "theme_color": "#CCC",
  "background_color": "#DEDEDE"
}

doing again, with old python2 kivy, to reuse my wiko cink peax

Shows how to change fullscreen settings,

import os

from kivy.app import App
from kivy.clock import Clock
from kivy.graphics import Color, Rectangle
from kivy.properties import BooleanProperty, NumericProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.screenmanager import Screen, ScreenManager, ScreenManagerException
from kivy.core.audio import SoundLoader
from helpers.osc import oschandler, to_service
from helpers.wakelock import WakeLock
from android.runnable import run_on_ui_thread
from logging import getLogger

logger = getLogger(__name__)

pink = (1, 0.75, 0.8)
cyan = (0, 1, 1)
grey = (0.3, 0.3, 0.3)

class StateMachine(object):

    def __init__(self, manager, steps, step_time, rest_time):
        self.manager = manager
        self.steps = steps
        self.current_step = 0
        self.step_time = step_time
        self.rest_time = rest_time
        self.state = "init"
        self.timer = self.manager.get_screen('intervaltimer:timer')
        self.alert = SoundLoader.load(os.path.join(os.path.dirname(__file__), "alert.ogg"))
        self.end = SoundLoader.load(os.path.join(os.path.dirname(__file__), "end.ogg"))
        self.gong = SoundLoader.load(os.path.join(os.path.dirname(__file__), "gong.ogg"))
        self.gong2 = SoundLoader.load(os.path.join(os.path.dirname(__file__), "gong2.ogg"))

    def update(self):
        if self.state in ("init", "rest"):
            self.current_step += 1
            self.state = "step"
            what = "Brosse les dents !"
            color = cyan
            duration = self.step_time
            current_number = self.current_step
            total_number = self.steps
            sound_map = {None: self.alert, 2:self.end}
        elif self.state == "step":
            if self.current_step == self.steps:
                self.gong.play()
                self.timer.finish()
                return
            self.state = "rest"
            what = "Attends..."
            color = pink
            duration = self.rest_time
            current_number = self.current_step
            total_number = self.steps
            sound_map = {2: self.end, None:self.gong2}
        else:
            raise NotImplementedError()
        self.timer.start_timer(
            what=what,
            color=color,
            duration=duration,
            current_number=current_number,
            total_number=total_number,
            on_done=self.update,
            sound_map=sound_map,
        )

    def start(self):

        self.timer.start_timer(
            what="Attends...",
            color=pink,
            duration=5,
            current_number=0,
            total_number=0,
            on_done=self.update,
            sound_map={2: self.end}
        )


class SettingsScreen(Screen):
    steps = NumericProperty(4)
    step_time = NumericProperty(20)
    rest_time = NumericProperty(10)
    total_time = StringProperty("Total: 0s")

    def _update_rect(self, instance, value):
        self.rect.pos = instance.pos
        self.rect.size = instance.size

    def __init__(self, **kwargs):
        super(SettingsScreen, self).__init__(**kwargs)
        with self.canvas.before:
            self.color = Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size, pos=self.pos)

        self.bind(size=self._update_rect, pos=self._update_rect)

        layout = BoxLayout(orientation='vertical')

        # Step count setting
        step_layout = BoxLayout(size_hint=(1, 0.15))
        step_layout.add_widget(
            Button(text="-",
                   background_color=(0, 0, 1, 1),
                   color=(1, 1, 1, 1),
                   on_press=self.decrease_steps))
        self.steps_label = Label(text="", color=(0, 0, 0, 1))
        step_layout.add_widget(self.steps_label)
        step_layout.add_widget(
            Button(text="+",
                   background_color=(0, 0, 1, 1),
                   color=(1, 1, 1, 1),
                   on_press=self.increase_steps))
        layout.add_widget(step_layout)

        # Step time setting
        step_time_layout = BoxLayout(size_hint=(1, 0.15))
        step_time_layout.add_widget(
            Button(text="-",
                   background_color=(0, 0, 1, 1),
                   color=(1, 1, 1, 1),
                   on_press=self.decrease_step_time))
        self.step_time_label = Label(text="", color=(0, 0, 0, 1))
        step_time_layout.add_widget(self.step_time_label)
        step_time_layout.add_widget(
            Button(text="+",
                   background_color=(0, 0, 1, 1),
                   color=(1, 1, 1, 1),
                   on_press=self.increase_step_time))
        layout.add_widget(step_time_layout)

        # Rest time setting
        rest_time_layout = BoxLayout(size_hint=(1, 0.15))
        rest_time_layout.add_widget(
            Button(text="-",
                   background_color=(0, 0, 1, 1),
                   color=(1, 1, 1, 1),
                   on_press=self.decrease_rest_time))
        self.rest_time_label = Label(text="",
                  color=(0, 0, 0, 1))
        rest_time_layout.add_widget(self.rest_time_label)
        rest_time_layout.add_widget(
            Button(text="+",
                   background_color=(0, 0, 1, 1),
                   color=(1, 1, 1, 1),
                   on_press=self.increase_rest_time))
        layout.add_widget(rest_time_layout)

        # Go button
        go_button = Button(text="Go !!!",
                           background_color=(0, 0, 1, 1),
                           color=(1, 1, 1, 1),
                           size_hint=(1, 0.15))
        go_button.bind(on_press=self.start_timer)
        layout.add_widget(go_button)

        # Total time display
        total_time_label = Label(text=self.total_time,
                                 color=(0, 0, 0, 1),
                                 size_hint=(1, 0.15))
        layout.add_widget(total_time_label)
        self.total_time_label = total_time_label

        self.add_widget(layout)
        self.update_display()

        @oschandler("intervaltimer:start")
        def _(config):
            import json
            config = json.loads(config)

            self.step_time = config["step_time"]
            self.update_display()
            self.start_timer()

        self.bind(on_pre_enter=self.pre_enter_handler)

    def pre_enter_handler(self, *_):
        to_service("intervaltimer:settings")

    def update_display(self):
        total = (self.step_time + self.rest_time) * self.steps - self.rest_time
        self.steps_label.text = "Nombre: " + str(self.steps)
        self.step_time_label.text = "Temps: " + str(self.step_time) + "s"
        self.rest_time_label.text = "Pause: " + str(self.rest_time) + "s"
        self.total_time_label.text = "Total Time: {}s".format(total)

    def increase_steps(self, instance):
        self.steps += 1
        self.update_display()

    def decrease_steps(self, instance):
        if self.steps > 1:
            self.steps -= 1
        self.update_display()

    def increase_step_time(self, instance):
        self.step_time += 10
        self.update_display()

    def decrease_step_time(self, instance):
        if self.step_time > 10:
            self.step_time -= 10
        self.update_display()

    def increase_rest_time(self, instance):
        self.rest_time += 5
        self.update_display()

    def decrease_rest_time(self, instance):
        if self.rest_time > 10:
            self.rest_time -= 10
        self.update_display()

    def start_timer(self, *instance):
        app = App.get_running_app()
        app.root.state_machine = StateMachine(
            self.manager,
            self.steps,
            self.step_time,
            self.rest_time,
        )
        app.root.state_machine.start()

class TimerScreen(Screen):
    remaining_time = NumericProperty(0)
    current_number = NumericProperty(1)
    duration = NumericProperty(0)
    timer_running = BooleanProperty(False)
    what = StringProperty("")
    total_number = NumericProperty(0)

    def _update_rect(self, instance, value):
        self.rect.pos = instance.pos
        self.rect.size = instance.size

    def __init__(self, **kwargs):
        super(TimerScreen, self).__init__(**kwargs)

        with self.canvas.before:
            self.color = Color(0, 1, 1, 1)
            self.rect = Rectangle(size=self.size, pos=self.pos)

        self.bind(size=self._update_rect, pos=self._update_rect)

        layout = GridLayout(cols=1)
        self.label = Label(text="", font_size='40sp', color=(0, 0, 0, 1))
        layout.add_widget(self.label)

        self.time_label = Label(text="", font_size='80sp', color=(0, 0, 0, 1))
        layout.add_widget(self.time_label)

        self.what_label = Label(text="", font_size='40sp', color=(0, 0, 0, 1))
        layout.add_widget(self.what_label)

        button_layout = BoxLayout(size_hint=(1, 0.7))
        self.pause_button = Button(text="Pause",
                              background_color=(0, 0, 1, 1),
                              color=(1, 1, 1, 1))
        self.pause_button.bind(on_press=self.toggle_clock)
        button_layout.add_widget(self.pause_button)

        layout.add_widget(button_layout)
        self.add_widget(layout)

        self.bind(on_pre_enter=self.pre_enter_handler)
        self.bind(on_pre_leave=self.pre_leave_handler)

        with WakeLock("intervaltimer", wakeup=True):
            self.keep_screen_on()

    def keep_screen_on(self, revert=False):
        from android import mActivity
        from jnius import autoclass
        from android.runnable import run_on_ui_thread
        LayoutParams = autoclass('android.view.WindowManager$LayoutParams')

        @run_on_ui_thread
        def do():
            if revert:
                mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON)
            else:
                mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON)

        do()

    def pre_leave_handler(self, *args):
        self.stop_clock()
        self.keep_screen_on(revert=True)

        from jnius import autoclass
        from android import mActivity

        View = autoclass('android.view.View')

        option = View.SYSTEM_UI_FLAG_VISIBLE

        @run_on_ui_thread
        def fs():
            logger.debug("Leaving fullscreen mode")
            mActivity.getWindow().getDecorView().setSystemUiVisibility(option)

        fs()

    def pre_enter_handler(self, *args):
        self.keep_screen_on()

        from jnius import autoclass
        from android import mActivity

        View = autoclass('android.view.View')

        option = View.SYSTEM_UI_FLAG_FULLSCREEN

        @run_on_ui_thread
        def fs():
            logger.debug("Entering fullscreen mode")
            mActivity.getWindow().getDecorView().setSystemUiVisibility(option)

        fs()

    def start_timer(
        self,
        what,
        color,
        duration,
        current_number,
        total_number,
        on_done,
        sound_map,
    ):
        self.wanted_color = color
        self.color.rgb = self.wanted_color
        App.get_running_app().goto("intervaltimer:timer")
        self.duration = duration
        self.what = what
        self.current_number = current_number
        self.remaining_time = duration
        self.total_number = total_number
        self.on_done = on_done
        self.finished = False
        self.sound_map = sound_map
        self.sound = self.sound_map.get(None)
        if self.sound:
            self.sound.play()
        self.start_clock()

    def update_display(self):
        self.what_label.text = self.what

        if self.what == "Attends..." and self.current_number > 0:
            self.label.text = "{} -> {}/{}".format(
                self.current_number,
                self.current_number+1,
                self.total_number,
            )
        elif self.current_number:
            self.label.text = "{}/{}".format(
                self.current_number,
                self.total_number,
            )
        else:
            self.label.text = ""

        if self.clock:
            self.color.rgb = self.wanted_color
            self.pause_button.text = "Pause"
        else:
            if self.finished:
                self.what_label.text = "C'est fini !!!"
            else:
                self.color.rgb = grey
                self.pause_button.text = "Continue"
        self.time_label.text = str(self.remaining_time)

    def countdown(self, dt):
        if self.remaining_time > 0:
            self.remaining_time -= 1
        else:
            self.stop_clock()
            self.on_done()
        self.update_display()
        new_sound = self.sound_map.get(self.remaining_time)
        if new_sound:
            if self.sound:
                self.sound.stop()
            self.sound = new_sound
            self.sound.play()

    def start_clock(self):
        self.clock = Clock.schedule_interval(self.countdown, 1)
        self.update_display()

    def stop_clock(self):
        Clock.unschedule(self.clock)
        self.clock = None
        self.update_display()

    def toggle_clock(self, instance=None):
        if self.clock:
            self.stop_clock()
        else:
            self.start_clock()

    def finish(self):
        self.finished = True
        Clock.schedule_once(self.leave, 2)

    def leave(self, instance=None):
        App.get_running_app().back()


class IntervalTimerApp(App):

    def goto(self, screen):
        self.manager.current = screen

    def back(self):
        self.manager.current = 'intervaltimer:settings'

    @staticmethod
    def populate(sm):
        try:
            sm.get_screen('intervaltimer:settings')
            return
        except ScreenManagerException:
            pass # not populated yet

        sm.add_widget(SettingsScreen(name='intervaltimer:settings'))
        sm.add_widget(TimerScreen(name='intervaltimer:timer'))

    def build(self):
        sm = ScreenManager()
        self.populate(sm)
        return sm


def run():
    IntervalTimerApp().run()

Notes linking here