Merge branch 'master' of github.com:nxdefiant/BangleApps
|
@ -11,3 +11,4 @@ tests/Layout/bin/tmp.*
|
|||
tests/Layout/testresult.bmp
|
||||
apps.local.json
|
||||
_site
|
||||
.jekyll-cache
|
||||
|
|
24
README.md
|
@ -186,7 +186,7 @@ The widget example is available in [`apps/_example_widget`](apps/_example_widget
|
|||
|
||||
Widgets are just small bits of code that run whenever an app that supports them
|
||||
calls `Bangle.loadWidgets()`. If they want to display something in the 24px high
|
||||
widget bar at the top of the screen they can add themselves to the global
|
||||
widget bar at the top of the screen they can add themselves to the global
|
||||
`WIDGETS` array with:
|
||||
|
||||
```
|
||||
|
@ -226,10 +226,8 @@ and which gives information about the app for the Launcher.
|
|||
"name":"Short Name", // for Bangle.js menu
|
||||
"icon":"*myappid", // for Bangle.js menu
|
||||
"src":"-myappid", // source file
|
||||
"type":"widget/clock/app/bootloader", // optional, default "app"
|
||||
// if this is 'widget' then it's not displayed in the menu
|
||||
// if it's 'clock' then it'll be loaded by default at boot time
|
||||
// if this is 'bootloader' then it's code that is run at boot time, but is not in a menu
|
||||
"type":"widget/clock/app/bootloader/...", // optional, default "app"
|
||||
// see 'type' in 'metadata.json format' below for more options/info
|
||||
"version":"1.23",
|
||||
// added by BangleApps loader on upload based on metadata.json
|
||||
"files:"file1,file2,file3",
|
||||
|
@ -252,17 +250,23 @@ and which gives information about the app for the Launcher.
|
|||
"version": "0v01", // the version of this app
|
||||
"description": "...", // long description (can contain markdown)
|
||||
"icon": "icon.png", // icon in apps/
|
||||
"screenshots" : [ { url:"screenshot.png" } ], // optional screenshot for app
|
||||
"screenshots" : [ { "url":"screenshot.png" } ], // optional screenshot for app
|
||||
"type":"...", // optional(if app) -
|
||||
// 'app' - an application
|
||||
// 'clock' - a clock - required for clocks to automatically start
|
||||
// 'widget' - a widget
|
||||
// 'launch' - replacement launcher app
|
||||
// 'bootloader' - code that runs at startup only
|
||||
// 'bootloader' - an app that at startup (app.boot.js) but doesn't have a launcher entry for 'app.js'
|
||||
// 'RAM' - code that runs and doesn't upload anything to storage
|
||||
// 'launch' - replacement 'Launcher'
|
||||
// 'textinput' - provides a 'textinput' library that allows text to be input on the Bangle
|
||||
// 'scheduler' - provides 'sched' library and boot code for scheduling alarms/timers
|
||||
// (currently only 'sched' app)
|
||||
// 'notify' - provides 'notify' library for showing notifications
|
||||
// 'locale' - provides 'locale' library for language-specific date/distance/etc
|
||||
// (a version of 'locale' is included in the firmware)
|
||||
"tags": "", // comma separated tag list for searching
|
||||
"supports": ["BANGLEJS2"], // List of device IDs supported, either BANGLEJS or BANGLEJS2
|
||||
"dependencies" : { "notify":"type" } // optional, app 'types' we depend on
|
||||
"dependencies" : { "notify":"type" } // optional, app 'types' we depend on (see "type" above)
|
||||
"dependencies" : { "messages":"app" } // optional, depend on a specific app ID
|
||||
// for instance this will use notify/notifyfs is they exist, or will pull in 'notify'
|
||||
"readme": "README.md", // if supplied, a link to a markdown-style text file
|
||||
|
@ -415,7 +419,7 @@ Example `settings.js`
|
|||
// make sure to enclose the function in parentheses
|
||||
(function(back) {
|
||||
let settings = require('Storage').readJSON('myappid.json',1)||{};
|
||||
if (typeof settings.monkeys !== "number") settings.monkeys = 12; // default value
|
||||
if (typeof settings.monkeys !== "number") settings.monkeys = 12; // default value
|
||||
function save(key, value) {
|
||||
settings[key] = value;
|
||||
require('Storage').write('myappid.json', settings);
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Fix the settings bug and some tweaking
|
||||
0.03: Do not alarm while charging
|
||||
0.04: Obey system quiet mode
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
# Activity reminder
|
||||
|
||||
A reminder to take short walks for the ones with a sedentary lifestyle.
|
||||
The alert will popup only if you didn't take your short walk yet
|
||||
The alert will popup only if you didn't take your short walk yet.
|
||||
|
||||
Different settings can be personalized:
|
||||
- Enable : Enable/Disable the app
|
||||
- Start hour: Hour to start the reminder
|
||||
- End hour: Hour to end the reminder
|
||||
- Max inactivity: Maximum inactivity time to allow before the alert. From 15 to 60 min
|
||||
- Dismiss delay: Delay added before the next alert if the alert is dismissed. From 5 to 15 min
|
||||
- Dismiss delay: Delay added before the next alert if the alert is dismissed. From 5 to 60 min
|
||||
Notice: If Dissmiss delay > Max inactivity then it will be equal Max inactivity
|
||||
- Min steps: Minimal amount of steps to count as an activity
|
||||
|
||||
|
|
|
@ -13,8 +13,11 @@ function drawAlert(){
|
|||
}
|
||||
load();
|
||||
});
|
||||
|
||||
Bangle.buzz(400);
|
||||
|
||||
// Obey system quiet mode:
|
||||
if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||
Bangle.buzz(400);
|
||||
}
|
||||
setTimeout(load, 20000);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
function run(){
|
||||
if (Bangle.isCharging()) return;
|
||||
var now = new Date();
|
||||
var h = now.getHours();
|
||||
if(h >= activityreminder.startHour && h < activityreminder.endHour){
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Activity Reminder",
|
||||
"shortName":"Activity Reminder",
|
||||
"description": "A reminder to take short walks for the ones with a sedentary lifestyle",
|
||||
"version":"0.02",
|
||||
"version":"0.04",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
"tags": "tool,activity",
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
},
|
||||
'Dismiss delay': {
|
||||
value: settings.dismissDelayMin,
|
||||
min: 5, max: 15,
|
||||
min: 5, max: 60,
|
||||
onchange: v => {
|
||||
settings.dismissDelayMin = v;
|
||||
require("activityreminder").writeSettings(settings);
|
||||
|
|
|
@ -25,3 +25,4 @@
|
|||
0.24: Automatically save the alarm/timer when the user returns to the main menu using the back arrow
|
||||
Add "Enable All", "Disable All" and "Remove All" actions
|
||||
0.25: Fix redrawing selected Alarm/Timer entry inside edit submenu
|
||||
0.26: Add support for Monday as first day of the week (#1780)
|
||||
|
|
|
@ -2,10 +2,14 @@ Bangle.loadWidgets();
|
|||
Bangle.drawWidgets();
|
||||
|
||||
// An array of alarm objects (see sched/README.md)
|
||||
let alarms = require("sched").getAlarms();
|
||||
var alarms = require("sched").getAlarms();
|
||||
|
||||
// 0 = Sunday
|
||||
// 1 = Monday
|
||||
var firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0;
|
||||
|
||||
function getCurrentTime() {
|
||||
let time = new Date();
|
||||
var time = new Date();
|
||||
return (
|
||||
time.getHours() * 3600000 +
|
||||
time.getMinutes() * 60000 +
|
||||
|
@ -14,6 +18,9 @@ function getCurrentTime() {
|
|||
}
|
||||
|
||||
function saveAndReload() {
|
||||
// Before saving revert the dow to the standard format
|
||||
alarms.forEach(a => a.dow = handleFirstDayOfWeek(a.dow, firstDayOfWeek));
|
||||
|
||||
require("sched").setAlarms(alarms);
|
||||
require("sched").reload();
|
||||
}
|
||||
|
@ -23,30 +30,32 @@ function showMainMenu() {
|
|||
// Alarm img "\0"+atob("FBSBAABgA4YcMPDGP8Zn/mx/48//PP/zD/8A//AP/wD/8A//AP/wH/+D//w//8AAAADwAAYA")
|
||||
const menu = {
|
||||
'': { 'title': /*LANG*/'Alarms&Timers' },
|
||||
/*LANG*/'< Back' : ()=>{load();},
|
||||
/*LANG*/'New Alarm': ()=>editAlarm(-1),
|
||||
/*LANG*/'New Timer': ()=>editTimer(-1)
|
||||
/*LANG*/'< Back': () => { load(); },
|
||||
/*LANG*/'New Alarm': () => editAlarm(-1),
|
||||
/*LANG*/'New Timer': () => editTimer(-1)
|
||||
};
|
||||
alarms.forEach((alarm,idx)=>{
|
||||
var type,txt; // a leading space is currently required (JS error in Espruino 2v12)
|
||||
alarms.forEach((alarm, idx) => {
|
||||
alarm.dow = handleFirstDayOfWeek(alarm.dow, firstDayOfWeek);
|
||||
|
||||
var type, txt; // a leading space is currently required (JS error in Espruino 2v12)
|
||||
if (alarm.timer) {
|
||||
type = /*LANG*/"Timer";
|
||||
txt = " "+require("sched").formatTime(alarm.timer);
|
||||
txt = " " + require("sched").formatTime(alarm.timer);
|
||||
} else {
|
||||
type = /*LANG*/"Alarm";
|
||||
txt = " "+require("sched").formatTime(alarm.t);
|
||||
txt = " " + require("sched").formatTime(alarm.t);
|
||||
}
|
||||
if (alarm.rp) txt += "\0"+atob("FBaBAAABgAAcAAHn//////wAHsABzAAYwAAMAADAAAAAAwAAMAADGAAzgAN4AD//////54AAOAABgAA=");
|
||||
if (alarm.rp) txt += "\0" + atob("FBaBAAABgAAcAAHn//////wAHsABzAAYwAAMAADAAAAAAwAAMAADGAAzgAN4AD//////54AAOAABgAA=");
|
||||
// rename duplicate alarms
|
||||
if (menu[type+txt]) {
|
||||
if (menu[type + txt]) {
|
||||
var n = 2;
|
||||
while (menu[type+" "+n+txt]) n++;
|
||||
txt = type+" "+n+txt;
|
||||
} else txt = type+txt;
|
||||
while (menu[type + " " + n + txt]) n++;
|
||||
txt = type + " " + n + txt;
|
||||
} else txt = type + txt;
|
||||
// add to menu
|
||||
menu[txt] = {
|
||||
value : "\0"+atob(alarm.on?"EhKBAH//v/////////////5//x//j//H+eP+Mf/A//h//z//////////3//g":"EhKBAH//v//8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA///3//g"),
|
||||
onchange : function() {
|
||||
value: "\0" + atob(alarm.on ? "EhKBAH//v/////////////5//x//j//H+eP+Mf/A//h//z//////////3//g" : "EhKBAH//v//8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA///3//g"),
|
||||
onchange: function () {
|
||||
setTimeout(alarm.timer ? editTimer : editAlarm, 10, idx, alarm);
|
||||
}
|
||||
};
|
||||
|
@ -69,25 +78,28 @@ function showMainMenu() {
|
|||
function editDOW(dow, onchange) {
|
||||
const menu = {
|
||||
'': { 'title': /*LANG*/'Days of Week' },
|
||||
/*LANG*/'< Back' : () => onchange(dow)
|
||||
/*LANG*/'< Back': () => onchange(dow)
|
||||
};
|
||||
for (let i = 0; i < 7; i++) (i => {
|
||||
let dayOfWeek = require("locale").dow({ getDay: () => i });
|
||||
menu[dayOfWeek] = {
|
||||
value: !!(dow&(1<<i)),
|
||||
|
||||
require("date_utils").dows(firstDayOfWeek).forEach((day, i) => {
|
||||
menu[day] = {
|
||||
value: !!(dow & (1 << (i + firstDayOfWeek))),
|
||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
||||
onchange: v => v ? dow |= 1<<i : dow &= ~(1<<i),
|
||||
onchange: v => v ? (dow |= 1 << (i + firstDayOfWeek)) : (dow &= ~(1 << (i + firstDayOfWeek)))
|
||||
};
|
||||
})(i);
|
||||
});
|
||||
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function editAlarm(alarmIndex, alarm) {
|
||||
let newAlarm = alarmIndex < 0;
|
||||
let a = require("sched").newDefaultAlarm();
|
||||
var newAlarm = alarmIndex < 0;
|
||||
var a = require("sched").newDefaultAlarm();
|
||||
a.dow = handleFirstDayOfWeek(a.dow, firstDayOfWeek);
|
||||
|
||||
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
|
||||
if (alarm) Object.assign(a,alarm);
|
||||
let t = require("sched").decodeTime(a.t);
|
||||
if (alarm) Object.assign(a, alarm);
|
||||
var t = require("sched").decodeTime(a.t);
|
||||
|
||||
const menu = {
|
||||
'': { 'title': /*LANG*/'Alarm' },
|
||||
|
@ -96,17 +108,17 @@ function editAlarm(alarmIndex, alarm) {
|
|||
showMainMenu();
|
||||
},
|
||||
/*LANG*/'Hours': {
|
||||
value: t.hrs, min : 0, max : 23, wrap : true,
|
||||
onchange: v => t.hrs=v
|
||||
value: t.hrs, min: 0, max: 23, wrap: true,
|
||||
onchange: v => t.hrs = v
|
||||
},
|
||||
/*LANG*/'Minutes': {
|
||||
value: t.mins, min : 0, max : 59, wrap : true,
|
||||
onchange: v => t.mins=v
|
||||
value: t.mins, min: 0, max: 59, wrap: true,
|
||||
onchange: v => t.mins = v
|
||||
},
|
||||
/*LANG*/'Enabled': {
|
||||
value: a.on,
|
||||
format: v => v ? /*LANG*/"On" : /*LANG*/"Off",
|
||||
onchange: v=>a.on=v
|
||||
onchange: v => a.on = v
|
||||
},
|
||||
/*LANG*/'Repeat': {
|
||||
value: a.rp,
|
||||
|
@ -114,14 +126,14 @@ function editAlarm(alarmIndex, alarm) {
|
|||
onchange: v => a.rp = v
|
||||
},
|
||||
/*LANG*/'Days': {
|
||||
value: "SMTWTFS".split("").map((d,n)=>a.dow&(1<<n)?d:".").join(""),
|
||||
value: decodeDOW(a.dow),
|
||||
onchange: () => setTimeout(editDOW, 100, a.dow, d => {
|
||||
a.dow = d;
|
||||
a.t = require("sched").encodeTime(t);
|
||||
editAlarm(alarmIndex, a);
|
||||
})
|
||||
},
|
||||
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
|
||||
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate = v),
|
||||
/*LANG*/'Auto Snooze': {
|
||||
value: a.as,
|
||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
||||
|
@ -156,11 +168,11 @@ function saveAlarm(newAlarm, alarmIndex, a, t) {
|
|||
}
|
||||
|
||||
function editTimer(alarmIndex, alarm) {
|
||||
let newAlarm = alarmIndex < 0;
|
||||
let a = require("sched").newDefaultTimer();
|
||||
var newAlarm = alarmIndex < 0;
|
||||
var a = require("sched").newDefaultTimer();
|
||||
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
|
||||
if (alarm) Object.assign(a,alarm);
|
||||
let t = require("sched").decodeTime(a.timer);
|
||||
if (alarm) Object.assign(a, alarm);
|
||||
var t = require("sched").decodeTime(a.timer);
|
||||
|
||||
const menu = {
|
||||
'': { 'title': /*LANG*/'Timer' },
|
||||
|
@ -169,26 +181,26 @@ function editTimer(alarmIndex, alarm) {
|
|||
showMainMenu();
|
||||
},
|
||||
/*LANG*/'Hours': {
|
||||
value: t.hrs, min : 0, max : 23, wrap : true,
|
||||
onchange: v => t.hrs=v
|
||||
value: t.hrs, min: 0, max: 23, wrap: true,
|
||||
onchange: v => t.hrs = v
|
||||
},
|
||||
/*LANG*/'Minutes': {
|
||||
value: t.mins, min : 0, max : 59, wrap : true,
|
||||
onchange: v => t.mins=v
|
||||
value: t.mins, min: 0, max: 59, wrap: true,
|
||||
onchange: v => t.mins = v
|
||||
},
|
||||
/*LANG*/'Enabled': {
|
||||
value: a.on,
|
||||
format: v => v ? /*LANG*/"On" : /*LANG*/"Off",
|
||||
onchange: v => a.on = v
|
||||
},
|
||||
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
|
||||
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate = v),
|
||||
};
|
||||
|
||||
menu[/*LANG*/"Cancel"] = () => showMainMenu();
|
||||
|
||||
if (!newAlarm) {
|
||||
menu[/*LANG*/"Delete"] = function() {
|
||||
alarms.splice(alarmIndex,1);
|
||||
menu[/*LANG*/"Delete"] = function () {
|
||||
alarms.splice(alarmIndex, 1);
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
};
|
||||
|
@ -210,6 +222,26 @@ function saveTimer(newAlarm, alarmIndex, a, t) {
|
|||
saveAndReload();
|
||||
}
|
||||
|
||||
function handleFirstDayOfWeek(dow, firstDayOfWeek) {
|
||||
if (firstDayOfWeek == 1) {
|
||||
if ((dow & 1) == 1) {
|
||||
// By default 1 = Sunday.
|
||||
// Here the week starts on Monday and Sunday is ON so move Sunday to 128.
|
||||
dow += 127;
|
||||
} else if ((dow & 128) == 128) {
|
||||
dow -= 127;
|
||||
}
|
||||
}
|
||||
return dow;
|
||||
}
|
||||
|
||||
function decodeDOW(dow) {
|
||||
return require("date_utils")
|
||||
.dows(firstDayOfWeek, 2)
|
||||
.map((day, index) => dow & (1 << (index + firstDayOfWeek)) ? day : "_")
|
||||
.join("");
|
||||
}
|
||||
|
||||
function enableAll(on) {
|
||||
E.showPrompt(/*LANG*/"Are you sure?", {
|
||||
title: on ? /*LANG*/"Enable All" : /*LANG*/"Disable All"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "alarm",
|
||||
"name": "Alarms & Timers",
|
||||
"shortName": "Alarms",
|
||||
"version": "0.25",
|
||||
"version": "0.26",
|
||||
"description": "Set alarms and timers on your Bangle",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,alarm,widget",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "HRM Accelerometer event recorder",
|
||||
"shortName": "HRM ACC recorder",
|
||||
"version": "0.01",
|
||||
"type": "ram",
|
||||
"type": "RAM",
|
||||
"description": "Record HRM and accelerometer events in high resolution to CSV files in your browser",
|
||||
"icon": "app.png",
|
||||
"tags": "debug",
|
||||
|
|
|
@ -115,7 +115,23 @@ E.on('notify',msg=>{
|
|||
// could also use NRF.ancsGetAppInfo(msg.appId) here
|
||||
};
|
||||
var unicodeRemap = {
|
||||
'2019':"'"
|
||||
'2019':"'",
|
||||
'260':"A",
|
||||
'261':"a",
|
||||
'262':"C",
|
||||
'263':"c",
|
||||
'280':"E",
|
||||
'281':"e",
|
||||
'321':"L",
|
||||
'322':"l",
|
||||
'323':"N",
|
||||
'324':"n",
|
||||
'346':"S",
|
||||
'347':"s",
|
||||
'377':"Z",
|
||||
'378':"z",
|
||||
'379':"Z",
|
||||
'380':"z",
|
||||
};
|
||||
var replacer = ""; //(n)=>print('Unknown unicode '+n.toString(16));
|
||||
//if (appNames[msg.appId]) msg.a
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Introduced settings to customize the layout and functionality of the keyboard.
|
||||
|
|
|
@ -2,6 +2,17 @@
|
|||
|
||||
A library that provides an on-screen keyboard for text input.
|
||||
|
||||
## Settings
|
||||
Text size - small or big text font. Default=Big. Suggested=Small.
|
||||
|
||||
Offset keyboard - display the keyboard on top, making it faster to see what character you have selected. Default=No. Suggested=Yes.
|
||||
|
||||
Loop around - should the keyboard highlight loop around when going past the edges? Default=Yes. Suggested=No.
|
||||
|
||||
One-to-one input and release to select - should the input correspond directly to discrete areas on the screen, instead of being handled by scaled relative changes in position on swipes? Default=No. Suggested=Yes.
|
||||
|
||||
Speed scaling - how much should a swipe move the highligt on the keyboard? Higher number corresponds to slower movement. Not applicable if using one-to-one input. Default=24. Suggested=15.
|
||||
|
||||
## Usage
|
||||
|
||||
In your app's metadata, add:
|
||||
|
|
|
@ -69,13 +69,24 @@ var KEYEXTRA = [
|
|||
String.fromCharCode(27,91,53,126), // 0x84 page up
|
||||
String.fromCharCode(27,91,54,126), // 0x85 page down
|
||||
];
|
||||
|
||||
var settings = Object.assign({
|
||||
// default values
|
||||
textSize: 1,
|
||||
offsetKeyboard: 0,
|
||||
loopAround: 1,
|
||||
oneToOne: 0,
|
||||
speedScaling: 24
|
||||
}, require('Storage').readJSON("kbtouch.settings.json", true) || {});
|
||||
|
||||
// state
|
||||
const R = Bangle.appRect;
|
||||
var kbx = 0, kby = 0, kbdx = 0, kbdy = 0, kbShift = false, flashToggle = false;
|
||||
const PX=12, PY=16, DRAGSCALE=24;
|
||||
var xoff = 3, yoff = g.getHeight()-PY*4;
|
||||
const PX=12, PY=16, DRAGSCALE=settings.speedScaling;
|
||||
var xoff = 3, yoff = g.getHeight()-PY*(4+5*settings.offsetKeyboard);
|
||||
|
||||
function draw() {
|
||||
"ram";
|
||||
var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER;
|
||||
//g.drawImage(KEYIMG,0,yoff);
|
||||
g.reset().setFont("6x8:2");
|
||||
|
@ -88,9 +99,9 @@ function draw() {
|
|||
g.drawString(map[1],xoff,yoff+PY);
|
||||
g.drawString(map[2],xoff,yoff+PY*2);
|
||||
g.drawString(map[3],xoff,yoff+PY*3);
|
||||
var l = g.setFont("6x8:4").wrapString(text+(flashToggle?"_":" "), R.w-8);
|
||||
if (l.length>2) l=l.slice(-2);
|
||||
g.drawString(l.join("\n"),R.x+4,R.y+4);
|
||||
var l = g.setFont(settings.textSize ? "6x8:4":"6x8:2").wrapString(text+(flashToggle?"_":" "), R.w-8);
|
||||
if (l.length>2+2*settings.textSize) l=l.slice(-(2+2*settings.textSize));
|
||||
g.drawString(l.join("\n"),R.x+4,R.y+4 +82*settings.offsetKeyboard);
|
||||
|
||||
g.flip();
|
||||
}
|
||||
|
@ -104,24 +115,49 @@ function draw() {
|
|||
return new Promise((resolve,reject) => {
|
||||
|
||||
Bangle.setUI({mode:"custom", drag:e=>{
|
||||
kbdx += e.dx;
|
||||
kbdy += e.dy;
|
||||
var dx = Math.round(kbdx/DRAGSCALE), dy = Math.round(kbdy/DRAGSCALE);
|
||||
kbdx -= dx*DRAGSCALE;
|
||||
kbdy -= dy*DRAGSCALE;
|
||||
if (dx || dy) {
|
||||
kbx = (kbx+dx+15)%15;
|
||||
kby = (kby+dy+4)%4;
|
||||
if (settings.oneToOne) {
|
||||
kbx = Math.max(Math.min(Math.floor((e.x-16) / (6*2)) , 13) , 0);
|
||||
kby = Math.max(Math.min(Math.floor((e.y-120) / (8*2)) , 3) , 0);
|
||||
//print(e.y, kby, e.x, kbx);
|
||||
}
|
||||
|
||||
if (!settings.oneToOne) {
|
||||
kbdx += e.dx;
|
||||
kbdy += e.dy;
|
||||
var dx = Math.round(kbdx/DRAGSCALE), dy = Math.round(kbdy/DRAGSCALE);
|
||||
kbdx -= dx*DRAGSCALE;
|
||||
kbdy -= dy*DRAGSCALE;
|
||||
if (dx || dy) {
|
||||
if (settings.loopAround) {
|
||||
kbx = (kbx+dx+15)%15;
|
||||
kby = (kby+dy+4)%4;
|
||||
} else {
|
||||
kbx = Math.max(Math.min((kbx+dx),13),0);
|
||||
kby = Math.max(Math.min((kby+dy),3),0);
|
||||
}
|
||||
}
|
||||
}
|
||||
draw();
|
||||
|
||||
if (!e.b && e.y>Bangle.appRect.y && settings.oneToOne /*&& settings.releaseToSelect*/) {
|
||||
var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER;
|
||||
var ch = map[kby][kbx];
|
||||
if (ch=="\2") kbShift=!kbShift;
|
||||
else if (ch=="\b") text = text.slice(0,-1);
|
||||
else text += ch;
|
||||
Bangle.buzz(20);
|
||||
draw();
|
||||
}
|
||||
},touch:()=>{
|
||||
var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER;
|
||||
var ch = map[kby][kbx];
|
||||
if (ch=="\2") kbShift=!kbShift;
|
||||
else if (ch=="\b") text = text.slice(0,-1);
|
||||
else text += ch;
|
||||
Bangle.buzz(20);
|
||||
draw();
|
||||
if ( !settings.oneToOne /*|| !settings.releaseToSelect*/) {
|
||||
var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER;
|
||||
var ch = map[kby][kbx];
|
||||
if (ch=="\2") kbShift=!kbShift;
|
||||
else if (ch=="\b") text = text.slice(0,-1);
|
||||
else text += ch;
|
||||
Bangle.buzz(20);
|
||||
draw();
|
||||
}
|
||||
},back:()=>{
|
||||
clearInterval(flashInterval);
|
||||
Bangle.setUI();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{ "id": "kbtouch",
|
||||
"name": "Touch keyboard",
|
||||
"version":"0.01",
|
||||
"version":"0.02",
|
||||
"description": "A library for text input via onscreen keyboard",
|
||||
"icon": "app.png",
|
||||
"type":"textinput",
|
||||
|
@ -9,6 +9,7 @@
|
|||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"textinput","url":"lib.js"}
|
||||
{"name":"textinput","url":"lib.js"},
|
||||
{"name":"kbtouch.settings.js","url":"settings.js"}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
(function(back) {
|
||||
function settings() {
|
||||
let settings = require('Storage').readJSON("kbtouch.settings.json", true) || {};
|
||||
if (settings.textSize===undefined) settings.textSize=1;
|
||||
if (settings.offsetKeyboard===undefined) settings.offsetKeyboard=0;
|
||||
if (settings.loopAround===undefined) settings.loopAround=1;
|
||||
if (settings.oneToOne===undefined) settings.oneToOne=0;
|
||||
if (settings.speedScaling===undefined) settings.speedScaling=24;
|
||||
return settings;
|
||||
}
|
||||
|
||||
function updateSetting(setting, value) {
|
||||
let settings = require('Storage').readJSON("kbtouch.settings.json", true) || {};
|
||||
settings[setting] = value;
|
||||
require('Storage').writeJSON("kbtouch.settings.json", settings);
|
||||
}
|
||||
|
||||
var mainmenu = {
|
||||
"" : { "title" : /*LANG*/"Touch Keyboard" },
|
||||
"< Back" : back,
|
||||
/*LANG*/'Text size': {
|
||||
value: settings().textSize,
|
||||
min: 0, max: 1,
|
||||
format: v => [/*LANG*/"Small",/*LANG*/"Big"][v],
|
||||
onchange: v => updateSetting("textSize", v)
|
||||
},
|
||||
/*LANG*/'Offset keyboard': {
|
||||
value: settings().offsetKeyboard,
|
||||
min: 0, max: 1,
|
||||
format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
||||
onchange: v => updateSetting("offsetKeyboard", v)
|
||||
},
|
||||
/*LANG*/'Loop around': {
|
||||
value: settings().loopAround,
|
||||
min: 0, max: 1,
|
||||
format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
||||
onchange: v => updateSetting("loopAround", v)
|
||||
},
|
||||
/*LANG*/'One-to-one input and release to select': {
|
||||
value: settings().oneToOne,
|
||||
min: 0, max: 1,
|
||||
format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
||||
onchange: v => updateSetting("oneToOne", v)
|
||||
},
|
||||
/*LANG*/'Speed scaling': {
|
||||
value: settings().speedScaling,
|
||||
min: 1, max: 24, step : 1,
|
||||
format: v => v,
|
||||
onchange: v => updateSetting("speedScaling", v)
|
||||
}
|
||||
///*LANG*/'Release to select': {
|
||||
// value: 1|settings().fontSize,
|
||||
// min: 0, max: 1,
|
||||
// format: v => [/*LANG*/"No",/*LANG*/"Yes"][v],
|
||||
// onchange: v => updateSetting("releaseToSelect", v)
|
||||
//}
|
||||
};
|
||||
E.showMenu(mainmenu);
|
||||
})
|
|
@ -4,7 +4,7 @@
|
|||
"version": "0.02",
|
||||
"description": "Replace Bangle.js 2's menus with a version that contains smaller text",
|
||||
"icon": "app.png",
|
||||
"type": "boot",
|
||||
"type": "bootloader",
|
||||
"tags": "system",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"storage": [
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
{"url":"screenshot_b1_dark.png"},{"url":"screenshot_b1_edit.png"},{"url":"screenshot_b1_light.png"},
|
||||
{"url":"screenshot_b2_dark.png"},{"url":"screenshot_b2_edit.png"},{"url":"screenshot_b2_light.png"}
|
||||
],
|
||||
"type": "boot",
|
||||
"type": "bootloader",
|
||||
"tags": "system",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"storage": [
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"icon": "mmind.png",
|
||||
"version":"0.01",
|
||||
"description": "This is the classic game for masterminds",
|
||||
"type": "game",
|
||||
"type": "app",
|
||||
"tags": "mastermind, game, classic",
|
||||
"readme":"README.md",
|
||||
"supports": ["BANGLEJS2"],
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"version": "0.01",
|
||||
"description": "A retro-inspired dockface that displays the current time and battery charge while plugged in, and which features an interactive mode that shows the time, date, and a rotating data display line.",
|
||||
"icon": "mystic-dock.png",
|
||||
"type": "dock",
|
||||
"type": "app",
|
||||
"tags": "dock",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Fix true wind computation, add swipe gesture to pause GPS
|
||||
|
|
|
@ -14,7 +14,9 @@ additionally displayed in red. In this mode, the speed over ground in knots is
|
|||
|
||||
## Controls
|
||||
|
||||
There are no controls in the main app, but there are two settings in the settings app that can be changed:
|
||||
In the main app, when true wind mode is enabled (see below), swiping left on the screen will temporarily disable GPS (to preserve battery); a small
|
||||
red satellite symbol will appear on the bottom right. Swiping right will turn GPS back on.
|
||||
The settings app provides the following two settings:
|
||||
|
||||
* True wind: enables or disables true wind calculations; enabling this will turn on GPS inside the app
|
||||
* Mounting angle: mounting relative to the boat of the wind instrument (in degrees)
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
OW_CHAR_UUID = '0000cc91-0000-1000-8000-00805f9b34fb';
|
||||
require("Font7x11Numeric7Seg").add(Graphics);
|
||||
gatt = {};
|
||||
cx = g.getWidth()/2;
|
||||
cy = 24+(g.getHeight()-24)/2;
|
||||
w = (g.getWidth()-24)/2;
|
||||
|
||||
gps_course = { spd: 0 };
|
||||
var gatt = {};
|
||||
var cx = g.getWidth()/2;
|
||||
var cy = 24+(g.getHeight()-24)/2;
|
||||
var w = (g.getWidth()-24)/2;
|
||||
var y1 = 24;
|
||||
var y2 = g.getHeight()-1;
|
||||
var gps_course = { spd: 0 };
|
||||
var course_marker_len = g.getWidth()/4;
|
||||
|
||||
var settings = require("Storage").readJSON('openwindsettings.json', 1) || {};
|
||||
|
||||
i = 0;
|
||||
hullpoly = [];
|
||||
var pause_gps = false;
|
||||
|
||||
var i = 0;
|
||||
var hullpoly = [];
|
||||
for (y=-1; y<=1; y+=0.1) {
|
||||
hullpoly[i++] = cx - (y<0 ? 1+y*0.15 : (Math.sqrt(1-0.7*y*y)-Math.sqrt(0.3))/(1-Math.sqrt(0.3)))*w*0.3;
|
||||
hullpoly[i++] = cy - y*w*0.7;
|
||||
|
@ -22,21 +26,22 @@ for (y=1; y>=-1; y-=0.1) {
|
|||
|
||||
function wind_updated(ev) {
|
||||
if (ev.target.uuid == "0xcc91") {
|
||||
awa = settings.mount_angle-ev.target.value.getInt16(1, true)*0.1;
|
||||
awa = settings.mount_angle+ev.target.value.getInt16(1, true)*0.1;
|
||||
if (awa<0) awa += 360;
|
||||
aws = ev.target.value.getInt16(3, true)*0.01;
|
||||
// console.log(awa, aws);
|
||||
//console.log(awa, aws);
|
||||
if (gps_course.spd > 0) {
|
||||
wv = { // wind vector (in fixed reference frame)
|
||||
lon: Math.sin(Math.PI*(gps_course.course+awa)/180)*aws,
|
||||
lat: Math.cos(Math.PI*(gps_course.course+awa)/180)*aws
|
||||
wv = { // wind vector (in "earth" reference frame)
|
||||
vlon: Math.sin(Math.PI*(gps_course.course+(awa+180))/180)*aws,
|
||||
vlat: Math.cos(Math.PI*(gps_course.course+(awa+180))/180)*aws
|
||||
};
|
||||
twv = { lon: wv.lon+gps_course.lon, lat: wv.lat+gps_course.lat };
|
||||
tws = Math.sqrt(Math.pow(twv.lon,2)+Math.pow(twv.lat, 2));
|
||||
twa = Math.atan2(twv.lat, twv.lon)*180/Math.PI-gps_course.course;
|
||||
twv = { vlon: wv.vlon+gps_course.vlon, vlat: wv.vlat+gps_course.vlat };
|
||||
tws = Math.sqrt(Math.pow(twv.vlon,2)+Math.pow(twv.vlat, 2));
|
||||
twa = 180+Math.atan2(twv.vlon, twv.vlat)*180/Math.PI-gps_course.course;
|
||||
if (twa<0) twa += 360;
|
||||
if (twa>360) twa -=360;
|
||||
}
|
||||
else {
|
||||
else {
|
||||
tws = -1;
|
||||
twa = 0;
|
||||
}
|
||||
|
@ -57,34 +62,37 @@ function draw_compass(awa, aws, twa, tws) {
|
|||
a = i*Math.PI/2+Math.PI/4;
|
||||
g.drawLineAA(cx+Math.cos(a)*w*0.85, cy+Math.sin(a)*w*0.85, cx+Math.cos(a)*w*0.99, cy+Math.sin(a)*w*0.99);
|
||||
}
|
||||
g.setColor(0, 1, 0).fillCircle(cx+Math.sin(Math.PI*awa/180)*w*0.9, cy+Math.cos(Math.PI*awa/180)*w*0.9, w*0.1);
|
||||
g.setColor(0, 1, 0).fillCircle(cx+Math.sin(Math.PI*awa/180)*w*0.9, cy-Math.cos(Math.PI*awa/180)*w*0.9, w*0.1);
|
||||
if (tws>0) g.setColor(1, 0, 0).fillCircle(cx+Math.sin(Math.PI*twa/180)*w*0.9, cy+Math.cos(Math.PI*twa/180)*w*0.9, w*0.1);
|
||||
g.setColor(0, 1, 0).setFont("7x11Numeric7Seg",w*0.06);
|
||||
g.setFontAlign(0, 0, 0).drawString(aws.toFixed(1), cx, cy-0.32*w);
|
||||
if (tws>0) g.setColor(1, 0, 0).drawString(tws.toFixed(1), cx, cy+0.32*w);
|
||||
if (settings.truewind && typeof gps_course.spd!=='undefined') {
|
||||
spd = gps_course.spd/1.852;
|
||||
g.setColor(g.theme.fg).setFont("7x11Numeric7Seg", w*0.03).setFontAlign(-1, 1, 0).drawString(spd.toFixed(1), 1, g.getHeight()-1);
|
||||
if (!pause_gps) {
|
||||
if (tws>0) g.setColor(1, 0, 0).drawString(tws.toFixed(1), cx, cy+0.32*w);
|
||||
if (settings.truewind && gps_course.spd!=-1) {
|
||||
spd = gps_course.spd/1.852;
|
||||
g.setColor(g.theme.fg).setFont("7x11Numeric7Seg", w*0.03).setFontAlign(-1, 1, 0).drawString(spd.toFixed(1), 1, g.getHeight()-1);
|
||||
}
|
||||
}
|
||||
if (pause_gps) g.setColor("#f00").drawImage(atob("DAwBEAKARAKQE4DwHkPqPRGKAEAA"),g.getWidth()-15, g.getHeight()-15);
|
||||
}
|
||||
|
||||
function parseDevice(d) {
|
||||
device = d;
|
||||
console.log("Found device");
|
||||
device.gatt.connect().then(function(ga) {
|
||||
console.log("Connected");
|
||||
gatt = ga;
|
||||
return ga.getPrimaryService("cc90");
|
||||
}).then(function(s) {
|
||||
return s.getCharacteristic("cc91");
|
||||
}).then(function(c) {
|
||||
c.on('characteristicvaluechanged', (event)=>wind_updated(event));
|
||||
return c.startNotifications();
|
||||
}).then(function() {
|
||||
console.log("Done!");
|
||||
}).catch(function(e) {
|
||||
console.log("ERROR"+e);
|
||||
});}
|
||||
device.gatt.connect().then(function(ga) {
|
||||
console.log("Connected");
|
||||
gatt = ga;
|
||||
return ga.getPrimaryService("cc90");
|
||||
}).then(function(s) {
|
||||
return s.getCharacteristic("cc91");
|
||||
}).then(function(c) {
|
||||
c.on('characteristicvaluechanged', (event)=>wind_updated(event));
|
||||
return c.startNotifications();
|
||||
}).then(function() {
|
||||
console.log("Done!");
|
||||
}).catch(function(e) {
|
||||
console.log("ERROR"+e);
|
||||
});}
|
||||
|
||||
function connection_setup() {
|
||||
NRF.setScan();
|
||||
|
@ -96,8 +104,10 @@ if (settings.truewind) {
|
|||
Bangle.on('GPS',function(fix) {
|
||||
if (fix.fix && fix.satellites>3 && fix.speed>2) { // only uses fixes w/ more than 3 sats and speed > 2kph
|
||||
gps_course =
|
||||
{ lon: Math.sin(Math.PI*fix.course/180)*fix.speed/1.852,
|
||||
lat: Math.cos(Math.PI*fix.course/180)*fix.speed/1.852,
|
||||
{ vlon: Math.sin(Math.PI*fix.course/180)*fix.speed/1.852,
|
||||
vlat: Math.cos(Math.PI*fix.course/180)*fix.speed/1.852,
|
||||
lat: fix.lat,
|
||||
lon: fix.lon,
|
||||
spd: fix.speed,
|
||||
course: fix.course
|
||||
};
|
||||
|
@ -107,6 +117,20 @@ if (settings.truewind) {
|
|||
Bangle.setGPSPower(1, "app");
|
||||
}
|
||||
|
||||
if (settings.truewind) {
|
||||
Bangle.on("swipe", (d)=>{
|
||||
if (d==-1 && !pause_gps) {
|
||||
pause_gps = true;
|
||||
Bangle.setGPSPower(0);
|
||||
draw_compass(0, 0, 0, 0);
|
||||
}
|
||||
else if (d==1 && pause_gps) {
|
||||
pause_gps = false;
|
||||
Bangle.setGPSPower(1, "app");
|
||||
draw_compass(0, 0, 0, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
draw_compass(0, 0, 0, 0);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "openwind",
|
||||
"name": "OpenWind",
|
||||
"shortName":"OpenWind",
|
||||
"version":"0.01",
|
||||
"version":"0.02",
|
||||
"description": "OpenWind",
|
||||
"icon": "openwind.png",
|
||||
"readme": "README.md",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"version": "0.02",
|
||||
"description": "Replace the built in menu function. Supports Bangle.js 1 and Bangle.js 2.",
|
||||
"icon": "icon.png",
|
||||
"type": "boot",
|
||||
"type": "bootloader",
|
||||
"tags": "system",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: Initial version
|
||||
0.02: Moved settings from launcher to settings->apps menu
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
require("heatshrink").decompress(atob("kMigILIgPAAYMD/ADBwcGhkAwM5wcA/+2//Av/Rn/giFoyFggkUrFggEKlAkCiApCx+AAYNGoADBkU4AYMQj4DBvEICANkAoIPBgE2B4MAiMAH4MAwECAYNALYUgBIISCHYMYAoQWBAIMEgAYBAIMBwEDDQNgDwUf/4eBg4DCAA4"))
|
|
@ -1,14 +1,15 @@
|
|||
{ "id": "quicklaunch",
|
||||
{
|
||||
"id": "quicklaunch",
|
||||
"name": "Quick Launch",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"description": "Tap or swipe left/right/up/down on your clock face to launch up to five apps of your choice.",
|
||||
"version":"0.02",
|
||||
"description": "Tap or swipe left/right/up/down on your clock face to launch up to five apps of your choice. Configurations can be accessed through Settings->Apps.",
|
||||
"type": "bootloader",
|
||||
"tags": "tools, system",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"quicklaunch.app.js","url":"app.js"},
|
||||
{"name":"quicklaunch.boot.js","url":"boot.js"},
|
||||
{"name":"quicklaunch.img","url":"app-icon.js","evaluate":true}
|
||||
{"name":"quicklaunch.settings.js","url":"settings.js"},
|
||||
{"name":"quicklaunch.boot.js","url":"boot.js"}
|
||||
],
|
||||
"data": [{"name":"quicklaunch.json"}]
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
(function(back) {
|
||||
var settings = Object.assign(require("Storage").readJSON("quicklaunch.json", true) || {});
|
||||
|
||||
var apps = require("Storage").list(/\.info$/).map(app=>{var a=require("Storage").readJSON(app,1);return a&&{name:a.name,type:a.type,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || app.type=="launch" || app.type=="clock" || !app.type));
|
||||
|
@ -118,3 +119,4 @@ apps.forEach((a)=>{
|
|||
});
|
||||
|
||||
showMainMenu();
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,24 @@
|
|||
# SciCalc
|
||||
|
||||
Simple scientific calculator. I needed one, so I wrote a basic one, no design frills. Input expressions are slightly post processed and then evaluated
|
||||
by the JS interpreter.
|
||||
|
||||
## Usage
|
||||
|
||||
Buttons are arranged on 3 separate screens, swiping left or right switches between them. Swiping down has the same effect as hitting the "=" button.
|
||||
|
||||
## Features
|
||||
|
||||
The calculator supports the following operations:
|
||||
|
||||
* basic arithmetic: +, -, *, /, ^ (raise to a power), +/- (invert sign), 1/x (inverse), use of parentheses
|
||||
* trigonometric fucntions: sin, cos, tan, asin, acos, atan
|
||||
* exponential exp, natural logarithm log, pow function (this one takes 2 comma separated arguments)
|
||||
* Pi is provided as a constant
|
||||
* a memory button "M" stores or recalls the last result (after hitting the "=" button or swiping down)
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4AJioAaF1wwSFzowRCQUZo4AWjIvVFy4ABF/4vXyGQAYov/R+sZFy8ZF6oAcF/4vvi4AeF/4SCjseAAMdAx8MAAYvVEAQABAx4v/R/TvvF96PUg8cAAMHd9QuCAAIv/R+rvvF96Pvd94vvR97vvF96Pvd94vvR97vsGDwuQGDouSAH4A/AGwA=="))
|
|
@ -0,0 +1,113 @@
|
|||
const W = g.getWidth();
|
||||
const H = g.getHeight();
|
||||
|
||||
const dispH = H/5;
|
||||
const butH = H-dispH;
|
||||
|
||||
const buttons = [[['7', '8', '9'],
|
||||
['4', '5', '6'],
|
||||
['1', '2', '3'],
|
||||
['E', '0', '.']],
|
||||
[['<', 'M', 'C'],
|
||||
['+', '-', '*'],
|
||||
['/', '(', ')'],
|
||||
['^', ',', '=']],
|
||||
[['Sin', 'Cos', 'Tan'],
|
||||
['Asi', 'Aco', 'Ata'],
|
||||
['Pi', '1/x', '+/-'],
|
||||
['Log', 'Exp', 'Pow']
|
||||
]];
|
||||
|
||||
var curPage = 0;
|
||||
var inputStr = '';
|
||||
var memory = '';
|
||||
var qResult = false;
|
||||
|
||||
function drawPage (p) {
|
||||
g.clearRect(0, dispH, W-1, H-1);
|
||||
g.setFont('Vector', butH/5).setFontAlign(0, 0, 0).setColor(g.theme.fg);
|
||||
for (x=0; x<3; ++x)
|
||||
for (y=0; y<4; ++y)
|
||||
g.drawString(buttons[p][y][x], (x+0.5)*W/3, dispH+(y+0.7)*butH/4);
|
||||
g.setColor(0.5, 0.5, 0.5);
|
||||
for (x=1; x<3; ++x) g.drawLine(x*W/3, dispH+0.2*butH/4-2, x*W/3, H-1);
|
||||
for (y=1; y<4; ++y) g.drawLine(0, dispH+(y+0.2)*butH/4, W-1, dispH+(y+0.2)*butH/4);
|
||||
g.setColor(g.theme.fg).drawLine(0, dispH+0.2*butH/4-2, W-1, dispH+0.2*butH/4-2);
|
||||
}
|
||||
|
||||
function updateDisp(s, len) {
|
||||
var fh = butH/5;
|
||||
if (s.toString().length>len) s = s.toString().substr(0,len);
|
||||
g.setFont("Vector", butH/5).setColor(g.theme.fg).setFontAlign(1, 0, 0);
|
||||
while (g.stringWidth(s) > W-1) {
|
||||
fh /= 1.05;
|
||||
g.setFont("Vector", fh);
|
||||
}
|
||||
g.clearRect(0, 0, W-1, dispH-1).drawString(s, W-2, dispH/2);
|
||||
g.setColor(g.theme.fg).drawLine(0, dispH+0.2*butH/4-2, W-1, dispH+0.2*butH/4-2);
|
||||
}
|
||||
|
||||
function processInp (s) {
|
||||
var idx = s.indexOf("^");
|
||||
if (idx > 0) s = "Math.pow(" + s.slice(0,idx) + "," + s.slice(idx+1, s.length) + ")";
|
||||
['Sin', 'Cos', 'Tan', 'Asin', 'Acos', 'Atan', 'Log', 'Exp', 'Pow'].forEach((x) => {
|
||||
var i = s.indexOf(x);
|
||||
while (i>-1) {
|
||||
s = s.slice(0,i)+"Math."+s.slice(i,i+1).toLowerCase()+s.slice(i+1, s.length);
|
||||
i = s.indexOf(x, i+6);
|
||||
}
|
||||
});
|
||||
idx = s.indexOf('Pi');
|
||||
if (idx>-1) s = s.slice(0,idx) + "Math.PI" + s.slice(idx+2, s.length);
|
||||
idx = 0;
|
||||
s.split('').forEach((x)=>{ if (x=='(') idx++; if (x==')') idx-- });
|
||||
s += ')'.repeat(idx);
|
||||
return s;
|
||||
}
|
||||
|
||||
function compute() {
|
||||
var res;
|
||||
console.log(processInp(inputStr));
|
||||
try { res = eval(processInp(inputStr)); }
|
||||
catch(e) { res = "error"; }
|
||||
inputStr = res;
|
||||
qResult = true;
|
||||
updateDisp(inputStr, 19);
|
||||
}
|
||||
|
||||
function touchHandler(e, d) {
|
||||
var x = Math.floor(d.x/(W/3));
|
||||
var y = Math.floor((d.y-dispH-0.2*butH/4)/(butH/4));
|
||||
var c = buttons[curPage][y][x];
|
||||
if (c=="=") { // do the computation
|
||||
compute();
|
||||
return;
|
||||
}
|
||||
else if (c=="<" && inputStr.length>0) inputStr = inputStr.slice(0, -1); // delete last character
|
||||
else if (c=='M' && qResult) memory = inputStr;
|
||||
else if (c=='M') inputStr += memory;
|
||||
else if (c=="C") inputStr = ''; // clear
|
||||
else {
|
||||
if ("Sin Cos Tan Log Exp Pow".indexOf(c)>-1 && c!='E') c += "(";
|
||||
if ("Asi Aco Ata".indexOf(c)>-1) c += "n(";
|
||||
if (c=='1/x') { inputStr = "1/("+inputStr+")"; compute(); return; }
|
||||
if (c=='+/-') { inputStr = "-("+inputStr+")"; compute(); return; }
|
||||
if (qResult && "+-*/^".indexOf(c)==-1) inputStr = c + inputStr + ")";
|
||||
else inputStr += c;
|
||||
}
|
||||
qResult = false;
|
||||
updateDisp(inputStr, 32);
|
||||
}
|
||||
|
||||
function swipeHandler(e,d) {
|
||||
curPage -= e;
|
||||
if (curPage>buttons.length-1) curPage = 0;
|
||||
if (curPage<0) curPage = buttons.length-1;
|
||||
drawPage(curPage);
|
||||
if (d==1) compute();
|
||||
}
|
||||
|
||||
Bangle.on("touch", touchHandler);
|
||||
Bangle.on("swipe", swipeHandler);
|
||||
g.clear();
|
||||
drawPage(curPage);
|
|
@ -0,0 +1,16 @@
|
|||
{ "id": "scicalc",
|
||||
"name": "Scientific Calculator",
|
||||
"shortName":"SciCalc",
|
||||
"version":"0.01",
|
||||
"description": "Scientific calculator",
|
||||
"icon": "scicalc.png",
|
||||
"screenshots" : [ { "url":"scicalc_screenshot1.png" }, { "url":"scicalc_screenshot2.png" }, { "url":"scicalc_screenshot3.png" } ],
|
||||
"readme": "README.md",
|
||||
"tags": "app,tool",
|
||||
"allow_emulator": true,
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"scicalc.app.js","url":"app.js"},
|
||||
{"name":"scicalc.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 562 B |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 2.9 KiB |
|
@ -46,3 +46,5 @@
|
|||
0.41: Stop users disabling all wake-up methods and locking themselves out (fix #1272)
|
||||
0.42: Fix theme customizer on new Bangle 2 firmware
|
||||
0.43: Add some Bangle 1 colours to theme customizer
|
||||
0.44: Add "Start Week On X" option (#1780)
|
||||
UI improvements to Locale and Date & Time menu
|
||||
|
|
|
@ -7,9 +7,9 @@ This is Bangle.js's settings menu
|
|||
* **Beep** most Bangle.js do not have a speaker inside, but they can use the vibration motor to beep in different pitches. You can change the behaviour here to use a Piezo speaker if one is connected
|
||||
* **Vibration** enable/disable the vibration motor
|
||||
* **Quiet Mode** prevent notifications/alarms from vibrating/beeping/turning the screen on - see below
|
||||
* **Locale** set time zone/whether the clock is 12/24 hour (for supported clocks)
|
||||
* **Locale** set time zone, the time format (12/24h, for supported clocks) and the first day of the week
|
||||
* **Select Clock** if you have more than one clock face, select the default one
|
||||
* **Set Time** Configure the current time - Note that this can be done much more easily by choosing 'Set Time' from the App Loader
|
||||
* **Date & Time** Configure the current time - Note that this can be done much more easily by choosing 'Set Time' from the App Loader
|
||||
* **LCD** Configure settings about the screen. How long it stays on, how bright it is, and when it turns on - see below.
|
||||
* **Theme** Adjust the colour scheme
|
||||
* **Utils** Utilities - including resetting settings (see below)
|
||||
|
@ -35,11 +35,15 @@ This is Bangle.js's settings menu
|
|||
`Wake on Touch` actually uses the accelerometer, and you need to actually tap the display to wake Bangle.js.
|
||||
* **Twist X** these options adjust the sensitivity of `Wake on Twist` to ensure Bangle.js wakes up with just the right amount of wrist movement.
|
||||
|
||||
## Locale
|
||||
|
||||
* **Time Zone** your current Time zone. This is usually set automatically by the App Loader
|
||||
* **Time Format** whether you want a 24 or 12 hour clock. However not all clocks will honour this.
|
||||
* **Start Week On** start the displayed week on Sunday, or Monday. This currently only applies to the Alarm app.
|
||||
|
||||
## Quiet Mode
|
||||
|
||||
Quiet Mode is a hint to apps and widgets that you do not want to be disturbed.
|
||||
Quiet Mode is a hint to apps and widgets that you do not want to be disturbed.
|
||||
The exact effects depend on the app. In general the watch will not wake up by itself, but will still respond to button presses.
|
||||
|
||||
* **Quiet Mode**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "setting",
|
||||
"name": "Settings",
|
||||
"version": "0.43",
|
||||
"version": "0.44",
|
||||
"description": "A menu for setting up Bangle.js",
|
||||
"icon": "settings.png",
|
||||
"tags": "tool,system",
|
||||
|
|
|
@ -37,18 +37,19 @@ function internalToG(u) {
|
|||
|
||||
function resetSettings() {
|
||||
settings = {
|
||||
ble: true, // Bluetooth enabled by default
|
||||
blerepl: true, // Is REPL on Bluetooth - can Espruino IDE be used?
|
||||
log: false, // Do log messages appear on screen?
|
||||
quiet: 0, // quiet mode: 0: off, 1: priority only, 2: total silence
|
||||
timeout: 10, // Default LCD timeout in seconds
|
||||
vibrate: true, // Vibration enabled by default. App must support
|
||||
beep: BANGLEJS2?true:"vib", // Beep enabled by default. App must support
|
||||
timezone: 0, // Set the timezone for the device
|
||||
HID: false, // BLE HID mode, off by default
|
||||
clock: null, // a string for the default clock's name
|
||||
"12hour" : false, // 12 or 24 hour clock?
|
||||
brightness: 1, // LCD brightness from 0 to 1
|
||||
ble: true, // Bluetooth enabled by default
|
||||
blerepl: true, // Is REPL on Bluetooth - can Espruino IDE be used?
|
||||
log: false, // Do log messages appear on screen?
|
||||
quiet: 0, // quiet mode: 0: off, 1: priority only, 2: total silence
|
||||
timeout: 10, // Default LCD timeout in seconds
|
||||
vibrate: true, // Vibration enabled by default. App must support
|
||||
beep: BANGLEJS2 ? true : "vib", // Beep enabled by default. App must support
|
||||
timezone: 0, // Set the timezone for the device
|
||||
HID: false, // BLE HID mode, off by default
|
||||
clock: null, // a string for the default clock's name
|
||||
"12hour" : false, // 12 or 24 hour clock?
|
||||
firstDayOfWeek: 0, // 0 -> Sunday (default), 1 -> Monday
|
||||
brightness: 1, // LCD brightness from 0 to 1
|
||||
// welcomed : undefined/true (whether welcome app should show)
|
||||
options: {
|
||||
wakeOnBTN1: true,
|
||||
|
@ -94,7 +95,7 @@ function showSystemMenu() {
|
|||
/*LANG*/'LCD': ()=>showLCDMenu(),
|
||||
/*LANG*/'Locale': ()=>showLocaleMenu(),
|
||||
/*LANG*/'Select Clock': ()=>showClockMenu(),
|
||||
/*LANG*/'Set Time': ()=>showSetTimeMenu()
|
||||
/*LANG*/'Date & Time': ()=>showSetTimeMenu()
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
|
@ -478,6 +479,7 @@ function showLocaleMenu() {
|
|||
'< Back': ()=>showSystemMenu(),
|
||||
/*LANG*/'Time Zone': {
|
||||
value: settings.timezone,
|
||||
format: v => (v > 0 ? "+" : "") + v,
|
||||
min: -11,
|
||||
max: 13,
|
||||
step: 0.5,
|
||||
|
@ -486,13 +488,23 @@ function showLocaleMenu() {
|
|||
updateSettings();
|
||||
}
|
||||
},
|
||||
/*LANG*/'Clock Style': {
|
||||
/*LANG*/'Time Format': {
|
||||
value: !!settings["12hour"],
|
||||
format: v => v ? "12hr" : "24hr",
|
||||
format: v => v ? "12h" : "24h",
|
||||
onchange: v => {
|
||||
settings["12hour"] = v;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
/*LANG*/'Start Week On': {
|
||||
value: settings["firstDayOfWeek"] || 0,
|
||||
min: 0, // Sunday
|
||||
max: 1, // Monday
|
||||
format: v => require("date_utils").dow(v, 1),
|
||||
onchange: v => {
|
||||
settings["firstDayOfWeek"] = v;
|
||||
updateSettings();
|
||||
},
|
||||
}
|
||||
};
|
||||
return E.showMenu(localemenu);
|
||||
|
@ -606,11 +618,34 @@ function showClockMenu() {
|
|||
function showSetTimeMenu() {
|
||||
d = new Date();
|
||||
const timemenu = {
|
||||
'': { 'title': /*LANG*/'Set Time' },
|
||||
'': { 'title': /*LANG*/'Date & Time' },
|
||||
'< Back': function () {
|
||||
setTime(d.getTime() / 1000);
|
||||
showSystemMenu();
|
||||
},
|
||||
/*LANG*/'Day': {
|
||||
value: d.getDate(),
|
||||
onchange: function (v) {
|
||||
this.value = ((v+30)%31)+1;
|
||||
d.setDate(this.value);
|
||||
}
|
||||
},
|
||||
/*LANG*/'Month': {
|
||||
value: d.getMonth() + 1,
|
||||
format: v => require("date_utils").month(v),
|
||||
onchange: function (v) {
|
||||
this.value = ((v+11)%12)+1;
|
||||
d.setMonth(this.value - 1);
|
||||
}
|
||||
},
|
||||
/*LANG*/'Year': {
|
||||
value: d.getFullYear(),
|
||||
min: 2019,
|
||||
max: 2100,
|
||||
onchange: function (v) {
|
||||
d.setFullYear(v);
|
||||
}
|
||||
},
|
||||
/*LANG*/'Hour': {
|
||||
value: d.getHours(),
|
||||
onchange: function (v) {
|
||||
|
@ -631,28 +666,6 @@ function showSetTimeMenu() {
|
|||
this.value = (v+60)%60;
|
||||
d.setSeconds(this.value);
|
||||
}
|
||||
},
|
||||
/*LANG*/'Date': {
|
||||
value: d.getDate(),
|
||||
onchange: function (v) {
|
||||
this.value = ((v+30)%31)+1;
|
||||
d.setDate(this.value);
|
||||
}
|
||||
},
|
||||
/*LANG*/'Month': {
|
||||
value: d.getMonth() + 1,
|
||||
onchange: function (v) {
|
||||
this.value = ((v+11)%12)+1;
|
||||
d.setMonth(this.value - 1);
|
||||
}
|
||||
},
|
||||
/*LANG*/'Year': {
|
||||
value: d.getFullYear(),
|
||||
min: 2019,
|
||||
max: 2100,
|
||||
onchange: function (v) {
|
||||
d.setFullYear(v);
|
||||
}
|
||||
}
|
||||
};
|
||||
return E.showMenu(timemenu);
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: Release
|
||||
0.01: Release
|
||||
0.02: Rewrite with new interface
|
|
@ -1,21 +1,18 @@
|
|||
# Simple Timer
|
||||
|
||||
A simple app to set a timer quickly. Simply tab on top/bottom/left/right
|
||||
to select the minutes and tab in the middle of the screen to start/stop
|
||||
the timer. Note that this timer depends on qalarm.
|
||||
A simple app to set a timer quickly. Drag or tap on the up and down buttons over the hour, minute or second to set the time.
|
||||
|
||||
# Overview
|
||||
If you open the app, you can simply control the timer
|
||||
by clicking on top, bottom, left or right of the screen.
|
||||
If you tab at the middle of the screen, the timer is
|
||||
started / stopped.
|
||||
This app uses the `sched` library, which allows the timer to continue to run in the background when this app is closed.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
# Creator
|
||||
# Creators
|
||||
[David Peer](https://github.com/peerdavid)
|
||||
|
||||
[Sir Indy](https://github.com/sir-indy)
|
||||
|
||||
# Thanks to...
|
||||
Time icon created by <a href="https://www.flaticon.com/free-icons/time" title="time icons">CreativeCons - Flaticon</a>
|
|
@ -3,122 +3,188 @@
|
|||
*
|
||||
* Creator: David Peer
|
||||
* Date: 02/2022
|
||||
*
|
||||
* Modified: Sir Indy
|
||||
* Date: 05/2022
|
||||
*/
|
||||
|
||||
Bangle.loadWidgets();
|
||||
|
||||
|
||||
const alarm = require("sched");
|
||||
|
||||
const Layout = require("Layout");
|
||||
const alarm = require("sched")
|
||||
const TIMER_IDX = "smpltmr";
|
||||
const screenWidth = g.getWidth();
|
||||
const screenHeight = g.getHeight();
|
||||
const cx = parseInt(screenWidth/2);
|
||||
const cy = parseInt(screenHeight/2)-12;
|
||||
var minutes = 5;
|
||||
var interval; //used for the 1 second interval timer
|
||||
|
||||
|
||||
function isTimerEnabled(){
|
||||
var alarmObj = alarm.getAlarm(TIMER_IDX);
|
||||
if(alarmObj===undefined || !alarmObj.on){
|
||||
return false;
|
||||
const secondsToTime = (s) => new Object({h:Math.floor((s/3600) % 24), m:Math.floor((s/60) % 60), s:Math.floor(s % 60)});
|
||||
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
|
||||
function formatTime(s) {
|
||||
var t = secondsToTime(s);
|
||||
if (t.h) {
|
||||
return t.h + ':' + ("0" + t.m).substr(-2) + ':' + ("0" + t.s).substr(-2);
|
||||
} else {
|
||||
return t.m + ':' + ("0" + t.s).substr(-2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getTimerMin(){
|
||||
var alarmObj = alarm.getAlarm(TIMER_IDX);
|
||||
return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000));
|
||||
var seconds = 5 * 60; // Default to 5 minutes
|
||||
var drawTimeout;
|
||||
//var timerRunning = false;
|
||||
function timerRunning() {
|
||||
return (alarm.getTimeToAlarm(alarm.getAlarm(TIMER_IDX)) != undefined)
|
||||
}
|
||||
const imgArrow = atob("CQmBAAgOBwfD47ndx+OA");
|
||||
const imgPause = atob("GBiBAP+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B/w==");
|
||||
const imgPlay = atob("GBiBAIAAAOAAAPgAAP4AAP+AAP/gAP/4AP/+AP//gP//4P//+P///v///v//+P//4P//gP/+AP/4AP/gAP+AAP4AAPgAAOAAAIAAAA==");
|
||||
|
||||
function onDrag(event) {
|
||||
if (!timerRunning()) {
|
||||
Bangle.buzz(40, 0.3);
|
||||
var diff = -Math.round(event.dy/5);
|
||||
if (event.x < timePickerLayout.hours.w) {
|
||||
diff *= 3600;
|
||||
} else if (event.x > timePickerLayout.mins.x && event.x < timePickerLayout.secs.x) {
|
||||
diff *= 60;
|
||||
}
|
||||
updateTimePicker(diff);
|
||||
}
|
||||
}
|
||||
|
||||
function setTimer(minutes){
|
||||
function onTouch(button, xy) {
|
||||
if (xy.y > (timePickerLayout.btnStart.y||timerLayout.btnStart.y)) {
|
||||
Bangle.buzz(40, 0.3);
|
||||
onButton();
|
||||
return;
|
||||
}
|
||||
if (!timerRunning()) {
|
||||
var touchMidpoint = timePickerLayout.hours.y + timePickerLayout.hours.h/2;
|
||||
var diff = 0;
|
||||
Bangle.buzz(40, 0.3);
|
||||
if (xy.y > 24 && xy.y < touchMidpoint - 10) {
|
||||
diff = 1;
|
||||
} else if (xy.y > touchMidpoint + 10 && xy.y < timePickerLayout.btnStart.y) {
|
||||
diff = -1;
|
||||
}
|
||||
if (xy.x < timePickerLayout.hours.w) {
|
||||
diff *= 3600;
|
||||
} else if (xy.x > timePickerLayout.mins.x && xy.x < timePickerLayout.secs.x) {
|
||||
diff *= 60;
|
||||
}
|
||||
updateTimePicker(diff);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onButton() {
|
||||
g.clearRect(Bangle.appRect);
|
||||
if (timerRunning()) {
|
||||
timerStop();
|
||||
} else {
|
||||
if (seconds > 0) {
|
||||
timerRun();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateTimePicker(diff) {
|
||||
seconds = clamp(seconds + (diff || 0), 0, 24 * 3600 - 1);
|
||||
var set_time = secondsToTime(seconds);
|
||||
updateLayoutField(timePickerLayout, 'hours', set_time.h);
|
||||
updateLayoutField(timePickerLayout, 'mins', set_time.m);
|
||||
updateLayoutField(timePickerLayout, 'secs', set_time.s);
|
||||
}
|
||||
|
||||
function updateTimer() {
|
||||
var timeToNext = alarm.getTimeToAlarm(alarm.getAlarm(TIMER_IDX));
|
||||
updateLayoutField(timerLayout, 'timer', formatTime(timeToNext / 1000));
|
||||
queueDraw(1000);
|
||||
}
|
||||
|
||||
function queueDraw(millisecs) {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
updateTimer();
|
||||
}, millisecs - (Date.now() % millisecs));
|
||||
}
|
||||
|
||||
function timerRun() {
|
||||
alarm.setAlarm(TIMER_IDX, {
|
||||
// msg : "Simple Timer",
|
||||
timer : minutes*60*1000,
|
||||
vibrate : ".-.-",
|
||||
hidden: true,
|
||||
timer : seconds * 1000
|
||||
});
|
||||
alarm.reload();
|
||||
g.clearRect(Bangle.appRect);
|
||||
timerLayout.render();
|
||||
updateTimer();
|
||||
}
|
||||
|
||||
function deleteTimer(){
|
||||
function timerStop() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
var timeToNext = alarm.getTimeToAlarm(alarm.getAlarm(TIMER_IDX));
|
||||
if (timeToNext != undefined) {
|
||||
seconds = timeToNext / 1000;
|
||||
}
|
||||
alarm.setAlarm(TIMER_IDX, undefined);
|
||||
alarm.reload();
|
||||
g.clearRect(Bangle.appRect);
|
||||
timePickerLayout.render();
|
||||
updateTimePicker();
|
||||
}
|
||||
|
||||
setWatch(_=>load(), BTN1);
|
||||
function draw(){
|
||||
g.clear(1);
|
||||
Bangle.drawWidgets();
|
||||
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
interval = undefined;
|
||||
|
||||
// Write time
|
||||
g.setFontAlign(0, 0, 0);
|
||||
g.setFont("Vector", 32).setFontAlign(0,-1);
|
||||
|
||||
var started = isTimerEnabled();
|
||||
var text = minutes + " min.";
|
||||
if(started){
|
||||
var min = getTimerMin();
|
||||
text = min + " min.";
|
||||
}
|
||||
|
||||
var rectWidth = parseInt(g.stringWidth(text) / 2);
|
||||
|
||||
if(started){
|
||||
interval = setInterval(draw, 1000);
|
||||
g.setColor("#ff0000");
|
||||
} else {
|
||||
g.setColor(g.theme.fg);
|
||||
}
|
||||
g.fillRect(cx-rectWidth-5, cy-5, cx+rectWidth, cy+30);
|
||||
|
||||
g.setColor(g.theme.bg);
|
||||
g.drawString(text, cx, cy);
|
||||
}
|
||||
|
||||
|
||||
Bangle.on('touch', function(btn, e){
|
||||
var left = parseInt(g.getWidth() * 0.25);
|
||||
var right = g.getWidth() - left;
|
||||
var upper = parseInt(g.getHeight() * 0.25);
|
||||
var lower = g.getHeight() - upper;
|
||||
|
||||
var isLeft = e.x < left;
|
||||
var isRight = e.x > right;
|
||||
var isUpper = e.y < upper;
|
||||
var isLower = e.y > lower;
|
||||
var isMiddle = !isLeft && !isRight && !isUpper && !isLower;
|
||||
var started = isTimerEnabled();
|
||||
|
||||
if(isRight && !started){
|
||||
minutes += 1;
|
||||
Bangle.buzz(40, 0.3);
|
||||
} else if(isLeft && !started){
|
||||
minutes -= 1;
|
||||
Bangle.buzz(40, 0.3);
|
||||
} else if(isUpper && !started){
|
||||
minutes += 5;
|
||||
Bangle.buzz(40, 0.3);
|
||||
} else if(isLower && !started){
|
||||
minutes -= 5;
|
||||
Bangle.buzz(40, 0.3);
|
||||
} else if(isMiddle) {
|
||||
if(!started){
|
||||
setTimer(minutes);
|
||||
} else {
|
||||
deleteTimer();
|
||||
}
|
||||
Bangle.buzz(80, 0.6);
|
||||
}
|
||||
minutes = Math.max(0, minutes);
|
||||
|
||||
draw();
|
||||
var timePickerLayout = new Layout({
|
||||
type:"v", c: [
|
||||
{type:undefined, height:2},
|
||||
{type:"h", c: [
|
||||
{type:"v", width:g.getWidth()/3, c: [
|
||||
{type:"txt", font:"6x8", label:/*LANG*/"Hours"},
|
||||
{type:"img", pad:8, src:imgArrow},
|
||||
{type:"txt", font:"20%", label:"00", id:"hours", filly:1, fillx:1},
|
||||
{type:"img", pad:8, src:imgArrow, r:2}
|
||||
]},
|
||||
{type:"v", width:g.getWidth()/3, c: [
|
||||
{type:"txt", font:"6x8", label:/*LANG*/"Minutes"},
|
||||
{type:"img", pad:8, src:imgArrow},
|
||||
{type:"txt", font:"20%", label:"00", id:"mins", filly:1, fillx:1},
|
||||
{type:"img", pad:8, src:imgArrow, r:2}
|
||||
]},
|
||||
{type:"v", width:g.getWidth()/3, c: [
|
||||
{type:"txt", font:"6x8", label:/*LANG*/"Seconds"},
|
||||
{type:"img", pad:8, src:imgArrow},
|
||||
{type:"txt", font:"20%", label:"00", id:"secs", filly:1, fillx:1},
|
||||
{type:"img", pad:8, src:imgArrow, r:2}
|
||||
]},
|
||||
]},
|
||||
{type:"btn", src:imgPlay, id:"btnStart", fillx:1 }
|
||||
], filly:1
|
||||
});
|
||||
|
||||
g.reset();
|
||||
draw();
|
||||
var timerLayout = new Layout({
|
||||
type:"v", c: [
|
||||
{type:"txt", font:"22%", label:"0:00", id:"timer", fillx:1, filly:1 },
|
||||
{type:"btn", src:imgPause, id:"btnStart", cb: l=>timerStop(), fillx:1 }
|
||||
], filly:1
|
||||
});
|
||||
|
||||
function updateLayoutField(layout, field, value) {
|
||||
layout.clear(layout[field]);
|
||||
layout[field].label = value;
|
||||
layout.render(layout[field]);
|
||||
}
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
Bangle.setUI({
|
||||
mode : "custom",
|
||||
touch : function(n,e) {onTouch(n,e);},
|
||||
drag : function(e) {onDrag(e);},
|
||||
btn : function(n) {onButton();},
|
||||
});
|
||||
|
||||
g.clearRect(Bangle.appRect);
|
||||
if (timerRunning()) {
|
||||
timerLayout.render();
|
||||
updateTimer();
|
||||
} else {
|
||||
timePickerLayout.render();
|
||||
updateTimePicker();
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 7.6 KiB |
|
@ -2,13 +2,13 @@
|
|||
"id": "smpltmr",
|
||||
"name": "Simple Timer",
|
||||
"shortName": "Simple Timer",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "A very simple app to start a timer.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool",
|
||||
"tags": "tool,alarm,timer",
|
||||
"dependencies": {"scheduler":"type"},
|
||||
"supports": ["BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot.png"}, {"url": "screenshot_2.png"}],
|
||||
"screenshots": [{"url":"screenshot_1.png"}, {"url": "screenshot_2.png"}, {"url": "screenshot_3.png"}, {"url": "screenshot_4.png"}],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"smpltmr.app.js","url":"app.js"},
|
||||
|
|
Before Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.9 KiB |
|
@ -5,7 +5,7 @@
|
|||
"version": "0.01",
|
||||
"description": "Let's you swipe from left to right on any app to return back to the clock face. Please configure in the settings app after installing to activate, since its disabled by default.",
|
||||
"icon": "app.png",
|
||||
"type": "boot",
|
||||
"type": "bootloader",
|
||||
"tags": "tools",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
0.01: Initial version
|
||||
0.02: Do not warn multiple times for the same exceedance
|
||||
0.03: Fix crash
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "widbaroalarm",
|
||||
"name": "Barometer Alarm Widget",
|
||||
"shortName": "Barometer Alarm",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "A widget that can alarm on when the pressure reaches defined thresholds.",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
saveSetting("lastHighWarningTs", 0);
|
||||
}
|
||||
|
||||
if (!alreadyWarned) {
|
||||
if (history3.length > 0 && !alreadyWarned) {
|
||||
// 3h change detection
|
||||
const drop3halarm = setting("drop3halarm");
|
||||
const raise3halarm = setting("raise3halarm");
|
||||
|
|
|
@ -65,6 +65,7 @@ const APP_KEYS = [
|
|||
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports'];
|
||||
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
|
||||
const SUPPORTS_DEVICES = ["BANGLEJS","BANGLEJS2"]; // device IDs allowed for 'supports'
|
||||
const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","textinput","scheduler","notify","locale"]; // values allowed for "type" field
|
||||
const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
|
||||
const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
|
||||
const GRANDFATHERED_ICONS = ["s7clk", "snek", "astral", "alpinenav", "slomoclock", "arrow", "pebble", "rebble"];
|
||||
|
@ -94,6 +95,8 @@ apps.forEach((app,appIdx) => {
|
|||
if (!app.name) ERROR(`App ${app.id} has no name`);
|
||||
var isApp = !app.type || app.type=="app";
|
||||
if (app.name.length>20 && !app.shortName && isApp) ERROR(`App ${app.id} has a long name, but no shortName`);
|
||||
if (app.type && !METADATA_TYPES.includes(app.type))
|
||||
ERROR(`App ${app.id} 'type' is one one of `+METADATA_TYPES);
|
||||
if (!Array.isArray(app.supports)) ERROR(`App ${app.id} has no 'supports' field or it's not an array`);
|
||||
else {
|
||||
app.supports.forEach(dev => {
|
||||
|
@ -135,6 +138,9 @@ apps.forEach((app,appIdx) => {
|
|||
Object.keys(app.dependencies).forEach(dependency => {
|
||||
if (!["type","app"].includes(app.dependencies[dependency]))
|
||||
ERROR(`App ${app.id} 'dependencies' must all be tagged 'type' or 'app' right now`);
|
||||
if (app.dependencies[dependency]=="type" && !METADATA_TYPES.includes(dependency))
|
||||
ERROR(`App ${app.id} 'type' dependency must be one of `+METADATA_TYPES);
|
||||
|
||||
});
|
||||
} else
|
||||
ERROR(`App ${app.id} 'dependencies' must be an object`);
|
||||
|
|
2
core
|
@ -1 +1 @@
|
|||
Subproject commit 6fc78fc39531a43148ae8d515efaeff9404d1daf
|
||||
Subproject commit 32d01b5b3d8e013ca0364671e2352b7b0dd48bb4
|
|
@ -25,7 +25,7 @@ DEVICEINFO = DEVICEINFO.filter(x=>x.id.startsWith("BANGLEJS"));
|
|||
// Set up source code URL
|
||||
(function() {
|
||||
let username = "espruino";
|
||||
let githubMatch = window.location.href.match(/\/(\w+)\.github\.io/);
|
||||
let githubMatch = window.location.href.match(/\/([\w-]+)\.github\.io/);
|
||||
if (githubMatch) username = githubMatch[1];
|
||||
Const.APP_SOURCECODE_URL = `https://github.com/${username}/BangleApps/tree/master/apps`;
|
||||
})();
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
/* Copyright (c) 2022 Bangle.js contributors. See the file LICENSE for copying permission. */
|
||||
/*
|
||||
|
||||
Take a look at README.md for hints on developing with this library.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
var Layout = require("Layout");
|
||||
var layout = new Layout( layoutObject, options )
|
||||
layout.render(optionalObject);
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
var Layout = require("Layout");
|
||||
var layout = new Layout( {
|
||||
|
@ -24,23 +19,22 @@ var layout = new Layout( {
|
|||
g.clear();
|
||||
layout.render();
|
||||
```
|
||||
|
||||
|
||||
layoutObject has:
|
||||
|
||||
* A `type` field of:
|
||||
* `undefined` - blank, can be used for padding
|
||||
* `"txt"` - a text label, with value `label` and `r` for text rotation. 'font' is required
|
||||
* `"txt"` - a text label, with value `label`. 'font' is required
|
||||
* `"btn"` - a button, with value `label` and callback `cb`
|
||||
optional `src` specifies an image (like img) in which case label is ignored
|
||||
Default font is `6x8`, scale 2. This can be overridden with the `font` or `scale` fields.
|
||||
* `"img"` - an image where `src` is an image, or a function which is called to return an image to draw.
|
||||
optional `scale` specifies if image should be scaled up or not
|
||||
* `"custom"` - a custom block where `render(layoutObj)` is called to render
|
||||
* `"h"` - Horizontal layout, `c` is an array of more `layoutObject`
|
||||
* `"v"` - Vertical layout, `c` is an array of more `layoutObject`
|
||||
* A `id` field. If specified the object is added with this name to the
|
||||
returned `layout` object, so can be referenced as `layout.foo`
|
||||
* A `font` field, eg `6x8` or `30%` to use a percentage of screen height
|
||||
* A `font` field, eg `6x8` or `30%` to use a percentage of screen height. Set scale with :, e.g. `6x8:2`.
|
||||
* A `scale` field, eg `2` to set scale of an image
|
||||
* A `r` field to set rotation of text or images (0: 0°, 1: 90°, 2: 180°, 3: 270°).
|
||||
* A `wrap` field to enable line wrapping. Requires some combination of `width`/`height`
|
||||
and `fillx`/`filly` to be set. Not compatible with text rotation.
|
||||
* A `col` field, eg `#f00` for red
|
||||
|
@ -51,34 +45,25 @@ layoutObject has:
|
|||
* A `fillx` int to choose if the object should fill available space in x. 0=no, 1=yes, 2=2x more space
|
||||
* A `filly` int to choose if the object should fill available space in y. 0=no, 1=yes, 2=2x more space
|
||||
* `width` and `height` fields to optionally specify minimum size
|
||||
|
||||
options is an object containing:
|
||||
|
||||
* `lazy` - a boolean specifying whether to enable automatic lazy rendering
|
||||
* `btns` - array of objects containing:
|
||||
* `label` - the text on the button
|
||||
* `cb` - a callback function
|
||||
* `cbl` - a callback function for long presses
|
||||
* `back` - a callback function, passed as `back` into Bangle.setUI
|
||||
|
||||
If automatic lazy rendering is enabled, calls to `layout.render()` will attempt to automatically
|
||||
determine what objects have changed or moved, clear their previous locations, and re-render just those objects.
|
||||
|
||||
Once `layout.update()` is called, the following fields are added
|
||||
to each object:
|
||||
|
||||
* `x` and `y` for the top left position
|
||||
* `w` and `h` for the width and height
|
||||
* `_w` and `_h` for the **minimum** width and height
|
||||
|
||||
|
||||
Other functions:
|
||||
|
||||
* `layout.update()` - update positions of everything if contents have changed
|
||||
* `layout.debug(obj)` - draw outlines for objects on screen
|
||||
* `layout.clear(obj)` - clear the given object (you can also just specify `bgCol` to clear before each render)
|
||||
* `layout.forgetLazyState()` - if lazy rendering is enabled, makes the next call to `render()` perform a full re-render
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
@ -259,12 +244,22 @@ Layout.prototype.render = function (l) {
|
|||
x,y+h-5,
|
||||
x,y+4
|
||||
], bg = l.selected?g.theme.bgH:g.theme.bg2;
|
||||
g.setColor(bg).fillPoly(poly).setColor(l.selected ? g.theme.fgH : g.theme.fg2).drawPoly(poly);
|
||||
g.setColor(bg).fillPoly(poly).setColor(l.selected ? g.theme.fgH : g.theme.fg2).drawPoly(poly);
|
||||
if (l.col!==undefined) g.setColor(l.col);
|
||||
if (l.src) g.setBgColor(bg).drawImage("function"==typeof l.src?l.src():l.src, l.x + 10 + (0|l.pad), l.y + 8 + (0|l.pad));
|
||||
else g.setFont("6x8",2).setFontAlign(0,0,l.r).drawString(l.label,l.x+l.w/2,l.y+l.h/2);
|
||||
if (l.src) g.setBgColor(bg).drawImage(
|
||||
"function"==typeof l.src?l.src():l.src,
|
||||
l.x + l.w/2,
|
||||
l.y + l.h/2,
|
||||
{scale: l.scale||undefined, rotate: Math.PI*0.5*(l.r||0)}
|
||||
);
|
||||
else g.setFont(l.font||"6x8:2").setFontAlign(0,0,l.r).drawString(l.label,l.x+l.w/2,l.y+l.h/2);
|
||||
}, "img":function(l){
|
||||
g.drawImage("function"==typeof l.src?l.src():l.src, l.x + (0|l.pad), l.y + (0|l.pad), l.scale?{scale:l.scale}:undefined);
|
||||
g.drawImage(
|
||||
"function"==typeof l.src?l.src():l.src,
|
||||
l.x + l.w/2,
|
||||
l.y + l.h/2,
|
||||
{scale: l.scale||undefined, rotate: Math.PI*0.5*(l.r||0)}
|
||||
);
|
||||
}, "custom":function(l){
|
||||
l.render(l);
|
||||
},"h":function(l) { l.c.forEach(render); },
|
||||
|
@ -365,7 +360,9 @@ Layout.prototype.update = function() {
|
|||
l._w = m.width; l._h = m.height;
|
||||
}
|
||||
}, "btn": function(l) {
|
||||
var m = l.src?g.imageMetrics("function"==typeof l.src?l.src():l.src):g.setFont("6x8",2).stringMetrics(l.label);
|
||||
if (l.font && l.font.endsWith("%"))
|
||||
l.font = "Vector"+Math.round(g.getHeight()*l.font.slice(0,-1)/100);
|
||||
var m = l.src?g.imageMetrics("function"==typeof l.src?l.src():l.src):g.setFont(l.font||"6x8:2").stringMetrics(l.label);
|
||||
l._h = 16 + m.height;
|
||||
l._w = 20 + m.width;
|
||||
}, "img": function(l) {
|
||||
|
|
|
@ -1,39 +1,65 @@
|
|||
/* Utility functions that use the 'locale' module so can produce text
|
||||
in the currently selected language. */
|
||||
// module "date_utils"
|
||||
//
|
||||
// Utility functions that use the "locale" module so can produce
|
||||
// date-related text in the currently selected language.
|
||||
//
|
||||
// Some functions have a "firstDayOfWeek" parameter.
|
||||
// Most used values are:
|
||||
// - 0/undefined --> Sunday
|
||||
// - 1 --> Monday
|
||||
// but you can start the week from any day if you need it.
|
||||
//
|
||||
// Some functions have an "abbreviated" parameter.
|
||||
// It supports the following 3 values:
|
||||
// - 0/undefined --> get the full value, without abbreviation (eg.: "Monday", "January", etc.)
|
||||
// - 1 --> get the short value (eg.: "Mon", "Jan", etc.)
|
||||
// - 2 --> get only the first char (eg.: "M", "J", etc.)
|
||||
//
|
||||
|
||||
/** Return the day of the week (0=Sunday)
|
||||
short==0/undefined -> "Sunday"
|
||||
short==1 -> "Sun"
|
||||
*/
|
||||
exports.getDOW = (dow, short) => require("locale").dow({getDay:()=>dow},short);
|
||||
|
||||
/** Return the month (1=January)
|
||||
short==0/undefined -> "January"
|
||||
short==1 -> "Jan"
|
||||
*/
|
||||
exports.getMonth = (month, short) => require("locale").month({getMonth:()=>month-1},short);
|
||||
|
||||
/** Return all 7 days of the week as an array ["Sunday","Monday",...].
|
||||
short==0/undefined -> ["Sunday",...
|
||||
short==1 -> ["Sun",...
|
||||
short==2 -> ["S",...
|
||||
*/
|
||||
exports.getDOWs = (short) => {
|
||||
var locale = require("locale");
|
||||
var days = [];
|
||||
for (var i=0;i<7;i++)
|
||||
days.push(locale.dow({getDay:()=>i},short).slice(0,(short==2)?1:100));
|
||||
return days;
|
||||
/**
|
||||
* @param {int} i The index of the day of the week (0 = Sunday)
|
||||
* @param {int} abbreviated
|
||||
* @returns The localized name of the i-th day of the week
|
||||
*/
|
||||
exports.dow = (i, abbreviated) => {
|
||||
var dow = require("locale").dow(new Date(((i || 0) + 3.5) * 86400000), abbreviated).slice(0, (abbreviated == 2) ? 1 : 100);
|
||||
return abbreviated == 2 ? dow.toUpperCase() : dow;
|
||||
}
|
||||
|
||||
/** Return all 12 months as an array ["January","February",...]
|
||||
short==0/undefined -> ["January",...
|
||||
short==1 -> ["Jan",...
|
||||
*/
|
||||
exports.getMonths = (short) => {
|
||||
/**
|
||||
* @param {int} firstDayOfWeek 0/undefined -> Sunday,
|
||||
* 1 -> Monday
|
||||
* @param {int} abbreviated
|
||||
* @returns All 7 days of the week (localized) as an array
|
||||
*/
|
||||
exports.dows = (firstDayOfWeek, abbreviated) => {
|
||||
var dows = [];
|
||||
var locale = require("locale");
|
||||
for (var i = 0; i < 7; i++) {
|
||||
dows.push(exports.dow(i + (firstDayOfWeek || 0), abbreviated))
|
||||
}
|
||||
return abbreviated == 2 ? dows.map(dow => dow.toUpperCase()) : dows;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {int} i The index of the month (1 = January)
|
||||
* @param {int} abbreviated
|
||||
* @returns The localized name of the i-th month
|
||||
*/
|
||||
exports.month = (i, abbreviated) => {
|
||||
var month = require("locale").month(new Date((i - 0.5) * 2628000000), abbreviated).slice(0, (abbreviated == 2) ? 1 : 100);
|
||||
return abbreviated == 2 ? month.toUpperCase() : month;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {int} abbreviated
|
||||
* @returns All 12 months (localized) as an array
|
||||
*/
|
||||
exports.months = (abbreviated) => {
|
||||
var months = [];
|
||||
for (var i=0;i<12;i++)
|
||||
months.push(locale.month({getMonth:()=>i},short));
|
||||
return months;
|
||||
}
|
||||
var locale = require("locale");
|
||||
for (var i = 1; i <= 12; i++) {
|
||||
months.push(locale.month(new Date((i - 0.5) * 2628000000), abbreviated).slice(0, (abbreviated == 2) ? 1 : 100));
|
||||
}
|
||||
return abbreviated == 2 ? months.map(month => month.toUpperCase()) : months;
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ SRCBMP=$SRCDIR/`basename $SRCJS .js`.bmp
|
|||
echo "TEST $SRCJS ($SRCBMP)"
|
||||
|
||||
cat ../../modules/Layout.js > $TESTJS
|
||||
echo 'Bangle = { setUI : function(){} };BTN1=0;process.env = process.env;process.env.HWVERSION=2;' >> $TESTJS
|
||||
echo 'Bangle = { setUI : function(){}, appRect:{x:0,y:0,w:176,h:176,x2:175,y2:175} };BTN1=0;process.env = process.env;process.env.HWVERSION=2;' >> $TESTJS
|
||||
echo 'g = Graphics.createArrayBuffer(176,176,4);' >> $TESTJS
|
||||
cat $SRCJS >> $TESTJS || exit 1
|
||||
echo 'layout.render()' >> $TESTJS
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
var BTN2 = 1, BTN3=2;
|
||||
process.env = process.env;process.env.HWVERSION=1;
|
||||
g = Graphics.createArrayBuffer(240,240,4);
|
||||
Bangle.appRect = {x:0,y:0,w:240,h:240,x2:239,y2:239};
|
||||
|
||||
var layout = new Layout({ type: "v", c: [
|
||||
{type:"txt", font:"6x8", label:"A test"},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
var BTN2 = 1, BTN3=2;
|
||||
process.env = process.env;process.env.HWVERSION=1;
|
||||
g = Graphics.createArrayBuffer(240,240,4);
|
||||
Bangle.appRect = {x:0,y:0,w:240,h:240,x2:239,y2:239};
|
||||
|
||||
var layout = new Layout({ type: "v", c: [
|
||||
{type:"txt", font:"6x8", label:"A test"},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
var BTN2 = 1, BTN3=2;
|
||||
process.env = process.env;process.env.HWVERSION=1;
|
||||
g = Graphics.createArrayBuffer(240,240,4);
|
||||
Bangle.appRect = {x:0,y:0,w:240,h:240,x2:239,y2:239};
|
||||
|
||||
/* When displaying OSD buttons on Bangle.js 1 we should turn
|
||||
the side buttons into 'soft' buttons and then use the physical
|
||||
|
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |