mirror of https://github.com/espruino/BangleApps
[datetime_picker] create app and use it in alarm app
parent
01d84a5334
commit
2d256f23c5
|
@ -50,3 +50,4 @@
|
||||||
0.45: Fix new alarm when selectedAlarm is undefined
|
0.45: Fix new alarm when selectedAlarm is undefined
|
||||||
0.46: Show alarm groups if the Show Group setting is ON. Scroll alarms menu back to previous position when getting back to it.
|
0.46: Show alarm groups if the Show Group setting is ON. Scroll alarms menu back to previous position when getting back to it.
|
||||||
0.47: Fix wrap around when snoozed through midnight
|
0.47: Fix wrap around when snoozed through midnight
|
||||||
|
0.48: Use datetimeinput for Events, if available. Scroll back when getting out of group. Menu date format setting for shorter dates on current year.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
This app allows you to add/modify any alarms, timers and events.
|
This app allows you to add/modify any alarms, timers and events.
|
||||||
|
|
||||||
Optional: When a keyboard app is detected, you can add a message to display when any of these is triggered.
|
Optional: When a keyboard app is detected, you can add a message to display when any of these is triggered. If a datetime input app (e.g. datetime_picker) is detected, it will be used for the selection of the date+time of events.
|
||||||
|
|
||||||
It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps.
|
It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps.
|
||||||
|
|
||||||
|
|
|
@ -50,13 +50,17 @@ function handleFirstDayOfWeek(dow) {
|
||||||
alarms.filter(e => e.timer === undefined).forEach(a => a.dow = handleFirstDayOfWeek(a.dow));
|
alarms.filter(e => e.timer === undefined).forEach(a => a.dow = handleFirstDayOfWeek(a.dow));
|
||||||
|
|
||||||
function getLabel(e) {
|
function getLabel(e) {
|
||||||
const dateStr = e.date && require("locale").date(new Date(e.date), 1);
|
const dateStr = getDateText(e.date);
|
||||||
return (e.timer
|
return (e.timer
|
||||||
? require("time_utils").formatDuration(e.timer)
|
? require("time_utils").formatDuration(e.timer)
|
||||||
: (dateStr ? `${dateStr}${e.rp?"*":""} ${require("time_utils").formatTime(e.t)}` : require("time_utils").formatTime(e.t) + (e.rp ? ` ${decodeRepeat(e)}` : ""))
|
: (dateStr ? `${dateStr}${e.rp?"*":""} ${require("time_utils").formatTime(e.t)}` : require("time_utils").formatTime(e.t) + (e.rp ? ` ${decodeRepeat(e)}` : ""))
|
||||||
) + (e.msg ? ` ${e.msg}` : "");
|
) + (e.msg ? ` ${e.msg}` : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDateText(d) {
|
||||||
|
return d && (settings.menuDateFormat === "mmdd" ? d.substring(d.startsWith(new Date().getFullYear()) ? 5 : 0) : require("locale").date(new Date(d), 1));
|
||||||
|
}
|
||||||
|
|
||||||
function trimLabel(label, maxLength) {
|
function trimLabel(label, maxLength) {
|
||||||
if(settings.showOverflow) return label;
|
if(settings.showOverflow) return label;
|
||||||
return (label.length > maxLength
|
return (label.length > maxLength
|
||||||
|
@ -75,10 +79,10 @@ function formatAlarmProperty(msg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMainMenu(scroll, group) {
|
function showMainMenu(scroll, group, scrollback) {
|
||||||
const menu = {
|
const menu = {
|
||||||
"": { "title": group || /*LANG*/"Alarms & Timers", scroll: scroll },
|
"": { "title": group || /*LANG*/"Alarms & Timers", scroll: scroll },
|
||||||
"< Back": () => group ? showMainMenu() : load(),
|
"< Back": () => group ? showMainMenu(scrollback) : load(),
|
||||||
/*LANG*/"New...": () => showNewMenu(group)
|
/*LANG*/"New...": () => showNewMenu(group)
|
||||||
};
|
};
|
||||||
const getGroups = settings.showGroup && !group;
|
const getGroups = settings.showGroup && !group;
|
||||||
|
@ -98,7 +102,7 @@ function showMainMenu(scroll, group) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!group) {
|
if (!group) {
|
||||||
Object.keys(groups).sort().forEach(g => menu[g] = () => showMainMenu(null, g));
|
Object.keys(groups).sort().forEach(g => menu[g] = () => showMainMenu(null, g, scroller.scroll));
|
||||||
menu[/*LANG*/"Advanced"] = () => showAdvancedMenu();
|
menu[/*LANG*/"Advanced"] = () => showAdvancedMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +123,7 @@ function showNewMenu(group) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate, scroll, group) {
|
function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate, scroll, group) {
|
||||||
|
console.log(scroll);
|
||||||
var isNew = alarmIndex === undefined;
|
var isNew = alarmIndex === undefined;
|
||||||
|
|
||||||
var alarm = require("sched").newDefaultAlarm();
|
var alarm = require("sched").newDefaultAlarm();
|
||||||
|
@ -138,6 +143,8 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate, scroll, group) {
|
||||||
var title = date ? (isNew ? /*LANG*/"New Event" : /*LANG*/"Edit Event") : (isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm");
|
var title = date ? (isNew ? /*LANG*/"New Event" : /*LANG*/"Edit Event") : (isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm");
|
||||||
var keyboard = "textinput";
|
var keyboard = "textinput";
|
||||||
try {keyboard = require(keyboard);} catch(e) {keyboard = null;}
|
try {keyboard = require(keyboard);} catch(e) {keyboard = null;}
|
||||||
|
var datetimeinput;
|
||||||
|
try {datetimeinput = require("datetimeinput");} catch(e) {datetimeinput = null;}
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
"": { "title": title },
|
"": { "title": title },
|
||||||
|
@ -145,41 +152,66 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate, scroll, group) {
|
||||||
prepareAlarmForSave(alarm, alarmIndex, time, date);
|
prepareAlarmForSave(alarm, alarmIndex, time, date);
|
||||||
saveAndReload();
|
saveAndReload();
|
||||||
showMainMenu(scroll, group);
|
showMainMenu(scroll, group);
|
||||||
},
|
}
|
||||||
/*LANG*/"Hour": {
|
};
|
||||||
value: time.h,
|
|
||||||
format: v => ("0" + v).substr(-2),
|
if (alarm.date && datetimeinput) {
|
||||||
min: 0,
|
menu[`${getDateText(date.toLocalISOString().slice(0,10))} ${require("time_utils").formatTime(time)}`] = {
|
||||||
max: 23,
|
value: date,
|
||||||
wrap: true,
|
format: v => "",
|
||||||
onchange: v => time.h = v
|
onchange: v => {
|
||||||
},
|
setTimeout(() => {
|
||||||
/*LANG*/"Minute": {
|
var datetime = new Date(v.getTime());
|
||||||
value: time.m,
|
datetime.setHours(time.h, time.m);
|
||||||
format: v => ("0" + v).substr(-2),
|
datetimeinput.input({datetime}).then(result => {
|
||||||
min: 0,
|
time.h = result.getHours();
|
||||||
max: 59,
|
time.m = result.getMinutes();
|
||||||
wrap: true,
|
prepareAlarmForSave(alarm, alarmIndex, time, result, true);
|
||||||
onchange: v => time.m = v
|
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate, scroll, group);
|
||||||
},
|
});
|
||||||
/*LANG*/"Day": {
|
}, 100);
|
||||||
value: date ? date.getDate() : null,
|
}
|
||||||
min: 1,
|
};
|
||||||
max: 31,
|
} else {
|
||||||
wrap: true,
|
Object.assign(menu, {
|
||||||
onchange: v => date.setDate(v)
|
/*LANG*/"Hour": {
|
||||||
},
|
value: time.h,
|
||||||
/*LANG*/"Month": {
|
format: v => ("0" + v).substr(-2),
|
||||||
value: date ? date.getMonth() + 1 : null,
|
min: 0,
|
||||||
format: v => require("date_utils").month(v),
|
max: 23,
|
||||||
onchange: v => date.setMonth((v+11)%12)
|
wrap: true,
|
||||||
},
|
onchange: v => time.h = v
|
||||||
/*LANG*/"Year": {
|
},
|
||||||
value: date ? date.getFullYear() : null,
|
/*LANG*/"Minute": {
|
||||||
min: new Date().getFullYear(),
|
value: time.m,
|
||||||
max: 2100,
|
format: v => ("0" + v).substr(-2),
|
||||||
onchange: v => date.setFullYear(v)
|
min: 0,
|
||||||
},
|
max: 59,
|
||||||
|
wrap: true,
|
||||||
|
onchange: v => time.m = v
|
||||||
|
},
|
||||||
|
/*LANG*/"Day": {
|
||||||
|
value: date ? date.getDate() : null,
|
||||||
|
min: 1,
|
||||||
|
max: 31,
|
||||||
|
wrap: true,
|
||||||
|
onchange: v => date.setDate(v)
|
||||||
|
},
|
||||||
|
/*LANG*/"Month": {
|
||||||
|
value: date ? date.getMonth() + 1 : null,
|
||||||
|
format: v => require("date_utils").month(v),
|
||||||
|
onchange: v => date.setMonth((v+11)%12)
|
||||||
|
},
|
||||||
|
/*LANG*/"Year": {
|
||||||
|
value: date ? date.getFullYear() : null,
|
||||||
|
min: new Date().getFullYear(),
|
||||||
|
max: 2100,
|
||||||
|
onchange: v => date.setFullYear(v)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(menu, {
|
||||||
/*LANG*/"Message": {
|
/*LANG*/"Message": {
|
||||||
value: alarm.msg,
|
value: alarm.msg,
|
||||||
format: formatAlarmProperty,
|
format: formatAlarmProperty,
|
||||||
|
@ -241,7 +273,7 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate, scroll, group) {
|
||||||
saveAndReload();
|
saveAndReload();
|
||||||
showMainMenu(scroll, group);
|
showMainMenu(scroll, group);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
if (!keyboard) delete menu[/*LANG*/"Message"];
|
if (!keyboard) delete menu[/*LANG*/"Message"];
|
||||||
if (!keyboard || !settings.showGroup) delete menu[/*LANG*/"Group"];
|
if (!keyboard || !settings.showGroup) delete menu[/*LANG*/"Group"];
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "alarm",
|
"id": "alarm",
|
||||||
"name": "Alarms & Timers",
|
"name": "Alarms & Timers",
|
||||||
"shortName": "Alarms",
|
"shortName": "Alarms",
|
||||||
"version": "0.47",
|
"version": "0.48",
|
||||||
"description": "Set alarms and timers on your Bangle",
|
"description": "Set alarms and timers on your Bangle",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm",
|
"tags": "tool,alarm",
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New drag/swipe date time picker, e.g. for use with dated events alarms
|
|
@ -0,0 +1,9 @@
|
||||||
|
# App Name
|
||||||
|
|
||||||
|
Datetime Picker allows to swipe along the bars to select date and time elements, e.g. for the datetime of Events in the Alarm App. As a standalone app, it allows to see the weekday of a given date and, once a datetime is selected, the number of days and time between that datetime and now.
|
||||||
|
|
||||||
|
Screenshot: 
|
||||||
|
|
||||||
|
## Controls
|
||||||
|
|
||||||
|
Swipe to increase or decrease date and time elements. Press button or go back to select shown datetime.
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwgP/AAnfAgf9z4FD/AFE/gFECIoFB98+tv+voFB//C/99z3Z7+J84XC3/7DpAFhKYP3AgP3AoPAOQMD/v/84LB+Z2FABiDKPoqJFKaWe/P/9Pznuf+wKB/29z+2//uTYOeTYPtRMxZKQaPAh6hBnEBwEGAoMYgHf9+/dwP5A=="))
|
|
@ -0,0 +1,5 @@
|
||||||
|
require("datetimeinput").input().then(result => {
|
||||||
|
E.showPrompt(`${result}\n\n${require("time_utils").formatDuration(Math.abs(result-Date.now()))}`, {buttons:{"Ok":true}}).then(function() {
|
||||||
|
load();
|
||||||
|
});
|
||||||
|
});
|
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,145 @@
|
||||||
|
exports.input = function(options) {
|
||||||
|
options = options||{};
|
||||||
|
var selectedDate;
|
||||||
|
if (options.datetime instanceof Date) {
|
||||||
|
selectedDate = new Date(options.datetime.getTime());
|
||||||
|
} else {
|
||||||
|
selectedDate = new Date();
|
||||||
|
selectedDate.setMinutes(0);
|
||||||
|
selectedDate.setSeconds(0);
|
||||||
|
selectedDate.setMilliseconds(0);
|
||||||
|
selectedDate.setHours(selectedDate.getHours() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var R;
|
||||||
|
var tip = {w: 12, h: 10};
|
||||||
|
var arrowRectArray;
|
||||||
|
var dragging = null;
|
||||||
|
var startPos = null;
|
||||||
|
var dateAtDragStart = null;
|
||||||
|
var SELECTEDFONT = '6x8:2';
|
||||||
|
|
||||||
|
function drawDateTime() {
|
||||||
|
g.clearRect(R.x+tip.w,R.y,R.x2-tip.w,R.y+40);
|
||||||
|
g.clearRect(R.x+tip.w,R.y2-60,R.x2-tip.w,R.y2-40);
|
||||||
|
|
||||||
|
g.setFont(SELECTEDFONT).setColor(g.theme.fg).setFontAlign(-1, -1, 0);
|
||||||
|
var dateUtils = require('date_utils');
|
||||||
|
g.drawString(selectedDate.getFullYear(), R.x+tip.w+10, R.y+15)
|
||||||
|
.drawString(dateUtils.month(selectedDate.getMonth()+1,1), R.x+tip.w+65, R.y+15)
|
||||||
|
.drawString(selectedDate.getDate(), R.x2-tip.w-40, R.y+15)
|
||||||
|
.drawString(`${dateUtils.dow(selectedDate.getDay(), 1)} ${selectedDate.toLocalISOString().slice(11,16)}`, R.x+tip.w+10, R.y2-60);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dragHandler = function(event) {
|
||||||
|
"ram";
|
||||||
|
|
||||||
|
if (event.b) {
|
||||||
|
if (dragging === null) {
|
||||||
|
// determine which component we are affecting
|
||||||
|
var rect = arrowRectArray.find(rect => rect.y2
|
||||||
|
? (event.y >= rect.y && event.y <= rect.y2 && event.x >= rect.x - 10 && event.x <= rect.x + tip.w + 10)
|
||||||
|
: (event.x >= rect.x && event.x <= rect.x2 && event.y >= rect.y - tip.w - 5 && event.y <= rect.y + 5));
|
||||||
|
if (rect) {
|
||||||
|
dragging = rect;
|
||||||
|
startPos = dragging.y2 ? event.y : event.x;
|
||||||
|
dateAtDragStart = selectedDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dragging) {
|
||||||
|
dragging.swipe(dragging.y2 ? startPos - event.y : event.x - startPos);
|
||||||
|
drawDateTime();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dateAtDragStart = null;
|
||||||
|
dragging = null;
|
||||||
|
startPos = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let catchSwipe = ()=>{
|
||||||
|
E.stopEventPropagation&&E.stopEventPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve,reject) => {
|
||||||
|
// Interpret touch input
|
||||||
|
Bangle.setUI({
|
||||||
|
mode: 'custom',
|
||||||
|
back: ()=>{
|
||||||
|
Bangle.setUI();
|
||||||
|
Bangle.prependListener&&Bangle.removeListener('swipe', catchSwipe); // Remove swipe listener if it was added with `Bangle.prependListener()` (fw2v19 and up).
|
||||||
|
g.clearRect(Bangle.appRect);
|
||||||
|
resolve(selectedDate);
|
||||||
|
},
|
||||||
|
drag: dragHandler
|
||||||
|
});
|
||||||
|
Bangle.prependListener&&Bangle.prependListener('swipe', catchSwipe); // Intercept swipes on fw2v19 and later. Should not break on older firmwares.
|
||||||
|
|
||||||
|
R = Bangle.appRect;
|
||||||
|
g.clear();
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
function drawArrow(rect) {
|
||||||
|
if(rect.x2) {
|
||||||
|
g.fillRect(rect.x + tip.h, rect.y - tip.w + 4, rect.x2 - tip.h, rect.y - 4)
|
||||||
|
.fillPoly([rect.x + tip.h, rect.y, rect.x + tip.h, rect.y - tip.w, rect.x, rect.y - (tip.w / 2)])
|
||||||
|
.fillPoly([rect.x2-tip.h, rect.y, rect.x2 - tip.h, rect.y - tip.w, rect.x2, rect.y - (tip.w / 2)]);
|
||||||
|
} else {
|
||||||
|
g.fillRect(rect.x + 4, rect.y + tip.h, rect.x + tip.w - 4, rect.y2 - tip.h)
|
||||||
|
.fillPoly([rect.x, rect.y + tip.h, rect.x + tip.w, rect.y + tip.h, rect.x + (tip.w / 2), rect.y])
|
||||||
|
.fillPoly([rect.x, rect.y2 - tip.h, rect.x + tip.w, rect.y2 - tip.h, rect.x + (tip.w / 2), rect.y2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var yearArrowRect = {x: R.x, y: R.y, y2: R.y + (R.y2 - R.y) * 0.4, swipe: d => {
|
||||||
|
selectedDate = new Date(dateAtDragStart.valueOf());
|
||||||
|
selectedDate.setFullYear(dateAtDragStart.getFullYear() + Math.floor(d/10));
|
||||||
|
if (dateAtDragStart.getDate() != selectedDate.getDate()) selectedDate.setDate(0);
|
||||||
|
}};
|
||||||
|
|
||||||
|
var weekArrowRect = {x: R.x, y: yearArrowRect.y2 + 10, y2: R.y2 - tip.w - 5, swipe: d => {
|
||||||
|
selectedDate = new Date(dateAtDragStart.valueOf());
|
||||||
|
selectedDate.setDate(dateAtDragStart.getDate() + (Math.floor(d/10) * 7));
|
||||||
|
}};
|
||||||
|
|
||||||
|
var dayArrowRect = {x: R.x2 - tip.w, y: R.y, y2: R.y + (R.y2 - R.y) * 0.4, swipe: d => {
|
||||||
|
selectedDate = new Date(dateAtDragStart.valueOf());
|
||||||
|
selectedDate.setDate(dateAtDragStart.getDate() + Math.floor(d/10));
|
||||||
|
}};
|
||||||
|
|
||||||
|
var fifteenMinutesArrowRect = {x: R.x2 - tip.w, y: dayArrowRect.y2 + 10, y2: R.y2 - tip.w - 5, swipe: d => {
|
||||||
|
selectedDate = new Date(dateAtDragStart.valueOf());
|
||||||
|
selectedDate.setMinutes((((dateAtDragStart.getMinutes() - (dateAtDragStart.getMinutes() % 15) + (Math.floor(d/14) * 15)) % 60) + 60) % 60);
|
||||||
|
}};
|
||||||
|
|
||||||
|
var weekdayArrowRect = {x: R.x, y: R.y2, x2: (R.x2 - R.x) * 0.3 - 5, swipe: d => {
|
||||||
|
selectedDate = new Date(dateAtDragStart.valueOf());
|
||||||
|
selectedDate.setDate(dateAtDragStart.getDate() + Math.floor(d/10));
|
||||||
|
}};
|
||||||
|
|
||||||
|
var hourArrowRect = {x: weekdayArrowRect.x2 + 5, y: R.y2, x2: weekdayArrowRect.x2 + (R.x2 - R.x) * 0.38, swipe: d => {
|
||||||
|
selectedDate = new Date(dateAtDragStart.valueOf());
|
||||||
|
selectedDate.setHours((((dateAtDragStart.getHours() + Math.floor(d/10)) % 24) + 24) % 24);
|
||||||
|
}};
|
||||||
|
|
||||||
|
var minutesArrowRect = {x: hourArrowRect.x2 + 5, y: R.y2, x2: R.x2, swipe: d => {
|
||||||
|
selectedDate = new Date(dateAtDragStart.valueOf());
|
||||||
|
selectedDate.setMinutes((((dateAtDragStart.getMinutes() + Math.floor(d/7)) % 60) + 60) % 60);
|
||||||
|
}};
|
||||||
|
|
||||||
|
var monthArrowRect = {x: (R.x2 - R.x) * 0.2, y: R.y2 / 2 + 5, x2: (R.x2 - R.x) * 0.8, swipe: d => {
|
||||||
|
selectedDate = new Date(dateAtDragStart.valueOf());
|
||||||
|
selectedDate.setMonth(dateAtDragStart.getMonth() + Math.floor(d/10));
|
||||||
|
if (dateAtDragStart.getDate() != selectedDate.getDate()) selectedDate.setDate(0);
|
||||||
|
}};
|
||||||
|
|
||||||
|
arrowRectArray = [yearArrowRect, weekArrowRect, dayArrowRect, fifteenMinutesArrowRect,
|
||||||
|
weekdayArrowRect, hourArrowRect, minutesArrowRect, monthArrowRect];
|
||||||
|
|
||||||
|
drawDateTime();
|
||||||
|
arrowRectArray.forEach(drawArrow);
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,16 @@
|
||||||
|
{ "id": "datetime_picker",
|
||||||
|
"name": "Datetime picker",
|
||||||
|
"shortName":"Datetime picker",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "Allows to pick a date and time by swiping.",
|
||||||
|
"icon":"app.png",
|
||||||
|
"tags":"datetimeinput",
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"screenshots" : [ { "url":"screenshot.png" } ],
|
||||||
|
"storage": [
|
||||||
|
{"name":"datetimeinput","url":"lib.js"},
|
||||||
|
{"name":"datetime_picker.app.js","url":"app.js"},
|
||||||
|
{"name":"datetime_picker.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Loading…
Reference in New Issue