Tweak layouts and add data for release

pull/2818/head
Philip Andresen 2023-06-12 22:25:18 -04:00
parent 3682ee4e79
commit a398522e76
12 changed files with 141 additions and 15 deletions

1
apps/wrkmem/Changelog Normal file
View File

@ -0,0 +1 @@
1.00: Implement Working Memory Helper app

61
apps/wrkmem/README.md Normal file
View File

@ -0,0 +1,61 @@
# Working Memory Helper
Human brains keep track of what they are doing in a conceptual space known as "working memory". Older adults and people
of all ages with ADHD often struggle to maintain information in their working memories, causing them to forget what
they were doing only moments after deciding to do it. One excellent way to combat this symptom is to externalize your
working memory.
This app doesn't completely externalize and replace working memory, but it does act as a prosthesis for the task
management aspect of working memory. The workflow looks something like this:
1. Decide to do something. (If you can't get this far on your own, this app is not gonna help.)
2. Immediately enter a brief prompt in the app as a "task". For example, if you were going to take out the trash,
you might write "Trash". If you were going to take your car to the mechanic, you might write "car", or "mechanic". It
doesn't have to remind you what you were doing a week from now, only a minute or so, so it can be very simple / brief.
3. Thirty seconds after you enter the task into the app, your device will vibrate and ask you if you are on task, or if
you got distracted.
1. If you are on task, hit "On task" and the app will wait a little longer before reminding you again.
2. If you got distracted, hit "distracted" and the app will remind you a little sooner next time.
4. Continue getting reminders from your watch at various intervals until you complete the task, then tell the app the
task is complete. Repeat this process for every single thing you do until you die, basically.
![screenshot](screenshot.png) ![screenshot](screenshot2.png) ![screenshot](screenshot3.png) ![screenshot](screenshot4.png)
![screenshot](screenshot5.png) ![screenshot](screenshot6.png)
## Requirements
You must have some kind of keyboard library available in order to enter task descriptions on your device. This app is
only supported on BangleJS2
## Styling
This app attempts to match whatever theme your Bangle watch is using. Styling options are not currently available
beyond that, but tweaking some things will eventually be possible, like the size and presence of swipe hints, whether
or not task text is outlined, etc.
## Task settings
You can edit the settings of any individual task. You can rename the task, restart (un-complete) the task, or change
some of the reminder cadence settings. As far as cadence, there are a couple that warrante explanation:
#### Interval
This is the base reminder interval for your task. If it is 30, your first reminder will be after 30 seconds.
#### Incremental Backoff
Incremental backoff is a strategy for timing the reminder notifications you get based on how well you stay on task.
Each time you affirm that you are "on task", incremental backoff means it will wait longer before reminding you again.
Similarly each time you affirm that you are "distracted" the incremental backoff will wait less time before reminding
you again. The exact intervals are multiples of the base interval. For a task with a base interval of 30 seconds, the
second reminder would be after 60 seconds. The third after 120 seconds, etc. Then if you got distracted it would go
back to 60, then 30, then 15. Typically the interval will never go below 1/2 of your base interval.
If you disable Incremental Backoff, you will be reminded once every base interval no matter what you do. This can be
handy if you are having trouble staying on task when the intervals get too long with incremental backoff.
## Controls
A large focus of this app was making clear affordances for the user interface. Anything that can be pressed should look
like a button, however you may notice some small arrows and text on the sides / top / bottom of the screen in some
cases. These hints are there to tell you that you can swipe across the screen to perform additional actions.
Swipe your finger anywhere on the screen in the direction the arrow is pointing to use the listed function.
## Known issues
The clock is not super-duper accurate because it only updates when the screen refreshes (which can be 30 seconds or 5
minutes apart). I put it on there, though, because it is more useful to be there and lagging by 30+ seconds than to be
not there at all. I plan to fix this problem eventually but it's secondary to the main functions of the app at the
moment.

1
apps/wrkmem/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgYJGgVJkmQDZt/////wRfgVP8g1OiQRByQRNyQRCoAMHgP8wEAk5GBAAPJgET/IREh//yVJCAYABkmf/8gCIc///yNIQAD/gCB8ARFAAQmBkmTA4YREGoIvCJQIABHYY1EhIHB/DFDCgItB/IREgM//x3Byg2BiUAggRHLINAgsvFAOkC4JrGCIKtBmZCC/2IR4IRFLILRBCIf/6MAgf/8gREWYUz/J8CCIMAv4REAoIRBg1CpMmv/6GQNPDoQXGHAMA54RCF4Y7HAAOvCIZTCCIRfECIp3BCLpHDCJcJNYJ9CCJdSpaPDCJUDe4LFCCJ2odIIRN9USeogRK9C/DCLDFDCIzFEdIoRHfYwFDsmESodPCIgXFqVECId/CIpNEGohTFL4WSCI8T/8kCIvyVoIRFz7jFgE//1ICIsE//5CAkAg4IBDQJrCgLbB5ARFDQI+BwEJCgNJfwIsBAAsfCQOSpMkyYFB+QQGgECv4MBAAf8YQYAFhIRFXggAGk4QD5J6FAAxHCySVBAHQ="))

View File

@ -6,7 +6,24 @@ E.showMessage("Loading ... ");
Bangle.loadWidgets();
Bangle.drawWidgets();
const allTasks = [];
const localTaskFile = "wrkmem.json";
let savedData = require("Storage")
.readJSON(localTaskFile, true);
if (!savedData) {
savedData = {
tasks: [], keyboardAlpha: undefined
}
}
let currentMenu;
function save() {
require("Storage")
.writeJSON("wrkmem.json", savedData);
}
const allTasks = savedData.tasks;
const nudgeManager = {
activeTask : null, taskTimeout: null, responseTimeout: null, interrupt: () => {
if (this.taskTimeout) clearTimeout(this.taskTimeout);
@ -16,7 +33,8 @@ const nudgeManager = {
if (this.responseTimeout) clearTimeout(this.responseTimeout);
if (this.taskTimeout) clearTimeout(this.taskTimeout);
this.activeTask = task;
const time = task.incrementalBackoffSet[task.backoffIndex] * task.interval * 1000;
const backoffIndex = task.useBackoff ? task.backoffIndex : 1;
const time = task.incrementalBackoffSet[backoffIndex] * task.interval * 1000;
this.taskTimeout = setTimeout(nudgeFn, time);
}, queueResponseTimeout: (defaultFn) => {
// This timeout shouldn't be set if we've queued a response timeout, but we clear it anyway.
@ -254,23 +272,25 @@ function createMenu(options) {
function setMenu(menu) {
g.clearRect(Bangle.appRect);
g.reset();
currentMenu = menu;
menu.render();
menu.setUI();
}
let keyboardAlpha, keyboardNum;
let keyboardAlpha;
if (textInput.generateKeyboard) {
const charSet = textInput.createCharSet("ABCDEFGHIJKLMNOPQRSTUVWXYZ", ["spc", "ok", "del"]);
keyboardAlpha = textInput.generateKeyboard(charSet);
// keyboardNum = textInput.generateKeyboard([["1", "2", "3", "4", "5", "6", "7", "8", "9"], ["0"], "del", "ok"]);
keyboardAlpha = textInput.generateKeyboard(charSet)
}
function newTask(initialText) {
nudgeManager.interrupt();
initialText = initialText || "";
textInput.input({text: initialText, keyboardMain: keyboardAlpha})
.then(text => {
const task = createTask(text)
allTasks.unshift(task);
save();
startTask(task);
})
}
@ -290,8 +310,8 @@ function nudge(task) {
});
const nudgeMenu = createMenu({
title : "Are you on task?", titleFont: "6x8", items: [
{text: task.text, size: 1}, {text: "On Task", size: 2, callback: () => affirmOnTask(task)}, {
text: "Distracted", size: 2, callback: () => affirmDistracted(task)
{text: task.text, size: 1}, {text: "On Task", size: 1, callback: () => affirmOnTask(task)}, {
text: "Distracted", size: 1, callback: () => affirmDistracted(task)
}
], isHorizontal: false
});
@ -312,14 +332,15 @@ function affirmDistracted(task) {
}
function concludeUnresponsive(task) {
Bangle.buzz(250, 1).then(() => Bangle.setLCDPower(true));
Bangle.buzz(250, 1)
.then(() => Bangle.setLCDPower(true));
task.unresponsiveCount++;
task.backoffIndex = Math.max(0, task.backoffIndex - 1);
nudgeManager.queueResponseTimeout(() => concludeUnresponsive(task))
}
function showTempMessage(text, title, thenFn) {
E.showMessage(text,{title});
E.showMessage(text, {title});
setTimeout(() => {
Bangle.setLocked(true);
thenFn();
@ -335,6 +356,7 @@ function completeTask(task) {
task.complete = true;
removeTask(task, allTasks);
allTasks.push(task);
save();
setMenu(getTaskMenu(task));
}
@ -342,6 +364,7 @@ function restartTask(task) {
task.complete = false;
removeTask(task, allTasks);
allTasks.unshift(task);
save();
startTask(task);
}
@ -367,18 +390,27 @@ function createTask(text) {
interval : 30,
backoffIndex : 1,
incrementalBackoffSet,
complete : false
complete : false,
useBackoff: true
};
}
function getTaskMenu(task) {
const d = new Date();
const h = d.getHours(), m = d.getMinutes();
const time = h + ":" + m.toString().padStart(2,0);
const taskSwipeControls = [
createSwipeControl(SWIPE.LEFT, "Menu", () => setMenu(mainMenu)),
createSwipeControl(SWIPE.RIGHT, "New Task", newTask),
createSwipeControl(SWIPE.LEFT, "Menu", () => {
setMenu(mainMenu);
nudgeManager.interrupt();
}), createSwipeControl(SWIPE.RIGHT, "New Task", newTask),
];
const items = [];
if (task.complete) {
taskSwipeControls.push(createSwipeControl(SWIPE.UP, "Restart", () => restartTask(task)));
taskSwipeControls.push(createSwipeControl(SWIPE.DOWN,
"Task List",
() => showTaskList(allTasks, () => startTask(task))));
items.push({text: task.text + " completed!", size: 1});
const nextTask = getNextTask(task, allTasks);
if (nextTask) {
@ -390,11 +422,12 @@ function getTaskMenu(task) {
});
}
} else {
items.push({text: task.text})
taskSwipeControls.push(createSwipeControl(SWIPE.DOWN, "Complete", () => completeTask(task)))
items.push({text: task.text, size: 2})
taskSwipeControls.push(createSwipeControl(SWIPE.UP, "Complete", () => completeTask(task)))
taskSwipeControls.push(createSwipeControl(SWIPE.DOWN, "Edit Task", () => editTask(task, () => startTask(task))))
}
return createMenu({
items, spaceAround: 0, spaceBetween: 0, swipeControls: taskSwipeControls
items, spaceAround: 0, spaceBetween: 0, swipeControls: taskSwipeControls, title: time
});
}
@ -419,6 +452,7 @@ function st5(fn) {
}
function editTask(task, backFn) {
nudgeManager.interrupt();
let editMenu = [];
editMenu.push({title: "Rename", onchange: st5(() => renameTask(task, () => editTask(task, backFn)))});
if (task.complete) {
@ -427,6 +461,8 @@ function editTask(task, backFn) {
} else {
editMenu.push({title: "Resume Task", onchange: st5(() => startTask(task))})
}
editMenu.push({ title:"Interval", value: task.interval, min:10, step: 10, onchange: v => task.interval = v })
editMenu.push({ title:"Incremental Backoff", value: !!task.useBackoff, onchange: v => task.useBackoff = v })
editMenu[""] = {title: task.text, back: backFn};
E.showMenu(editMenu);
}
@ -435,6 +471,7 @@ function renameTask(task, backFn) {
return textInput.input({text: task.text, keyboardMain: keyboardAlpha})
.then(text => {
task.text = text
save();
backFn();
})
}

BIN
apps/wrkmem/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

26
apps/wrkmem/metadata.json Normal file
View File

@ -0,0 +1,26 @@
{
"id" : "wrkmem",
"name" : "Working Memory Helper",
"version" : "1.00",
"description" : "Externalize your working memory to help stay on task.",
"dependencies" : {"textinput": "type"},
"icon" : "icon.png",
"type" : "app",
"tags" : "tool",
"supports" : ["BANGLEJS2"],
"screenshots" : [
{"url": "screenshot.png"},
{"url": "screenshot2.png"},
{"url": "screenshot3.png"},
{"url": "screenshot4.png"},
{"url": "screenshot5.png"},
{"url": "screenshot6.png"}
],
"readme" : "README.md",
"allow_emulator": false,
"storage" : [
{"name": "wrkmem.app.js", "url": "app.js"},
{"name": "wrkmem.img", "url": "app-icon.js", "evaluate": true}
],
"data" : [{"name": "wrkmem.json"}]
}

BIN
apps/wrkmem/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
apps/wrkmem/screenshot2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
apps/wrkmem/screenshot3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
apps/wrkmem/screenshot4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
apps/wrkmem/screenshot5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
apps/wrkmem/screenshot6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB