commit
e5ebb03e13
|
|
@ -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.
|
||||
|
|
@ -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/)!
|
||||
|
|
@ -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();
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue