Merge pull request #3115 from 7kasper/master

Presentor V0.08 -> V0.14
master
Gordon Williams 2023-11-30 09:38:16 +00:00 committed by GitHub
commit e5ebb03e13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 169 additions and 142 deletions

View File

@ -6,3 +6,9 @@
0.06: Initial internal git(hub) release. Added icon and such.
0.07: Begin work on presentation parts.
0.08: Presentation parts!
0.09: Code cleanup and windows 11 support.
0.10: Bugfixes.
0.11: Fix mouse move getting stuck.
0.12: Added support for mouse dragging action (click then drag).
0.13: Removed mouse dragging (too buggy/unuseful).
0.14: Bugfix and add pointer mode. Also added default screen when no parts are there.

View File

@ -1,2 +1,80 @@
# Presentor
Use your Bangle to present!
This app basically turns your BangleJS watch into a presentor tool.
Very useful for presentations or when you are teaching a lesson.
### Features:
+ Control your (powerpoint) slides by swiping left and right.
+ See pre-programmed presentation notes by swiping up and down.
+ See if you are on time in your presentation.
+ Get notified if you go over-time for a certain subject.
+ Control cursor or spotlight in mouse mode (pressing the button).
### Usage:
Here is a step by step guide (thanks mu1)
1. If bluetooth connected to PC/laptop (check in bluetooth settings):
Select Bangle.js > Remove Device
2. On your smartphone, in "Gadgetbridge", close connection by long-pressing "Bangle.js"
3. Open banglejs.com/apps and select "Connect" (right upper corner)
4. Select Bangle.js in blue bar in pop-up window and select "Connect"
5. Search "Presentor" app and select diskette icon
6. Select "Clear" to start making new notes.
7. Fill the subjects, times in minutes (seconds optional) and Notes (optional)
The subject/times are on your watch displayed in a tall font, the notes in a very small font
Note: If you don't fill in any notes you can still save and the presentor will be for input only and not give any timing details.
8. Select "Save" and exit the pop-up window
9. Select "Disconnect" (right upper corner)
10. Open the Presentor app. This will start the correct HID service as well.
11. Open "Bluetooth & other devices" screen on PC/laptop and select "+" to add a device
12. Select "Bluetooth" and select Bangle.js
If "Try connecting your device again." is shown, switch PC/laptop bluetooth off and on again
13. Start your presentation.
- Swipe up to start timer for first subject
- Swipe down to pause
- Push button to switch between mouse mode and timer/presentation mode
14. Timer function
- Next subject: Swipe up
- Previous subject: Swipe down
- Pause timer: Swipe down to begin
15. Presentation function
- Next presentation slide: Swipe right or just touch
- Previous presentation slide: Swipe left
16. Mouse mode
- Swipe to move pointer over the presentation screen
- Tap to left click.
- Hold longer to right click.
17. Holding mode
In timer/presentation mode, hold one finger on your Bangle screen, point your arm foreward
- Mouse up/down on screen: Tilt your hand up/down
- Mouse left/right on screen: Rotate your hand counterclockwise/clockwise
18. Spotlight mode
- Dubble press button to go into presentor mode.
- Allows to move mouse but no clicking.
- Presses SHIFT+F10 on start (you can use app such as powertoys to set spotlight to this combo)
- Presses F10 at the end (will dis-engage spotlight for instance in powertoys)
19. End of presentation?
Switch bluetooth PC/laptop off or see step 1
20. Re-activate smartphone bluetooth (incl. Gadgetbridge)
### Creator
[7kasper](https://github.com/7kasper)
Issues or feature requests are welcome on my [Github](https://github.com/7kasper/BangleApps) or on the [Espruino Forums](https://forum.espruino.com/conversations/371443/)!

View File

@ -1,80 +1,12 @@
// Presentor by 7kasper (Kasper Müller)
// Version 3.0
const SpecialReport = new Uint8Array([
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, // REPORT_ID (1)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x05, // USAGE_MAXIMUM (Button 5)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0x05, 0x0c, // USAGE_PAGE (Consumer Devices)
0x0a, 0x38, 0x02, // USAGE (AC Pan)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x02, // REPORT_ID (2)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x73, // USAGE_MAXIMUM (Keyboard F24)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x73, // LOGICAL_MAXIMUM (115)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
]);
const MouseButton = {
NONE : 0,
LEFT : 1,
RIGHT : 2,
MIDDLE : 4,
BACK : 8,
FORWARD: 16
};
const kb = require("ble_hid_keyboard");
// Version 0.14
// Imports
const bt = require("ble_hid_combo");
const Layout = require("Layout");
const Locale = require("locale");
// App Layout
let mainLayout = new Layout({
'type': 'v',
filly: 1,
@ -153,18 +85,20 @@ let lasty = 0;
// Mouse states
let holding = false;
let trackPadMode = false;
let focusMode = false;
// Timeout IDs.
let timeoutId = -1;
let timeoutHolding = -1;
let timeoutDraw = -1;
let timeoutSendMouse = -1;
let homeRoll = 0;
let homePitch = 0;
let mCal = 0;
let mttl = 0;
let cttl = 0;
let bttl = 0;
// BT helper.
let clearToSend = true;
@ -174,7 +108,7 @@ let ptimers = [];
function delay(t, v) {
return new Promise((resolve) => {
setTimeout(resolve, t)
setTimeout(resolve, t);
});
}
@ -266,6 +200,11 @@ function drawMain() {
function doPPart(r) {
pparti += r;
if (settings.pparts.length == 0) {
mainLayout.Subject.label = 'PRESENTOR';
mainLayout.Notes.label = '';
return;
}
if (pparti < 0) {
pparti = -1;
mainLayout.Subject.label = 'PAUSED';
@ -289,56 +228,34 @@ function doPPart(r) {
drawMainFrame();
}
NRF.setServices(undefined, { hid : SpecialReport });
// TODO: figure out how to detect HID.
// Turn on Bluetooth as presentor.
NRF.setServices(undefined, { hid : bt.report });
NRF.on('HID', function() {
HIDenabled = true;
if (!HIDenabled) {
Bangle.buzz(200);
HIDenabled = true;
}
});
function moveMouse(x,y,b,wheel,hwheel,callback) {
if (!HIDenabled) return;
if (!b) b = 0;
if (!wheel) wheel = 0;
if (!hwheel) hwheel = 0;
NRF.sendHIDReport([1,b,x,y,wheel,hwheel,0,0], function() {
if (callback) callback();
});
}
//
NRF.setAdvertising([
{}, // include original Advertising packet
[ // second packet containing 'appearance'
2, 1, 6, // standard Bluetooth flags
3,3,0x12,0x18, // HID Service
3,0x19,0xCA,0x03 // Appearance: Presentation Remote
]
]);
// function getSign(x) {
// return ((x > 0) - (x < 0)) || +x;
// }
function scroll(wheel,hwheel,callback) {
moveMouse(0,0,0,wheel,hwheel,callback);
}
// Single click a certain button (immidiatly release).
function clickMouse(b, callback) {
if (!HIDenabled) return;
NRF.sendHIDReport([1,b,0,0,0,0,0,0], function() {
NRF.sendHIDReport([1,0,0,0,0,0,0,0], function() {
if (callback) callback();
});
});
}
function pressKey(keyCode, modifiers, callback) {
if (!HIDenabled) return;
if (!modifiers) modifiers = 0;
NRF.sendHIDReport([2, modifiers,0,keyCode,0,0,0,0], function() {
NRF.sendHIDReport([2,0,0,0,0,0,0,0], function() {
if (callback) callback();
});
});
}
function handleAcc(acc) {
let rRoll = acc.y * -50;
let rPitch = acc.x * -100;
if (mCal > 10) {
//console.log("x: " + (rRoll - homeRoll) + " y:" + (rPitch - homePitch));
moveMouse(acc.y * -50 - homeRoll, acc.x * -100 - homePitch);
bt.moveMouse(acc.y * -50 - homeRoll, acc.x * -100 - homePitch);
} else {
//console.log("homeroll: " +homeRoll +"homepitch: " + homePitch);
homeRoll = rRoll * 0.7 + homeRoll * 0.3;
@ -347,34 +264,37 @@ function handleAcc(acc) {
}
}
Bangle.on('lock', function(on) {
if (on && holding) {
if (on && (holding || trackPadMode)) {
Bangle.setLocked(false);
Bangle.setLCDPower(1);
}
});
function startHolding() {
pressKey(kb.KEY.F10);
bt.tapKey(bt.KEY.F10, bt.MODIFY.SHIFT);
holding = true;
focusMode = true;
Bangle.buzz();
E.showMessage('Holding');
Bangle.on('accel', handleAcc);
Bangle.setLCDPower(1);
}
function stopHolding() {
clearTimeout(timeoutId);
if (holding) {
pressKey(kb.KEY.F10);
bt.tapKey(bt.KEY.F10);
// bt.tapKey(bt.KEY.F10);
homePitch = 0;
homeRoll = 0;
holding = false;
focusMode = false;
mCal = 0;
Bangle.removeListener('accel', handleAcc);
Bangle.buzz();
drawMain();
} else {
timeoutId = setTimeout(drawMain, 1000);
}
}
// else {
// timeoutId = setTimeout(drawMain, 1000);
// }
clearTimeout(timeoutHolding);
timeoutHolding = -1;
}
@ -395,24 +315,28 @@ Bangle.on('drag', function(e) {
//let qX = getSign(difX) * Math.pow(Math.abs(difX), 1.2);
//let qY = getSign(difY) * Math.pow(Math.abs(difY), 1.2);
let qX = difX + 0.02 * vX, qY = difY + 0.02 * vY;
moveMouse(qX, qY, 0, 0, 0, function() {
setTimeout(function() {clearToSend = true;}, 50);
bt.moveMouse(qX, qY, 0, 0, 0, function() {
timeoutSendMouse = setTimeout(function() {clearToSend = true; timeoutSendMouse = -1;}, 50);
});
lastx = e.x;
lasty = e.y;
mttl = getTime();
console.log("Dx: " + (qX) + " Dy: " + (qY));
} else if (timeoutSendMouse == -1) { // Can happen perhaps on single bluetooth failure.
timeoutSendMouse = setTimeout(function() {clearToSend = true; timeoutSendMouse = -1;}, 50);
}
if (!e.b) {
// short press
if (getTime() - cttl < 0.2) {
clickMouse(MouseButton.LEFT);
console.log("click left");
}
// longer press in center
else if (getTime() - cttl < 0.6 && e.x > g.getWidth()/4 && e.x < 3 * g.getWidth()/4 && e.y > g.getHeight() / 4 && e.y < 3 * g.getHeight() / 4) {
clickMouse(MouseButton.RIGHT);
console.log("click right");
if (!focusMode) {
// short press
if (getTime() - cttl < 0.2) {
bt.clickButton(bt.BUTTON.LEFT);
console.log("click left");
}
// longer press in center
else if (getTime() - cttl < 0.6 && e.x > g.getWidth()/4 && e.x < 3 * g.getWidth()/4 && e.y > g.getHeight() / 4 && e.y < 3 * g.getHeight() / 4) {
bt.clickButton(bt.BUTTON.RIGHT);
console.log("click right");
}
}
cttl = 0;
lastx = 0;
@ -430,15 +354,17 @@ Bangle.on('drag', function(e) {
} else if(lastx > 40){
// E.showMessage('right');
//kb.tap(kb.KEY.RIGHT, 0);
scroll(-1);
bt.scroll(-1);
} else if(lastx < -40){
// E.showMessage('left');
//kb.tap(kb.KEY.LEFT, 0);
scroll(1);
} else if(lastx==0 && lasty==0 && holding == false){
// E.showMessage('press');
clickMouse(MouseButton.LEFT);
}
bt.scroll(1);
}
// Todo re-implement? Seems bit buggy or unnecessary for now.
// else if(lastx==0 && lasty==0 && holding == false){
// // E.showMessage('press');
// bt.clickButton(bt.BUTTON.LEFT);
// }
stopHolding();
lastx = 0;
lasty = 0;
@ -452,20 +378,37 @@ Bangle.on('drag', function(e) {
}
});
function onBtn() {
if (trackPadMode) {
trackPadMode = false;
stopHolding();
drawMain();
if ((getTime() - bttl < 0.4 && !focusMode)) {
E.showMessage('Pointer');
focusMode = true;
bt.tapKey(bt.KEY.F10, bt.MODIFY.SHIFT);
} else {
trackPadMode = false;
stopHolding();
drawMain();
if (focusMode) {
bt.tapKey(bt.KEY.F10);
focusMode = false;
}
}
} else {
stopHolding();
clearToSend = true;
trackPadMode = true;
E.showMessage('Mouse');
// Also skip drawing thingy for now.
if (timeoutDraw != -1) {
clearTimeout(timeoutDraw);
timeoutDraw = -1;
}
bttl = getTime();
}
Bangle.buzz();
}
setWatch(onBtn, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat: true});
// Start App
loadSettings();
drawMain();

View File

@ -1,7 +1,7 @@
{
"id": "presentor",
"name": "Presentor",
"version": "0.08",
"version": "0.14",
"description": "Use your Bangle to present!",
"icon": "app.png",
"type": "app",