VulpesOS/gaia/apps/clock/js/alarm.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

256 lines
7.6 KiB
JavaScript
Executable file

define(function(require, exports, module) {
'use strict';
/**
* Alarm represents one alarm instance. It tracks any mozAlarms it
* has registered, its IndexedDB ID, and any other properties
* relating to the alarm's schedule and firing options.
*/
function Alarm(opts) {
opts = opts || {};
var now = new Date();
var defaults = {
id: null,
registeredAlarms: {}, // keys: ('normal' or 'snooze') => mozAlarmID
// Map of weekdays { "0": true, "1": false, ... }
// "0" is for Sunday in Gregorian calendar
repeat: {},
hour: now.getHours(),
minute: now.getMinutes(),
label: '',
sound: 'ac_awake.opus',
vibrate: true,
snooze: 10 // Number of minutes to snooze
};
for (var key in defaults) {
this[key] = (key in opts ? opts[key] : defaults[key]);
}
}
Alarm.prototype = {
toJSON: function() {
return {
id: this.id,
registeredAlarms: this.registeredAlarms,
repeat: this.repeat,
hour: this.hour,
minute: this.minute,
label: this.label,
sound: this.sound,
vibrate: this.vibrate,
snooze: this.snooze
};
},
/**
* An alarm is enabled if and only if it has a registeredAlarm set
* with a type of 'normal'. To disable an alarm, any
* registeredAlarms are unregistered with mozAlarms and removed
* from this.registeredAlarms.
*/
isEnabled: function() {
for (var i in this.registeredAlarms) {
// Both 'normal' and 'snooze' registered alarms should be
// treated as enabled, because the alarm will imminently fire.
if (i === 'normal' || i === 'snooze') {
return true;
}
}
return false;
},
isRepeating: function() {
for (var key in this.repeat) {
if (this.repeat[key]) {
return true;
}
}
return false;
},
getNextAlarmFireTime: function(relativeTo) {
var now = relativeTo || new Date();
var nextFire = new Date(now.getTime());
nextFire.setHours(this.hour, this.minute, 0, 0);
while (nextFire <= now ||
(this.isRepeating() &&
!this.repeat[nextFire.getDay().toString()])) {
nextFire.setDate(nextFire.getDate() + 1);
}
return nextFire;
},
getNextSnoozeFireTime: function(relativeTo) {
var now = relativeTo || new Date();
return new Date(now.getTime() + this.snooze * 60 * 1000);
},
/**
* Schedule an alarm to ring in the future.
*
* @return {Promise}
* @param {'normal'|'snooze'} type
*/
schedule: function(type) {
var alarmDatabase = require('alarm_database'); // circular dependency
var firedate, promise;
if (type === 'normal') {
promise = this.cancel(); // Cancel both snooze and regular mozAlarms.
firedate = this.getNextAlarmFireTime();
} else if (type === 'snooze') {
promise = this.cancel('snooze'); // Cancel any snooze mozAlarms.
firedate = this.getNextSnoozeFireTime();
} else {
return Promise.reject('Invalid type for Alarm.schedule().');
}
// Save the alarm to the database first. This ensures we have a
// valid ID, and that we've saved any modified properties before
// attempting to schedule the alarm.
return promise.then(() => alarmDatabase.put(this)).then(() => {
return new Promise((resolve, reject) => {
// Then, schedule the alarm.
var req = navigator.mozAlarms.add(firedate, 'ignoreTimezone',
{ id: this.id, type: type });
req.onerror = reject;
req.onsuccess = (evt) => {
this.registeredAlarms[type] = evt.target.result;
resolve();
};
});
// After scheduling the alarm, this.registeredAlarms has
// changed, so we must save that too.
}).then(() => alarmDatabase.put(this))
.then(() => {
this._notifyChanged();
}).catch((e) => {
console.log('Alarm scheduling error: ' + e.toString());
throw e;
});
},
/**
* Cancel an alarm. If `type` is provided, cancel only that type
* ('normal' or 'snooze'). Returns a Promise.
*/
cancel: function(/* optional */ type) {
var types = (type ? [type] : Object.keys(this.registeredAlarms));
var alarmDatabase = require('alarm_database'); // circular dependency
types.forEach((type) => {
var id = this.registeredAlarms[type];
navigator.mozAlarms.remove(id);
delete this.registeredAlarms[type];
});
return alarmDatabase.put(this).then(() => {
this._notifyChanged();
}).catch((e) => {
console.log('Alarm cancel error: ' + e.toString());
throw e;
});
},
_notifyChanged: function(removed) {
// Only update the application if this alarm was actually saved
// (i.e. it has an ID).
if (this.id) {
var alarmDatabase = require('alarm_database');
// Updating Alarm info from the DataStore
alarmDatabase.getAll()
.then((alarms) => {
var storeAlarms = [];
alarms.forEach(function(a) {
// Grabbing alarms that are 'normal'
if(a.registeredAlarms.normal) {
storeAlarms.push(a.getNextAlarmFireTime());
}
});
return this.getNextAlarm(storeAlarms);
}).then((nextAlarm) => {
navigator.getDataStores('alarms')
.then((stores) => {
return [stores[0].getLength(), stores[0]];
}).then(([len, store]) => {
if(0 === len) {
return store.add(nextAlarm);
} else {
return store.put(nextAlarm, 1);
}
}).then((id) => {
if(id) {
console.log('[Clock] ==== Updated Alarm on LockScreen ====');
}
});
});
window.dispatchEvent(
new CustomEvent(removed ? 'alarm-removed' : 'alarm-changed', {
detail: { alarm: this }
})
);
}
},
/**
* Delete an alarm completely from the database, canceling any
* pending scheduled mozAlarms.
*/
delete: function() {
var alarmDatabase = require('alarm_database'); // circular dependency
return this.cancel().then(() => {
return alarmDatabase.delete(this.id).then(() => {
this._notifyChanged(/* removed = */ true);
});
});
},
/**
* Returns the next scheduled alarm within the next 24 hours
* @param {Array} type 'Date'
* @returns {Object} This is either an empty obj or hour and minute
*/
getNextAlarm: function(storeAlarms) {
var alarmIndex = [],
nextAlarm = {};
// remove alarms that have more than 24 hrs
storeAlarms.forEach( function(val,index) {
if((val.getTime() - new Date().getTime()) / 36e5 > 24 ) {
alarmIndex.push(index);
}
});
// remove last index first
alarmIndex.sort(function(a,b) {
return b - a;
});
alarmIndex.forEach( function(val) {
storeAlarms.splice(val, 1);
});
// sort alarms
storeAlarms.sort(function(a,b){
return new Date(a.getTime()) - new Date(b.getTime());
});
if(storeAlarms.length > 0) {
var mins = storeAlarms[0].getMinutes();
nextAlarm = {
hour : '' + storeAlarms[0].getHours(),
minute : mins < 10 ? '0' + mins : '' + mins
};
}
return nextAlarm;
}
};
module.exports = Alarm;
});