Skip to content

swipeinv: (WIP) Add inversion of drag events. Refactor to hijack and modify the events #3815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 20 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/swipeinv/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
0.01: New app!
0.02: Minor code improvements
0.03: (WIP) Add inversion of drag events. Add per app, per direction setting.
Refactor to hijack and modify the events instead of the handling of them.
This should add compatibility with most apps, even if Bangle.setUI is not
used.
15 changes: 9 additions & 6 deletions apps/swipeinv/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@

Inverts swipe direction globally or per app, see settings. If global inversion is enabled, you can unselect the inversion per app and vice versa.

## Limitations
Can invert swipe as well as drag events. This can be fine tuned for each inverted app in the settings.

Swipe Inversion can only invert directions on apps that use `Bangle.setUI` to set up swipes. Swipes set up with `Bangle.on("swipe", ...)` is currently not managed.
## Limitations

Swiping behavior that uses the `drag` event is not altered either.
Swipe Inversion must sit before other swipe and drag event listeners so they don't run before the event is inverted. If a listener were to be prepended to the list of listeners after Swipe Inversion was, that listener will act on the non-inverted event.

## TODO

- Try to handle swipes from `Bangle.on("swipe", ...)`
- alternatively refactor apps using that to only use `Bangle.setUI` for setting up swipes.
- Think about how to accommodate e.g. `touch` or `back` handlers set up in `Layout` library calls. They are removed if we refactor some `Bangle.on("swipe", ...)` to `Bangle.setUI`. (Is it maybe resolved if we call `Bangle.setUI` before the `Layout` call? - have not tested that yet.)
- Add bootloader apps and widgets to the list of apps that can be individually toggled in settings?
- How? Right now we look at `global.__FILE__` to find the active app in order to determine which events to invert. That doesn't work for widgets and bootcode.
- In fact they will probably be inverted along with the currently running app.
- Refactor to invert at time of registering the event listeners?
- This would make it so `swipeinv` does not depend on being first in the call list of event listeners.
- Some work towards this was done in [thyttan@5cbb72e](https://github.com/thyttan/BangleApps/commit/5cbb72ee55f7fb7d335ffba228575a862a0ae612) but it doesn't work yet.

## Requests

Expand All @@ -25,3 +27,4 @@ nxdefiant

## Contributors

thyttan
59 changes: 48 additions & 11 deletions apps/swipeinv/boot.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,56 @@
{
const settings = Object.assign({
const SETTINGS = Object.assign({
global: false,
apps: []
}, require("Storage").readJSON("swipeinv.json", true) || {});

if (settings.global || settings.apps.length > 0) {
const setURIOrig = Bangle.setUI;
Bangle.setUI = (mode, callback) => {
if (typeof mode === "object" && mode.swipe) {
if (settings.global ^ settings.apps.includes(global.__FILE__)) {
const origSwipeCb = mode.swipe;
mode.swipe = (dirLR, dirUD) => origSwipeCb(dirLR*-1, dirUD*-1);
const CLOCK_APP_ID = require("Storage").readJSON("setting.json",true).clock.split(".")[0];

let getAppIdFromCurrentFile = ()=> {
"ram"
if (!global.__FILE__ || global.__FILE__===".bootcde") {
return CLOCK_APP_ID;
} else {return global.__FILE__.split(".")[0];}
}

setTimeout(() => { // Timeout so we prepend listeners late, hopefully after all other listerners were added.
if (SETTINGS.global || Object.keys(SETTINGS.apps).length > 0) {

let swipeInverter = (dirLR, dirUD, obj) => {
"ram"
const APP_ID = getAppIdFromCurrentFile();
if (SETTINGS.global ^ Object.keys(SETTINGS.apps).includes(APP_ID)) {
if (!(obj && obj.inverted)) {
E.stopEventPropagation();
obj = Object.assign({inverted:true}, obj);

if (SETTINGS.global ^ (SETTINGS.apps[APP_ID]&&SETTINGS.apps[APP_ID].swipeH)) {dirLR *= -1;}
if (SETTINGS.global ^ (SETTINGS.apps[APP_ID]&&SETTINGS.apps[APP_ID].swipeV)) {dirUD *= -1;}

Bangle.emit("swipe", dirLR, dirUD, obj)
}
}
}
return setURIOrig(mode, callback);
};
}

let dragInverter = (e) => {
"ram"
const APP_ID = getAppIdFromCurrentFile();
if (SETTINGS.global ^ Object.keys(SETTINGS.apps).includes(APP_ID)) {
if (!e.inverted) {
E.stopEventPropagation();
e.inverted = true;

if (SETTINGS.global ^ (SETTINGS.apps[APP_ID]&&SETTINGS.apps[APP_ID].dragH)) {e.dx *= -1;}
if (SETTINGS.global ^ (SETTINGS.apps[APP_ID]&&SETTINGS.apps[APP_ID].dragV)) {e.dy *= -1;}

Bangle.emit("drag", e);
}
}
}

Bangle.prependListener("swipe", swipeInverter);
Bangle.prependListener("drag", dragInverter);

}
}, 0)
}
2 changes: 1 addition & 1 deletion apps/swipeinv/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "Swipe inversion",
"shortName":"Swipe inv.",
"icon": "app.png",
"version": "0.02",
"version": "0.03",
"description": "Inverts swipe direction globally or per app, see settings. If global inversion is enabled, you can unselect the inversion per app and vice versa.",
"readme":"README.md",
"type": "bootloader",
Expand Down
58 changes: 48 additions & 10 deletions apps/swipeinv/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,45 @@
// Load settings
const settings = Object.assign({
global: false,
apps: []
apps: {}
}, require("Storage").readJSON(FILE, true) || {});

function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}

// Convert settings when coming from ver < 4
function convertAppsArrayToObject() {
if (Array.isArray(settings.apps)) {
let newObject = {};
settings.apps.forEach((source)=>{
let idExtractedFromSource = source.split(".")[0];
newObject[idExtractedFromSource] = {};
})
settings.apps = newObject;
}
}
convertAppsArrayToObject();

function appMenu() {
let menu = {
"" : { "title" : /*LANG*/"Swipe inversion apps" },
"< Back" : () => { writeSettings(); mainMenu();}
};
require("Storage").list(/\.info$/).map(app=>require("Storage").readJSON(app,1)).filter(app => app.type === "app" || !app.type).sort((a,b) => {
require("Storage").list(/\.info$/).map(app=>require("Storage").readJSON(app,1)).filter(app => app.type==="app" || app.type==="clock" || app.type==="launch" || !app.type).sort((a,b) => {
if (a.name<b.name) return -1;
if (a.name>b.name) return 1;
return 0;
}).forEach(app => {
menu[app.name] = {
value: settings.apps.includes(app.src),
value: Object.keys(settings.apps).includes(app.id),
onchange: v => {
if (v) {
settings.apps.push(app.src);
if (!settings.apps[app.id] || Object.keys(settings.apps[app.id]).length===0) {
settings.apps[app.id] = {"name":app.name, "swipeH":true, "swipeV":true, "dragH":true, "dragV":true};
}
} else {
const idx = settings.apps.indexOf(app.src);
if (idx !== -1) {
settings.apps.splice(idx, 1);
}
if (settings.apps[app.id]) {delete settings.apps[app.id];}
}
}
};
Expand All @@ -38,7 +50,7 @@
}

function mainMenu() {
E.showMenu({
let menu = {
"" : { "title" : /*LANG*/"Swipe inversion" },
"< Back" : () => back(),

Expand All @@ -50,8 +62,34 @@
}
},

/*LANG*/'Select apps': () => appMenu()
/*LANG*/'Select apps': () => appMenu(),
};

Object.keys(settings.apps).forEach((appID) => {
// Create a sub menu and show it.
let subMenu = {
"" : { "title" : /*LANG*/"Tune"+" "+appID },
"< Back" : () => { writeSettings(); mainMenu();}
}
let subMenuEntries = [{name:"Swipe Horizontal", id:"swipeH"}, {name:"Swipe Vertical", id:"swipeV"}, {name:"Drag Horizontal", id:"dragH"}, {name:"Drag Vertical", id:"dragV"}];
subMenuEntries.forEach((setting)=>{
if (!Object.keys(settings.apps).includes(appID)) {settings.apps[appID] = {"swipeH":true, "swipeV":true, "dragH":true, "dragV":true}}
subMenu[setting.name] = {
value: settings.apps[appID][setting.id],
onchange: v => {
if (v) {
settings.apps[appID][setting.id] = true;
} else {
settings.apps[appID][setting.id] = false;
}
}
};
})

menu[settings.apps[appID].name] = ()=>E.showMenu(subMenu);
});

E.showMenu(menu);
}

mainMenu();
Expand Down