Interval Timer
Evergreen- External reference: https://salvacam.js.org/intervalTimer/
- External reference: https://konubinix.eu/ipfs/bafybeiapftob6e7j7pjv72auxamh522sivapb6fq2tx3yjuvmsmeertr5i
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/AAj5IQADQGgcIQADQGgcAAAACQGeYUQn/wALKCEAA0BoHAAAAAkBnmNEJ/8ACykhAANAaBwhAANAaBwAAAANQZpoNExDP/6plgAMuSEAA0BoHAAAAAtBnoZFESwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBnqVEJ/8ACykhAANAaBwAAAAJAZ6nRCf/AAsoIQADQGgcIQADQGgcAAAADUGarDRMQz/+qZYADLghAANAaBwAAAALQZ7KRRUsK/8ACPkhAANAaBwAAAAJAZ7pRCf/AAsoIQADQGgcIQADQGgcAAAACQGe60Qn/wALKCEAA0BoHAAAAA1BmvA0TEM//qmWAAy5IQADQGgcIQADQGgcAAAAC0GfDkUVLCv/AAj5IQADQGgcAAAACQGfLUQn/wALKSEAA0BoHCEAA0BoHAAAAAkBny9EJ/8ACyghAANAaBwAAAANQZs0NExDP/6plgAMuCEAA0BoHAAAAAtBn1JFFSwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBn3FEJ/8ACyghAANAaBwAAAAJAZ9zRCf/AAsoIQADQGgcIQADQGgcAAAADUGbeDRMQz/+qZYADLkhAANAaBwAAAALQZ+WRRUsK/8ACPghAANAaBwhAANAaBwAAAAJAZ+1RCf/AAspIQADQGgcAAAACQGft0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bm7w0TEM//qmWAAy4IQADQGgcAAAAC0Gf2kUVLCv/AAj5IQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHAAAAAkBn/tEJ/8ACykhAANAaBwAAAANQZvgNExDP/6plgAMuSEAA0BoHCEAA0BoHAAAAAtBnh5FFSwr/wAI+CEAA0BoHAAAAAkBnj1EJ/8ACyghAANAaBwhAANAaBwAAAAJAZ4/RCf/AAspIQADQGgcAAAADUGaJDRMQz/+qZYADLghAANAaBwAAAALQZ5CRRUsK/8ACPkhAANAaBwhAANAaBwAAAAJAZ5hRCf/AAsoIQADQGgcAAAACQGeY0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bmmg0TEM//qmWAAy5IQADQGgcAAAAC0GehkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGepUQn/wALKSEAA0BoHAAAAAkBnqdEJ/8ACyghAANAaBwAAAANQZqsNExDP/6plgAMuCEAA0BoHCEAA0BoHAAAAAtBnspFFSwr/wAI+SEAA0BoHAAAAAkBnulEJ/8ACyghAANAaBwhAANAaBwAAAAJAZ7rRCf/AAsoIQADQGgcAAAADUGa8DRMQz/+qZYADLkhAANAaBwhAANAaBwAAAALQZ8ORRUsK/8ACPkhAANAaBwAAAAJAZ8tRCf/AAspIQADQGgcIQADQGgcAAAACQGfL0Qn/wALKCEAA0BoHAAAAA1BmzQ0TEM//qmWAAy4IQADQGgcAAAAC0GfUkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGfcUQn/wALKCEAA0BoHAAAAAkBn3NEJ/8ACyghAANAaBwhAANAaBwAAAANQZt4NExC//6plgAMuSEAA0BoHAAAAAtBn5ZFFSwr/wAI+CEAA0BoHCEAA0BoHAAAAAkBn7VEJ/8ACykhAANAaBwAAAAJAZ+3RCf/AAspIQADQGgcAAAADUGbuzRMQn/+nhAAYsAhAANAaBwhAANAaBwAAAAJQZ/aQhP/AAspIQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHAAACiFtb292AAAAbG12aGQAAAAA1YCCX9WAgl8AAAPoAAAH/AABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAGGlvZHMAAAAAEICAgAcAT////v7/AAAF+XRyYWsAAABcdGtoZAAAAAPVgIJf1YCCXwAAAAEAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAygAAAMoAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAB9AAABdwAAEAAAAABXFtZGlhAAAAIG1kaGQAAAAA1YCCX9WAgl8AAV+QAAK/IFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAUcbWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAAE3HN0YmwAAACYc3RzZAAAAAAAAAABAAAAiGF2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAygDKAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAyYXZjQwFNQCj/4QAbZ01AKOyho3ySTUBAQFAAAAMAEAAr8gDxgxlgAQAEaO+G8gAAABhzdHRzAAAAAAAAAAEAAAA8AAALuAAAABRzdHNzAAAAAAAAAAEAAAABAAAB8GN0dHMAAAAAAAAAPAAAAAEAABdwAAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAAC7gAAAAAQAAF3AAAAABAAAAAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAEEc3RzegAAAAAAAAAAAAAAPAAAAzQAAAAQAAAADQAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAANAAAADQAAAQBzdGNvAAAAAAAAADwAAAAwAAADZAAAA3QAAAONAAADoAAAA7kAAAPQAAAD6wAAA/4AAAQXAAAELgAABEMAAARcAAAEbwAABIwAAAShAAAEugAABM0AAATkAAAE/wAABRIAAAUrAAAFQgAABV0AAAVwAAAFiQAABaAAAAW1AAAFzgAABeEAAAX+AAAGEwAABiwAAAY/AAAGVgAABnEAAAaEAAAGnQAABrQAAAbPAAAG4gAABvUAAAcSAAAHJwAAB0AAAAdTAAAHcAAAB4UAAAeeAAAHsQAAB8gAAAfjAAAH9gAACA8AAAgmAAAIQQAACFQAAAhnAAAIhAAACJcAAAMsdHJhawAAAFx0a2hkAAAAA9WAgl/VgIJfAAAAAgAAAAAAAAf8AAAAAAAAAAAAAAABAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAACsm1kaWEAAAAgbWRoZAAAAADVgIJf1YCCXwAArEQAAWAAVcQAAAAAACdoZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU3RlcmVvAAAAAmNtaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAidzdGJsAAAAZ3N0c2QAAAAAAAAAAQAAAFdtcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAArEQAAAAAADNlc2RzAAAAAAOAgIAiAAIABICAgBRAFQAAAAADDUAAAAAABYCAgAISEAaAgIABAgAAABhzdHRzAAAAAAAAAAEAAABYAAAEAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAAGAAAAWAAAAXBzdGNvAAAAAAAAAFgAAAOBAAADhwAAA5oAAAOtAAADswAAA8oAAAPfAAAD5QAAA/gAAAQLAAAEEQAABCgAAAQ9AAAEUAAABFYAAARpAAAEgAAABIYAAASbAAAErgAABLQAAATHAAAE3gAABPMAAAT5AAAFDAAABR8AAAUlAAAFPAAABVEAAAVXAAAFagAABX0AAAWDAAAFmgAABa8AAAXCAAAFyAAABdsAAAXyAAAF+AAABg0AAAYgAAAGJgAABjkAAAZQAAAGZQAABmsAAAZ+AAAGkQAABpcAAAauAAAGwwAABskAAAbcAAAG7wAABwYAAAcMAAAHIQAABzQAAAc6AAAHTQAAB2QAAAdqAAAHfwAAB5IAAAeYAAAHqwAAB8IAAAfXAAAH3QAAB/AAAAgDAAAICQAACCAAAAg1AAAIOwAACE4AAAhhAAAIeAAACH4AAAiRAAAIpAAACKoAAAiwAAAItgAACLwAAAjCAAAAFnVkdGEAAAAObmFtZVN0ZXJlbwAAAHB1ZHRhAAAAaG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAAO2lsc3QAAAAzqXRvbwAAACtkYXRhAAAAAQAAAABIYW5kQnJha2UgMC4xMC4yIDIwMTUwNjExMDA=" 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
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
pink = (1, 0.75, 0.8)
cyan = (0, 1, 1)
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('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):
print("update")
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()
print("going to step " + what)
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):
print("starting")
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()
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))
pause_button = Button(text="Pause",
background_color=(0, 0, 1, 1),
color=(1, 1, 1, 1))
pause_button.bind(on_press=self.toggle_clock)
button_layout.add_widget(pause_button)
reset_button = Button(text="Reset",
background_color=(0, 0, 1, 1),
color=(1, 1, 1, 1))
reset_button.bind(on_press=self.leave)
button_layout.add_widget(reset_button)
layout.add_widget(button_layout)
self.add_widget(layout)
self.bind(on_pre_leave=self.pre_leave_handler)
def pre_leave_handler(self, *args):
"make sure the screen won't work in the background"
self.stop_clock()
def start_timer(
self,
what,
color,
duration,
current_number,
total_number,
on_done,
sound_map,
):
self.color.rgb = color
App.get_running_app().goto("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.current_number:
self.label.text = "{}/{}".format(
self.current_number,
self.total_number,
)
else:
self.label.text = ""
if not self.clock:
if self.finished:
self.what_label.text = "C'est fini !!!"
else:
self.what_label.text = "(Paused) " + self.what_label.text
self.time_label.text = str(self.remaining_time)
def countdown(self, dt):
print("tick " + str(self.remaining_time))
if self.remaining_time > 0:
self.remaining_time -= 1
else:
print("stopping and goto next")
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):
print("start clock")
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 = 'settings'
@staticmethod
def populate(sm):
try:
sm.get_screen('settings')
return
except ScreenManagerException:
pass # not populated yet
sm.add_widget(SettingsScreen(name='settings'))
sm.add_widget(TimerScreen(name='timer'))
def build(self):
sm = ScreenManager()
self.populate(sm)
return sm
def run():
IntervalTimerApp().run()