Merge remote-tracking branch 'upstream/master'

master
Hugh Barney 2022-12-06 23:12:45 +00:00
commit 424d784f20
45 changed files with 361 additions and 348 deletions

View File

@ -282,8 +282,11 @@ and which gives information about the app for the Launcher.
"dependencies" : { "notify":"type" } // optional, app 'types' we depend on (see "type" above) "dependencies" : { "notify":"type" } // optional, app 'types' we depend on (see "type" above)
"dependencies" : { "messages":"app" } // optional, depend on a specific app ID "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' // for instance this will use notify/notifyfs is they exist, or will pull in 'notify'
"dependencies" : { "messageicons":"module" } // optional, depend on a specific library to be used with 'require' "dependencies" : { "messageicons":"module" } // optional, depend on a specific library to be used with 'require' - see provides_modules
"dependencies" : { "message":"widget" } // optional, depend on a specific type of widget - see provides_widgets
"provides_modules" : ["messageicons"] // optional, this app provides a module that can be used with 'require' "provides_modules" : ["messageicons"] // optional, this app provides a module that can be used with 'require'
"provides_widgets" : ["battery"] // optional, this app provides a type of widget - 'alarm/battery/bluetooth/pedometer/message'
"default" : true, // set if an app is the default implementer of something (a widget/module/etc)
"readme": "README.md", // if supplied, a link to a markdown-style text file "readme": "README.md", // if supplied, a link to a markdown-style text file
// that contains more information about this app (usage, etc) // that contains more information about this app (usage, etc)
// A 'Read more...' link will be added under the app // A 'Read more...' link will be added under the app

View File

@ -36,4 +36,4 @@
0.33: Allow hiding timers&alarms 0.33: Allow hiding timers&alarms
0.34: Add "Confirm" option to alarm/timer edit menus 0.34: Add "Confirm" option to alarm/timer edit menus
0.35: Add automatic translation of more strings 0.35: Add automatic translation of more strings
0.36: alarm widget moved out of app

View File

@ -2,17 +2,16 @@
"id": "alarm", "id": "alarm",
"name": "Alarms & Timers", "name": "Alarms & Timers",
"shortName": "Alarms", "shortName": "Alarms",
"version": "0.35", "version": "0.36",
"description": "Set alarms and timers on your Bangle", "description": "Set alarms and timers on your Bangle",
"icon": "app.png", "icon": "app.png",
"tags": "tool,alarm,widget", "tags": "tool,alarm",
"supports": [ "BANGLEJS", "BANGLEJS2" ], "supports": [ "BANGLEJS", "BANGLEJS2" ],
"readme": "README.md", "readme": "README.md",
"dependencies": { "scheduler":"type" }, "dependencies": { "scheduler":"type", "alarm":"widget" },
"storage": [ "storage": [
{ "name": "alarm.app.js", "url": "app.js" }, { "name": "alarm.app.js", "url": "app.js" },
{ "name": "alarm.img", "url": "app-icon.js", "evaluate": true }, { "name": "alarm.img", "url": "app-icon.js", "evaluate": true }
{ "name": "alarm.wid.js", "url": "widget.js" }
], ],
"screenshots": [ "screenshots": [
{ "url": "screenshot-1.png" }, { "url": "screenshot-1.png" },

View File

@ -1,2 +1,3 @@
0.01: New app! 0.01: New app!
0.02: Submitted to app loader 0.02: Submitted to app loader
0.03: Do not invert colors

View File

@ -35,7 +35,7 @@ function drawImage(fileName) {
Bangle.setLCDBrightness(1); // Full brightness Bangle.setLCDBrightness(1); // Full brightness
image = eval(storage.read(fileName)); // Sadly, the only reasonable way to do this image = eval(storage.read(fileName)); // Sadly, the only reasonable way to do this
g.clear().reset().drawImage(image, 88, 88, { rotate: angle }); g.clear().reset().setBgColor(0).setColor("#fff").drawImage(image, 88, 88, { rotate: angle });
} }
setWatch(info => { setWatch(info => {
@ -44,7 +44,7 @@ setWatch(info => {
else angle = 0; else angle = 0;
Bangle.buzz(); Bangle.buzz();
g.clear().reset().drawImage(image, 88, 88, { rotate: angle }) g.clear().reset().setBgColor(0).setColor("#fff").drawImage(image, 88, 88, { rotate: angle })
} }
}, BTN1, { repeat: true }); }, BTN1, { repeat: true });

View File

@ -1,7 +1,7 @@
{ {
"id": "gallery", "id": "gallery",
"name": "Gallery", "name": "Gallery",
"version": "0.02", "version": "0.03",
"description": "A gallery that lets you view images uploaded with the IDE (see README)", "description": "A gallery that lets you view images uploaded with the IDE (see README)",
"readme": "README.md", "readme": "README.md",
"icon": "icon.png", "icon": "icon.png",

View File

@ -7,6 +7,7 @@
"readme": "README.md", "readme": "README.md",
"icon": "app.png", "icon": "app.png",
"type": "launch", "type": "launch",
"default": true,
"tags": "tool,system,launcher", "tags": "tool,system,launcher",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"storage": [ "storage": [

View File

@ -9,6 +9,7 @@
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"dependencies" : { "messageicons":"module" }, "dependencies" : { "messageicons":"module" },
"provides_modules": ["messagegui"], "provides_modules": ["messagegui"],
"default": true,
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"messagegui","url":"lib.js"}, {"name":"messagegui","url":"lib.js"},

View File

@ -8,6 +8,7 @@
"tags": "tool,system", "tags": "tool,system",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"provides_modules" : ["messageicons"], "provides_modules" : ["messageicons"],
"default": true,
"storage": [ "storage": [
{"name":"messageicons","url":"lib.js"} {"name":"messageicons","url":"lib.js"}
] ]

View File

@ -8,7 +8,11 @@
"tags": "tool,system", "tags": "tool,system",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"provides_modules" : ["messages"], "provides_modules" : ["messages"],
"dependencies" : { "messagegui":"module","messagewidget":"module" }, "dependencies" : {
"messagegui":"module",
"message":"widget"
},
"default": true,
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"messages","url":"lib.js"}, {"name":"messages","url":"lib.js"},

View File

@ -1,2 +1,3 @@
0.01: Initial version 0.01: Initial version
0.02: Use default Bangle formatter for booleans 0.02: Use default Bangle formatter for booleans
0.03: Drop duplicate alarm widget

View File

@ -1,7 +1,7 @@
{ {
"id": "noteify", "id": "noteify",
"name": "Noteify", "name": "Noteify",
"version": "0.02", "version": "0.03",
"description": "Write notes using an onscreen keyboard and use them as custom messages for alarms or timers.", "description": "Write notes using an onscreen keyboard and use them as custom messages for alarms or timers.",
"icon": "app.png", "icon": "app.png",
"tags": "tool,alarm", "tags": "tool,alarm",
@ -9,8 +9,7 @@
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"noteify.app.js","url":"app.js"}, {"name":"noteify.app.js","url":"app.js"},
{"name":"noteify.img","url":"app-icon.js","evaluate":true}, {"name":"noteify.img","url":"app-icon.js","evaluate":true}
{"name":"noteify.wid.js","url":"widget.js"}
], ],
"data": [{"name":"noteify.json"}], "data": [{"name":"noteify.json"}],
"dependencies": {"scheduler":"type","textinput":"type"}, "dependencies": {"scheduler":"type","textinput":"type"},

View File

@ -1,8 +0,0 @@
WIDGETS["alarm"]={area:"tl",width:0,draw:function() {
if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
},reload:function() {
// don't include library here as we're trying to use as little RAM as possible
WIDGETS["alarm"].width = (require('Storage').readJSON('sched.json',1)||[]).some(alarm=>alarm.on&&(alarm.hidden!==false)) ? 24 : 0;
}
};
WIDGETS["alarm"].reload();

View File

@ -4,3 +4,6 @@
Addict. Addict.
0.04: New layout. 0.04: New layout.
0.05: Add widget field, tweak layout. 0.05: Add widget field, tweak layout.
0.06: Add compatibility with Fastload Utils.
0.07: Remove just the specific listeners to not interfere with Quick Launch
when fastloading.

View File

@ -1,77 +1,20 @@
{
/* /*
Bluetooth.println(JSON.stringify({t:"intent", action:"", flags:["flag1", "flag2",...], categories:["category1","category2",...], mimetype:"", data:"", package:"", class:"", target:"", extra:{someKey:"someValueOrString"}})); Bluetooth.println(JSON.stringify({t:"intent", action:"", flags:["flag1", "flag2",...], categories:["category1","category2",...], mimetype:"", data:"", package:"", class:"", target:"", extra:{someKey:"someValueOrString"}}));
Podcast Addict is developed by Xavier Guillemane and can be downloaded on Google Play Store: https://play.google.com/store/apps/details?id=com.bambuna.podcastaddict&hl=en_US&gl=US Podcast Addict is developed by Xavier Guillemane and can be downloaded on Google Play Store: https://play.google.com/store/apps/details?id=com.bambuna.podcastaddict&hl=en_US&gl=US
Podcast Addict can be controlled through the sending of remote commands called 'Intents'. How to use intents to control Podcast Addict: https://podcastaddict.com/faq/130
Some 3rd parties apps specialized in task automation will then allow you to control Podcast Addict. For example, you will be able to wake up to the sound of your playlist or to start automatically playing when some NFC tag has been detected.
In Tasker, you just need to copy/paste one of the following intent in the task Action field ("Misc" action type then select "Send Itent") .
If you prefer Automate It, you can use the Podcast Addict plugin that will save you some configuration time (https://play.google.com/store/apps/details?id=com.smarterapps.podcastaddictplugin )
Before using an intent make sure to set the following:
Package: com.bambuna.podcastaddict
Class (UPDATE intent only): com.bambuna.podcastaddict.receiver.PodcastAddictBroadcastReceiver
Class (every other intent): com.bambuna.podcastaddict.receiver.PodcastAddictPlayerReceiver
Here are the supported commands (Intents) :
com.bambuna.podcastaddict.service.player.toggle Toggle the playlist
com.bambuna.podcastaddict.service.player.stop Stop the player and release its resources
com.bambuna.podcastaddict.service.player.play Start playing the playlist
com.bambuna.podcastaddict.service.player.pause Pause the playlist
com.bambuna.podcastaddict.service.player.nexttrack Start playing next track
com.bambuna.podcastaddict.service.player.previoustrack Start playing previous track
com.bambuna.podcastaddict.service.player.jumpforward Jump 30s forward
com.bambuna.podcastaddict.service.player.jumpbackward Jump 15s backward
com.bambuna.podcastaddict.service.player.1xspeed - Disable the variable playback speed
com.bambuna.podcastaddict.service.player.1.5xspeed Force the playback speed at 1.5x
com.bambuna.podcastaddict.service.player.2xspeed Force the playback speed at 2.0x
com.bambuna.podcastaddict.service.player.stoptimer Disable the timer
com.bambuna.podcastaddict.service.player.15mntimer Set the timer at 15 minutes
com.bambuna.podcastaddict.service.player.30mntimer Set the timer at 30 minutes
com.bambuna.podcastaddict.service.player.60mntimer Set the timer at 1 hour
com.bambuna.podcastaddict.service.update Trigger podcasts update
com.bambuna.podcastaddict.openmainscreen Open the app on the Main screen
com.bambuna.podcastaddict.openplaylist Open the app on the Playlist screen
com.bambuna.podcastaddict.openplayer Open the app on the Player screen
com.bambuna.podcastaddict.opennewepisodes Open the app on the New episodes screen
com.bambuna.podcastaddict.opendownloadedepisodes Open the app on the Downloaded episodes screen
com.bambuna.podcastaddict.service.player.playfirstepisode Start playing the first episode in the playlist
com.bambuna.podcastaddict.service.player.customspeed Select playback speed
In order to use this intent you need to pass a float argument called "arg1". Valid values are within [0.1, 5.0]
com.bambuna.podcastaddict.service.player.customtimer Start a custom timer
In order to use this intent you need to pass an int argument called "arg1" containing the number of minutes. Valid values are within [1, 1440]
com.bambuna.podcastaddict.service.player.deletecurrentskipnexttrack Delete the current episode and skip to the next one. It behaves the same way as long pressing on the player >| button, but doesn't display any confirmation popup.
com.bambuna.podcastaddict.service.player.deletecurrentskipprevioustrack Delete the current episode and skip to the previous one. It behaves the same way as long pressing on the player |< button, but doesn't display any confirmation popup.
com.bambuna.podcastaddict.service.player.boostVolume Toggle the Volume Boost audio effect
You can pass a, optional boolean argument called "arg1" in order to create a ON or OFF button for the volume boost. Without this parameter the app will just toggle the current value
com.bambuna.podcastaddict.service.player.quickBookmark Creates a bookmark at the current playback position so you can easily retrieve it later.
com.bambuna.podcastaddict.service.download.pause Pause downloads
com.bambuna.podcastaddict.service.download.resume Resume downloads
com.bambuna.podcastaddict.service. download.toggle Toggle downloads
com.bambuna.podcastaddict.service.player.favorite Mark the current episode playing as favorite.
com.bambuna.podcastaddict.openplaylist Open the app on the Playlist screen
You can pass an optional string argument called "arg1" in order to select the playlist to open. Without this parameter the app will open the current playlist
Here's how it works:
##AUDIO## will open the Audio playlist screen
##VIDEO## will open the Video playlist screen
##RADIO## will open the Radio screen
Any other argument will be used as a CATEGORY name. The app will then open this category under the playlist CUSTOM tab
You can pass an optional boolean argument called "arg2" in order to select if the app UI should be opened. Without this parameter the playlist will be displayed
You can pass an optional boolean argument called "arg3" in order to select if the app should start playing the selected playlist. Without this parameter the playback won't start
Since v2020.3
com.bambuna.podcastaddict.service.full_backup Trigger a full backup of the app data (relies on the app automatic backup settings for the folder and the # of backup to keep)
This task takes a lot of resources and might take up to a minute to complete, so please avoid using the app at the same time
Since v2020.15
com.bambuna.podcastaddict.service.player.toggletimer This will toggle the Sleep Timer using the last duration and parameter used in the app.
Since v2020.16
com.bambuna.podcastaddict.service.player.togglespeed This will toggle the Playback speed for the episode currently playing (alternate between selected speed and 1.0x).
*/ */
var R; let R;
var backToMenu = false; let widgetUtils = require("widget_utils");
var dark = g.theme.dark; // bool let backToMenu = false;
let dark = g.theme.dark; // bool
// The main layout of the app // The main layout of the app
function gfx() { let gfx = function() {
//Bangle.drawWidgets(); widgetUtils.hide();
R = Bangle.appRect; R = Bangle.appRect;
marigin = 8; marigin = 8;
// g.drawString(str, x, y, solid) // g.drawString(str, x, y, solid)
@ -106,19 +49,19 @@ function gfx() {
g.setFontAlign(1, 1, 0); g.setFontAlign(1, 1, 0);
g.drawString("Speed", R.x + R.w - 2*marigin, R.y + R.h - 2*marigin); g.drawString("Speed", R.x + R.w - 2*marigin, R.y + R.h - 2*marigin);
} };
// Touch handler for main layout // Touch handler for main layout
function touchHandler(_, xy) { let touchHandler = function(_, xy) {
x = xy.x; x = xy.x;
y = xy.y; y = xy.y;
len = (R.w<R.h+1)?(R.w/3):(R.h/3); len = (R.w<R.h+1)?(R.w/3):(R.h/3);
// doing a<b+1 seemed faster than a<=b, also using a>b-1 instead of a>b. // doing a<b+1 seemed faster than a<=b, also using a>b-1 instead of a>=b.
if ((R.x-1<x && x<R.x+len) && (R.y-1<y && y<R.y+len)) { if ((R.x-1<x && x<R.x+len) && (R.y-1<y && y<R.y+len)) {
//Menu //Menu
Bangle.removeAllListeners("touch"); Bangle.removeListener("touch", touchHandler);
Bangle.removeAllListeners("swipe"); Bangle.removeListener("swipe", swipeHandler);
backToMenu = true; backToMenu = true;
E.showMenu(paMenu); E.showMenu(paMenu);
} else if ((R.x-1<x && x<R.x+len) && (R.y2-len<y && y<R.y2+1)) { } else if ((R.x-1<x && x<R.x+len) && (R.y2-len<y && y<R.y2+1)) {
@ -127,13 +70,13 @@ function touchHandler(_, xy) {
gadgetbridgeWake(); gadgetbridgeWake();
} else if ((R.x2-len<x && x<R.x2+1) && (R.y-1<y && y<R.y+len)) { } else if ((R.x2-len<x && x<R.x2+1) && (R.y-1<y && y<R.y+len)) {
//Srch //Srch
Bangle.removeAllListeners("touch"); Bangle.removeListener("touch", touchHandler);
Bangle.removeAllListeners("swipe"); Bangle.removeListener("swipe", swipeHandler);
E.showMenu(searchMenu); E.showMenu(searchMenu);
} else if ((R.x2-len<x && x<R.x2+1) && (R.y2-len<y && y<R.y2+1)) { } else if ((R.x2-len<x && x<R.x2+1) && (R.y2-len<y && y<R.y2+1)) {
//Speed //Speed
Bangle.removeAllListeners("touch"); Bangle.removeListener("touch", touchHandler);
Bangle.removeAllListeners("swipe"); Bangle.removeListener("swipe", swipeHandler);
E.showMenu(speedMenu); E.showMenu(speedMenu);
} else if ((R.x-1<x && x<R.x+len) && (R.y+R.h/2-len/2<y && y<R.y+R.h/2+len/2)) { } else if ((R.x-1<x && x<R.x+len) && (R.y+R.h/2-len/2<y && y<R.y+R.h/2+len/2)) {
//Previous //Previous
@ -145,43 +88,51 @@ function touchHandler(_, xy) {
//play/pause //play/pause
btMsg("service", standardCls, "player.toggle"); btMsg("service", standardCls, "player.toggle");
} }
} };
// Swipe handler for main layout, used to jump backward and forward within a podcast episode. // Swipe handler for main layout, used to jump backward and forward within a podcast episode.
function swipeHandler(LR, _) { let swipeHandler = function(LR, _) {
if (LR==-1) { if (LR==-1) {
btMsg("service", standardCls, "player.jumpforward"); btMsg("service", standardCls, "player.jumpforward");
} }
if (LR==1) { if (LR==1) {
btMsg("service", standardCls, "player.jumpbackward"); btMsg("service", standardCls, "player.jumpbackward");
} }
} };
// Navigation input on the main layout // Navigation input on the main layout
function setUI() { let setUI = function() {
// Bangle.setUI code from rigrig's smessages app for volume control: https://git.tubul.net/rigrig/BangleApps/src/branch/personal/apps/smessages/app.js // Bangle.setUI code from rigrig's smessages app for volume control: https://git.tubul.net/rigrig/BangleApps/src/branch/personal/apps/smessages/app.js
Bangle.setUI( Bangle.setUI(
{mode : "updown", back : load}, {mode : "updown",
remove : ()=>{
Bangle.removeListener("touch", touchHandler);
Bangle.removeListener("swipe", swipeHandler);
clearWatch(buttonHandler);
widgetUtils.show();
}
},
ud => { ud => {
if (ud) Bangle.musicControl(ud>0 ? "volumedown" : "volumeup"); if (ud) Bangle.musicControl(ud>0 ? "volumedown" : "volumeup");
} }
); );
Bangle.on("touch", touchHandler); Bangle.on("touch", touchHandler);
Bangle.on("swipe", swipeHandler); Bangle.on("swipe", swipeHandler);
} let buttonHandler = setWatch(()=>{load();}, BTN, {edge:'falling'});
};
/* /*
The functions for interacting with Android and the Podcast Addict app The functions for interacting with Android and the Podcast Addict app
*/ */
pkg = "com.bambuna.podcastaddict"; let pkg = "com.bambuna.podcastaddict";
standardCls = pkg + ".receiver.PodcastAddictPlayerReceiver"; let standardCls = pkg + ".receiver.PodcastAddictPlayerReceiver";
updateCls = pkg + ".receiver.PodcastAddictBroadcastReceiver"; let updateCls = pkg + ".receiver.PodcastAddictBroadcastReceiver";
speed = 1.0; let speed = 1.0;
simpleSearch = ""; let simpleSearch = "";
function simpleSearchTerm() { // input a simple search term without tags, overrides search with tags (artist and track) let simpleSearchTerm = function() { // input a simple search term without tags, overrides search with tags (artist and track)
require("textinput").input({ require("textinput").input({
text: simpleSearch text: simpleSearch
}).then(result => { }).then(result => {
@ -189,9 +140,9 @@ function simpleSearchTerm() { // input a simple search term without tags, overri
}).then(() => { }).then(() => {
E.showMenu(searchMenu); E.showMenu(searchMenu);
}); });
} };
function searchPlayWOTags() { //make a search and play using entered terms let searchPlayWOTags = function() { //make a search and play using entered terms
searchString = simpleSearch; searchString = simpleSearch;
Bluetooth.println(JSON.stringify({ Bluetooth.println(JSON.stringify({
t: "intent", t: "intent",
@ -203,9 +154,9 @@ function searchPlayWOTags() { //make a search and play using entered terms
}, },
flags: ["FLAG_ACTIVITY_NEW_TASK"] flags: ["FLAG_ACTIVITY_NEW_TASK"]
})); }));
} };
function gadgetbridgeWake() { let gadgetbridgeWake = function() {
Bluetooth.println(JSON.stringify({ Bluetooth.println(JSON.stringify({
t: "intent", t: "intent",
target: "activity", target: "activity",
@ -213,15 +164,15 @@ function gadgetbridgeWake() {
package: "gadgetbridge", package: "gadgetbridge",
class: "nodomain.freeyourgadget.gadgetbridge.activities.WakeActivity" class: "nodomain.freeyourgadget.gadgetbridge.activities.WakeActivity"
})); }));
} };
// For stringing together the action for Podcast Addict to perform // For stringing together the action for Podcast Addict to perform
function actFn(actName, activOrServ) { let actFn = function(actName, activOrServ) {
return "com.bambuna.podcastaddict." + (activOrServ == "service" ? "service." : "") + actName; return "com.bambuna.podcastaddict." + (activOrServ == "service" ? "service." : "") + actName;
} };
// Send the intent message to Gadgetbridge // Send the intent message to Gadgetbridge
function btMsg(activOrServ, cls, actName, xtra) { let btMsg = function(activOrServ, cls, actName, xtra) {
Bluetooth.println(JSON.stringify({ Bluetooth.println(JSON.stringify({
t: "intent", t: "intent",
@ -231,22 +182,20 @@ function btMsg(activOrServ, cls, actName, xtra) {
target: "broadcastreceiver", target: "broadcastreceiver",
extra: xtra extra: xtra
})); }));
} };
// Get back to the main layout // Get back to the main layout
function backToGfx() { let backToGfx = function() {
E.showMenu(); E.showMenu();
g.clear(); g.clear();
g.reset(); g.reset();
Bangle.removeAllListeners("touch");
Bangle.removeAllListeners("swipe");
setUI(); setUI();
gfx(); gfx();
backToMenu = false; backToMenu = false;
} };
// Podcast Addict Menu // Podcast Addict Menu
var paMenu = { let paMenu = {
"": { "": {
title: " ", title: " ",
back: backToGfx back: backToGfx
@ -271,7 +220,7 @@ var paMenu = {
}; };
var controlMenu = { let controlMenu = {
"": { "": {
title: " ", title: " ",
back: () => {if (backToMenu) E.showMenu(paMenu); back: () => {if (backToMenu) E.showMenu(paMenu);
@ -310,7 +259,7 @@ var controlMenu = {
}, },
}; };
var speedMenu = { let speedMenu = {
"": { "": {
title: " ", title: " ",
back: () => {if (backToMenu) E.showMenu(paMenu); back: () => {if (backToMenu) E.showMenu(paMenu);
@ -333,7 +282,7 @@ var speedMenu = {
//"Slower" : ()=>{speed-=0.1; speed=((speed<0.1)?0.1:speed); btMsg("service",standardCls,"player.customspeed",{arg1:speed});}, //"Slower" : ()=>{speed-=0.1; speed=((speed<0.1)?0.1:speed); btMsg("service",standardCls,"player.customspeed",{arg1:speed});},
}; };
var searchMenu = { let searchMenu = {
"": { "": {
title: " ", title: " ",
@ -356,7 +305,7 @@ var searchMenu = {
"Simpler search and play" : searchPlayWOTags, "Simpler search and play" : searchPlayWOTags,
}; };
var navigationMenu = { let navigationMenu = {
"": { "": {
title: " ", title: " ",
back: () => {if (backToMenu) E.showMenu(paMenu); back: () => {if (backToMenu) E.showMenu(paMenu);
@ -372,4 +321,6 @@ var navigationMenu = {
Bangle.loadWidgets(); Bangle.loadWidgets();
setUI(); setUI();
widgetUtils.hide();
gfx(); gfx();
}

View File

@ -2,7 +2,7 @@
"id": "podadrem", "id": "podadrem",
"name": "Podcast Addict Remote", "name": "Podcast Addict Remote",
"shortName": "PA Remote", "shortName": "PA Remote",
"version": "0.05", "version": "0.07",
"description": "Control Podcast Addict on your android device.", "description": "Control Podcast Addict on your android device.",
"readme": "README.md", "readme": "README.md",
"type": "app", "type": "app",

View File

@ -7,6 +7,8 @@
"type": "scheduler", "type": "scheduler",
"tags": "tool,system,alarm", "tags": "tool,system,alarm",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"provides_modules" : ["sched"],
"default" : true,
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"sched.boot.js","url":"boot.js"}, {"name":"sched.boot.js","url":"boot.js"},

View File

@ -12,3 +12,4 @@
Add setting to disable scheduler alarm Add setting to disable scheduler alarm
0.10: Fix: Do not wake when falling asleep 0.10: Fix: Do not wake when falling asleep
0.11: Minor tweaks 0.11: Minor tweaks
0.12: Support javascript command to execute as defined in scheduler 'js' configuration

View File

@ -173,12 +173,16 @@ if (nextAlarmDate !== undefined) {
setTimeout(load, 1000); setTimeout(load, 1000);
} else if (measure && now >= minAlarm && swest === false) { } else if (measure && now >= minAlarm && swest === false) {
addLog(now, "alarm"); addLog(now, "alarm");
buzz();
measure = false; measure = false;
if (nextAlarmConfig.js) {
eval(nextAlarmConfig.js); // run nextAlarmConfig.js if set
} else {
buzz();
if (config.settings.disableAlarm) { if (config.settings.disableAlarm) {
// disable alarm for scheduler // disable alarm for scheduler
nextAlarmConfig.last = now.getDate(); nextAlarmConfig.last = now.getDate();
require("Storage").writeJSON("sched.json", alarms); require('Storage').writeJSON('sched.json', alarms);
}
} }
} }
}); });

View File

@ -2,7 +2,7 @@
"id": "sleepphasealarm", "id": "sleepphasealarm",
"name": "SleepPhaseAlarm", "name": "SleepPhaseAlarm",
"shortName": "SleepPhaseAlarm", "shortName": "SleepPhaseAlarm",
"version": "0.11", "version": "0.12",
"description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.", "description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.",
"icon": "app.png", "icon": "app.png",
"tags": "alarm", "tags": "alarm",

View File

@ -3,3 +3,6 @@
0.03: change handling of intent extras. 0.03: change handling of intent extras.
0.04: New layout. 0.04: New layout.
0.05: Add widgets field. Tweak layout. 0.05: Add widgets field. Tweak layout.
0.06: Make compatible with Fastload Utils app.
0.07: Remove just the specific listeners to not interfere with Quick Launch
when fastloading.

View File

@ -1,15 +1,17 @@
{
/* /*
Bluetooth.println(JSON.stringify({t:"intent", action:"", flags:["flag1", "flag2",...], categories:["category1","category2",...], mimetype:"", data:"", package:"", class:"", target:"", extra:{someKey:"someValueOrString"}})); Bluetooth.println(JSON.stringify({t:"intent", action:"", flags:["flag1", "flag2",...], categories:["category1","category2",...], mimetype:"", data:"", package:"", class:"", target:"", extra:{someKey:"someValueOrString"}}));
*/ */
var R; let R;
var backToMenu = false; let widgetUtils = require("widget_utils");
var isPaused = true; let backToMenu = false;
var dark = g.theme.dark; // bool let isPaused = true;
let dark = g.theme.dark; // bool
// The main layout of the app // The main layout of the app
function gfx() { let gfx = function() {
//Bangle.drawWidgets(); widgetUtils.hide();
R = Bangle.appRect; R = Bangle.appRect;
marigin = 8; marigin = 8;
// g.drawString(str, x, y, solid) // g.drawString(str, x, y, solid)
@ -44,10 +46,10 @@ function gfx() {
g.setFontAlign(1, 1, 0); g.setFontAlign(1, 1, 0);
g.drawString("Saved", R.x + R.w - 2*marigin, R.y + R.h - 2*marigin); g.drawString("Saved", R.x + R.w - 2*marigin, R.y + R.h - 2*marigin);
} };
// Touch handler for main layout // Touch handler for main layout
function touchHandler(_, xy) { let touchHandler = function(_, xy) {
x = xy.x; x = xy.x;
y = xy.y; y = xy.y;
len = (R.w<R.h+1)?(R.w/3):(R.h/3); len = (R.w<R.h+1)?(R.w/3):(R.h/3);
@ -55,8 +57,8 @@ function touchHandler(_, xy) {
// doing a<b+1 seemed faster than a<=b, also using a>b-1 instead of a>b. // doing a<b+1 seemed faster than a<=b, also using a>b-1 instead of a>b.
if ((R.x-1<x && x<R.x+len) && (R.y-1<y && y<R.y+len)) { if ((R.x-1<x && x<R.x+len) && (R.y-1<y && y<R.y+len)) {
//Menu //Menu
Bangle.removeAllListeners("touch"); Bangle.removeListener("touch", touchHandler);
Bangle.removeAllListeners("swipe"); Bangle.removeListener("swipe", swipeHandler);
backToMenu = true; backToMenu = true;
E.showMenu(spotifyMenu); E.showMenu(spotifyMenu);
} else if ((R.x-1<x && x<R.x+len) && (R.y2-len<y && y<R.y2+1)) { } else if ((R.x-1<x && x<R.x+len) && (R.y2-len<y && y<R.y2+1)) {
@ -65,13 +67,13 @@ function touchHandler(_, xy) {
gadgetbridgeWake(); gadgetbridgeWake();
} else if ((R.x2-len<x && x<R.x2+1) && (R.y-1<y && y<R.y+len)) { } else if ((R.x2-len<x && x<R.x2+1) && (R.y-1<y && y<R.y+len)) {
//Srch //Srch
Bangle.removeAllListeners("touch"); Bangle.removeListener("touch", touchHandler);
Bangle.removeAllListeners("swipe"); Bangle.removeListener("swipe", swipeHandler);
E.showMenu(searchMenu); E.showMenu(searchMenu);
} else if ((R.x2-len<x && x<R.x2+1) && (R.y2-len<y && y<R.y2+1)) { } else if ((R.x2-len<x && x<R.x2+1) && (R.y2-len<y && y<R.y2+1)) {
//Saved //Saved
Bangle.removeAllListeners("touch"); Bangle.removeListener("touch", touchHandler);
Bangle.removeAllListeners("swipe"); Bangle.removeListener("swipe", swipeHandler);
E.showMenu(savedMenu); E.showMenu(savedMenu);
} else if ((R.x-1<x && x<R.x+len) && (R.y+R.h/2-len/2<y && y<R.y+R.h/2+len/2)) { } else if ((R.x-1<x && x<R.x+len) && (R.y+R.h/2-len/2<y && y<R.y+R.h/2+len/2)) {
//Previous //Previous
@ -85,146 +87,150 @@ function touchHandler(_, xy) {
Bangle.musicControl(playPause); Bangle.musicControl(playPause);
isPaused = !isPaused; isPaused = !isPaused;
} }
} };
// Swipe handler for main layout, used to jump backward and forward within a podcast episode. // Swipe handler for main layout, used for next previous track.
function swipeHandler(LR, _) { let swipeHandler = function(LR, _) {
if (LR==-1) { if (LR==-1) {
spotifyWidget("NEXT"); spotifyWidget("NEXT");
} }
if (LR==1) { if (LR==1) {
spotifyWidget("PREVIOUS"); spotifyWidget("PREVIOUS");
} }
} };
// Navigation input on the main layout // Navigation input on the main layout
function setUI() { let setUI = function() {
// Bangle.setUI code from rigrig's smessages app for volume control: https://git.tubul.net/rigrig/BangleApps/src/branch/personal/apps/smessages/app.js // Bangle.setUI code from rigrig's smessages app for volume control: https://git.tubul.net/rigrig/BangleApps/src/branch/personal/apps/smessages/app.js
Bangle.setUI( Bangle.setUI(
{mode : "updown", back : load}, {mode : "updown",
remove : ()=>{
Bangle.removeListener("touch", touchHandler);
Bangle.removeListener("swipe", swipeHandler);
clearWatch(buttonHandler);
widgetUtils.show();
}
},
ud => { ud => {
if (ud) Bangle.musicControl(ud>0 ? "volumedown" : "volumeup"); if (ud) Bangle.musicControl(ud>0 ? "volumedown" : "volumeup");
} }
); );
Bangle.on("touch", touchHandler); Bangle.on("touch", touchHandler);
Bangle.on("swipe", swipeHandler); Bangle.on("swipe", swipeHandler);
} let buttonHandler = setWatch(()=>{load();}, BTN, {edge:'falling'});
};
// Get back to the main layout // Get back to the main layout
function backToGfx() { let backToGfx = function() {
E.showMenu(); E.showMenu();
g.clear(); g.clear();
g.reset(); g.reset();
Bangle.removeAllListeners("touch");
Bangle.removeAllListeners("swipe");
setUI(); setUI();
gfx(); gfx();
backToMenu = false; backToMenu = false;
} };
/* /*
The functions for interacting with Android and the Spotify app The functions for interacting with Android and the Spotify app
*/ */
simpleSearch = ""; simpleSearch = "";
function simpleSearchTerm() { // input a simple search term without tags, overrides search with tags (artist and track) let simpleSearchTerm = function() { // input a simple search term without tags, overrides search with tags (artist and track)
require("textinput").input({text:simpleSearch}).then(result => {simpleSearch = result;}).then(() => {E.showMenu(searchMenu);}); require("textinput").input({text:simpleSearch}).then(result => {simpleSearch = result;}).then(() => {E.showMenu(searchMenu);});
} };
artist = ""; artist = "";
function artistSearchTerm() { // input artist to search for let artistSearchTerm = function() { // input artist to search for
require("textinput").input({text:artist}).then(result => {artist = result;}).then(() => {E.showMenu(searchMenu);}); require("textinput").input({text:artist}).then(result => {artist = result;}).then(() => {E.showMenu(searchMenu);});
} };
track = ""; track = "";
function trackSearchTerm() { // input track to search for let trackSearchTerm = function() { // input track to search for
require("textinput").input({text:track}).then(result => {track = result;}).then(() => {E.showMenu(searchMenu);}); require("textinput").input({text:track}).then(result => {track = result;}).then(() => {E.showMenu(searchMenu);});
} };
album = ""; album = "";
function albumSearchTerm() { // input album to search for let albumSearchTerm = function() { // input album to search for
require("textinput").input({text:album}).then(result => {album = result;}).then(() => {E.showMenu(searchMenu);}); require("textinput").input({text:album}).then(result => {album = result;}).then(() => {E.showMenu(searchMenu);});
} };
function searchPlayWOTags() {//make a spotify search and play using entered terms let searchPlayWOTags = function() {//make a spotify search and play using entered terms
searchString = simpleSearch; searchString = simpleSearch;
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:searchString}, flags:["FLAG_ACTIVITY_NEW_TASK"]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:searchString}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
} };
function searchPlayWTags() {//make a spotify search and play using entered terms let searchPlayWTags = function() {//make a spotify search and play using entered terms
searchString = (artist=="" ? "":("artist:\""+artist+"\"")) + ((artist!="" && track!="") ? " ":"") + (track=="" ? "":("track:\""+track+"\"")) + (((artist!="" && album!="") || (track!="" && album!="")) ? " ":"") + (album=="" ? "":(" album:\""+album+"\"")); searchString = (artist=="" ? "":("artist:\""+artist+"\"")) + ((artist!="" && track!="") ? " ":"") + (track=="" ? "":("track:\""+track+"\"")) + (((artist!="" && album!="") || (track!="" && album!="")) ? " ":"") + (album=="" ? "":(" album:\""+album+"\""));
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:searchString}, flags:["FLAG_ACTIVITY_NEW_TASK"]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:searchString}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
} };
function playVreden() {//Play the track "Vreden" by Sara Parkman via spotify uri-link let playVreden = function() {//Play the track "Vreden" by Sara Parkman via spotify uri-link
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:track:5QEFFJ5tAeRlVquCUNpAJY:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:track:5QEFFJ5tAeRlVquCUNpAJY:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
} };
function playVredenAlternate() {//Play the track "Vreden" by Sara Parkman via spotify uri-link let playVredenAlternate = function() {//Play the track "Vreden" by Sara Parkman via spotify uri-link
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:track:5QEFFJ5tAeRlVquCUNpAJY:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK"]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:track:5QEFFJ5tAeRlVquCUNpAJY:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK"]}));
} };
function searchPlayVreden() {//Play the track "Vreden" by Sara Parkman via search and play let searchPlayVreden = function() {//Play the track "Vreden" by Sara Parkman via search and play
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:'artist:"Sara Parkman" track:"Vreden"'}, flags:["FLAG_ACTIVITY_NEW_TASK"]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:'artist:"Sara Parkman" track:"Vreden"'}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
} };
function openAlbum() {//Play EP "The Blue Room" by Coldplay let openAlbum = function() {//Play EP "The Blue Room" by Coldplay
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:album:3MVb2CWB36x7VwYo5sZmf2", target:"activity", flags:["FLAG_ACTIVITY_NEW_TASK"]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:album:3MVb2CWB36x7VwYo5sZmf2", target:"activity", flags:["FLAG_ACTIVITY_NEW_TASK"]}));
} };
function searchPlayAlbum() {//Play EP "The Blue Room" by Coldplay via search and play let searchPlayAlbum = function() {//Play EP "The Blue Room" by Coldplay via search and play
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:'album:"The blue room" artist:"Coldplay"', "android.intent.extra.focus":"vnd.android.cursor.item/album"}, flags:["FLAG_ACTIVITY_NEW_TASK"]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:'album:"The blue room" artist:"Coldplay"', "android.intent.extra.focus":"vnd.android.cursor.item/album"}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
} };
function spotifyWidget(action) { let spotifyWidget = function(action) {
Bluetooth.println(JSON.stringify({t:"intent", action:("com.spotify.mobile.android.ui.widget."+action), package:"com.spotify.music", target:"broadcastreceiver"})); Bluetooth.println(JSON.stringify({t:"intent", action:("com.spotify.mobile.android.ui.widget."+action), package:"com.spotify.music", target:"broadcastreceiver"}));
} };
function gadgetbridgeWake() { let gadgetbridgeWake = function() {
Bluetooth.println(JSON.stringify({t:"intent", target:"activity", flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_CLEAR_TASK", "FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS", "FLAG_ACTIVITY_NO_ANIMATION"], package:"gadgetbridge", class:"nodomain.freeyourgadget.gadgetbridge.activities.WakeActivity"})); Bluetooth.println(JSON.stringify({t:"intent", target:"activity", flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_CLEAR_TASK", "FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS", "FLAG_ACTIVITY_NO_ANIMATION"], package:"gadgetbridge", class:"nodomain.freeyourgadget.gadgetbridge.activities.WakeActivity"}));
} };
function spotifyPlaylistDW() { let spotifyPlaylistDW = function() {
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZEVXcRfaeEbxXIgb:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZEVXcRfaeEbxXIgb:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
} };
function spotifyPlaylistDM1() { let spotifyPlaylistDM1 = function() {
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E365VyzxE0mxF:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E365VyzxE0mxF:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
} };
function spotifyPlaylistDM2() { let spotifyPlaylistDM2 = function() {
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E38LZHLFnrM61:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E38LZHLFnrM61:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
} };
function spotifyPlaylistDM3() { let spotifyPlaylistDM3 = function() {
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E36RU87qzgBFP:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E36RU87qzgBFP:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
} };
function spotifyPlaylistDM4() { let spotifyPlaylistDM4 = function() {
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E396gGyCXEBFh:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E396gGyCXEBFh:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
} };
function spotifyPlaylistDM5() { let spotifyPlaylistDM5 = function() {
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E37a0Tt6CKJLP:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E37a0Tt6CKJLP:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
} };
function spotifyPlaylistDM6() { let spotifyPlaylistDM6 = function() {
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E36UIQLQK79od:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E36UIQLQK79od:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
} };
function spotifyPlaylistDD() { let spotifyPlaylistDD = function() {
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1EfWFiI7QfIAKq:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1EfWFiI7QfIAKq:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
} };
function spotifyPlaylistRR() { let spotifyPlaylistRR = function() {
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZEVXbs0XkE2V8sMO:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZEVXbs0XkE2V8sMO:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
} };
// Spotify Remote Menu // Spotify Remote Menu
var spotifyMenu = { let spotifyMenu = {
"" : { title : " ", "" : { title : " Menu ",
back: backToGfx }, back: backToGfx },
"Controls" : ()=>{E.showMenu(controlMenu);}, "Controls" : ()=>{E.showMenu(controlMenu);},
"Search and play" : ()=>{E.showMenu(searchMenu);}, "Search and play" : ()=>{E.showMenu(searchMenu);},
@ -234,8 +240,8 @@ var spotifyMenu = {
}; };
var controlMenu = { let controlMenu = {
"" : { title : " ", "" : { title : " Controls ",
back: () => {if (backToMenu) E.showMenu(spotifyMenu); back: () => {if (backToMenu) E.showMenu(spotifyMenu);
if (!backToMenu) backToGfx();} }, if (!backToMenu) backToGfx();} },
"Play" : ()=>{Bangle.musicControl("play");}, "Play" : ()=>{Bangle.musicControl("play");},
@ -246,8 +252,8 @@ var controlMenu = {
"Messages Music Controls" : ()=>{load("messagesmusic.app.js");}, "Messages Music Controls" : ()=>{load("messagesmusic.app.js");},
}; };
var searchMenu = { let searchMenu = {
"" : { title : " ", "" : { title : " Search ",
back: () => {if (backToMenu) E.showMenu(spotifyMenu); back: () => {if (backToMenu) E.showMenu(spotifyMenu);
if (!backToMenu) backToGfx();} }, if (!backToMenu) backToGfx();} },
"Search term w/o tags" : ()=>{simpleSearchTerm();}, "Search term w/o tags" : ()=>{simpleSearchTerm();},
@ -258,8 +264,8 @@ var searchMenu = {
"Execute search and play with tags" : ()=>{searchPlayWTags();}, "Execute search and play with tags" : ()=>{searchPlayWTags();},
}; };
var savedMenu = { let savedMenu = {
"" : { title : " ", "" : { title : " Saved ",
back: () => {if (backToMenu) E.showMenu(spotifyMenu); back: () => {if (backToMenu) E.showMenu(spotifyMenu);
if (!backToMenu) backToGfx();} }, if (!backToMenu) backToGfx();} },
"Play Discover Weekly" : ()=>{spotifyPlaylistDW();}, "Play Discover Weekly" : ()=>{spotifyPlaylistDW();},
@ -279,3 +285,4 @@ var savedMenu = {
Bangle.loadWidgets(); Bangle.loadWidgets();
setUI(); setUI();
gfx(); gfx();
}

View File

@ -1,7 +1,7 @@
{ {
"id": "spotrem", "id": "spotrem",
"name": "Remote for Spotify", "name": "Remote for Spotify",
"version": "0.05", "version": "0.07",
"description": "Control spotify on your android device.", "description": "Control spotify on your android device.",
"readme": "README.md", "readme": "README.md",
"type": "app", "type": "app",

View File

@ -1 +1,2 @@
0.01: Inital release. 0.01: Inital release.
0.02: Rebasing on latest changes to showScroller_Q3 (https://github.com/espruino/Espruino/commit/2d3c34ef7c2b9fe2118e816aacd2e096adb99596).

View File

@ -5,7 +5,8 @@ E.showScroller = (function(options) {
scroll = initial scroll position scroll = initial scroll position
scrollMin = minimum scroll amount (can be negative) scrollMin = minimum scroll amount (can be negative)
draw = function(idx, rect) draw = function(idx, rect)
select = function(idx) remove = function()
select = function(idx, touch)
} }
returns { returns {
@ -15,9 +16,61 @@ E.showScroller = (function(options) {
*/ */
if (!options) return Bangle.setUI(); // remove existing handlers if (!options) return Bangle.setUI(); // remove existing handlers
Bangle.setUI({
mode : "custom",
back : options.back,
remove : options.remove,
swipe : (_,UD)=>{
pixels = 120;
var dy = UD*pixels;
if (s.scroll - dy > menuScrollMax)
dy = s.scroll - menuScrollMax-8; // Makes it so the last 'page' has the same position as previous pages. This should be done dynamically (change the static 8 to be a variable) so the offset is correct even when no widget field or title field is present.
if (s.scroll - dy < menuScrollMin)
dy = s.scroll - menuScrollMin;
s.scroll -= dy;
var oldScroll = rScroll;
rScroll = s.scroll &~1;
dy = oldScroll-rScroll;
if (!dy || options.c<=3) return; //options.c<=3 should maybe be dynamic, so 3 would be replaced by a variable dependent on R=Bangle.appRect. It's here so we don't try to scroll if all entries fit in the app rectangle.
g.reset().setClipRect(R.x,R.y,R.x2,R.y2);
g.scroll(0,dy);
var d = UD*pixels;
if (d < 0) {
g.setClipRect(R.x,R.y2-(1-d),R.x2,R.y2);
let i = YtoIdx(R.y2-(1-d));
let y = idxToY(i);
//print(i, options.c, options.c-i); //debugging info
while (y < R.y2 - (options.h*((options.c-i)<=0)) ) { //- (options.h*((options.c-i)<=0)) makes sure we don't go beyond the menu entries in the menu object "options". This has to do with "dy = s.scroll - menuScrollMax-8" above.
options.draw(i, {x:R.x,y:y,w:R.w,h:options.h});
i++;
y += options.h;
}
} else { // d>0
g.setClipRect(R.x,R.y,R.x2,R.y+d);
let i = YtoIdx(R.y+d);
let y = idxToY(i);
//print(i, options.c, options.c-i); //debugging info
while (y > R.y-options.h) {
options.draw(i, {x:R.x,y:y,w:R.w,h:options.h});
y -= options.h;
i--;
}
}
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
}, touch : (_,e)=>{
if (e.y<R.y-4) return;
var i = YtoIdx(e.y);
if ((menuScrollMin<0 || i>=0) && i<options.c){
let yAbs = (e.y + rScroll - R.y);
let yInElement = yAbs - i*options.h;
options.select(i, {x:e.x, y:yInElement});
}
}
});
var menuShowing = false; var menuShowing = false;
var R = Bangle.appRect; var R = Bangle.appRect;
var Y = Bangle.appRect.y; var Y = R.y;
var n = Math.ceil(R.h/options.h); var n = Math.ceil(R.h/options.h);
var menuScrollMin = 0|options.scrollMin; var menuScrollMin = 0|options.scrollMin;
var menuScrollMax = options.h*options.c - R.h; var menuScrollMax = options.h*options.c - R.h;
@ -49,52 +102,5 @@ var s = {
var rScroll = s.scroll&~1; // rendered menu scroll (we only shift by 2 because of dither) var rScroll = s.scroll&~1; // rendered menu scroll (we only shift by 2 because of dither)
s.draw(); // draw the full scroller s.draw(); // draw the full scroller
g.flip(); // force an update now to make this snappier g.flip(); // force an update now to make this snappier
Bangle.setUI({
mode : "custom",
back : options.back,
swipe : (hor,ver)=>{
pixels = 120;
var dy = ver*pixels;
if (s.scroll - dy > menuScrollMax)
dy = s.scroll - menuScrollMax-8; // Makes it so the last 'page' has the same position as previous pages. This should be done dynamically (change the static 8 to be a variable) so the offset is correct even when no widget field or title field is present.
if (s.scroll - dy < menuScrollMin)
dy = s.scroll - menuScrollMin;
s.scroll -= dy;
var oldScroll = rScroll;
rScroll = s.scroll &~1;
dy = oldScroll-rScroll;
if (!dy || options.c<=3) return; //options.c<=3 should maybe be dynamic, so 3 would be replaced by a variable dependent on R=Bangle.appRect. It's here so we don't try to scroll if all entries fit in the app rectangle.
g.reset().setClipRect(R.x,R.y,R.x2,R.y2);
g.scroll(0,dy);
var d = ver*pixels;
if (d < 0) {
g.setClipRect(R.x,R.y2-(1-d),R.x2,R.y2);
let i = YtoIdx(R.y2-(1-d));
let y = idxToY(i);
//print(i, options.c, options.c-i); //debugging info
while (y < R.y2 - (options.h*((options.c-i)<=0)) ) { //- (options.h*((options.c-i)<=0)) makes sure we don't go beyond the menu entries in the menu object "options". This has to do with "dy = s.scroll - menuScrollMax-8" above.
options.draw(i, {x:R.x,y:y,w:R.w,h:options.h});
i++;
y += options.h;
}
} else { // d>0
g.setClipRect(R.x,R.y,R.x2,R.y+d);
let i = YtoIdx(R.y+d);
let y = idxToY(i);
//print(i, options.c, options.c-i); //debugging info
while (y > R.y-options.h) {
options.draw(i, {x:R.x,y:y,w:R.w,h:options.h});
y -= options.h;
i--;
}
}
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
}, touch : (_,e)=>{
if (e.y<R.y-4) return;
var i = YtoIdx(e.y);
if ((menuScrollMin<0 || i>=0) && i<options.c)
options.select(i);
}
});
return s; return s;
}); })

View File

@ -1,7 +1,7 @@
{ {
"id": "swscroll", "id": "swscroll",
"name": "Swipe menus", "name": "Swipe menus",
"version": "0.01", "version": "0.02",
"description": "Replace built in E.showScroller to act on swipe instead of drag. Navigate menus in discrete steps instead of a continuous motion.", "description": "Replace built in E.showScroller to act on swipe instead of drag. Navigate menus in discrete steps instead of a continuous motion.",
"readme": "README.md", "readme": "README.md",
"icon": "app.png", "icon": "app.png",

1
apps/widalarm/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: Moved out of 'alarm' app

BIN
apps/widalarm/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,15 @@
{
"id": "widalarm",
"name": "Alarms Widget",
"version": "0.01",
"description": "Displays an alarm icon in the widgets bar if any alarm is active",
"icon": "app.png",
"type": "widget",
"tags": "tool,alarm,widget",
"supports": [ "BANGLEJS", "BANGLEJS2" ],
"provides_widgets" : ["alarm"],
"default" : true,
"storage": [
{ "name": "widalarm.wid.js", "url": "widget.js" }
]
}

View File

@ -8,6 +8,7 @@
"type": "widget", "type": "widget",
"tags": "widget", "tags": "widget",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"provides_widgets" : ["alarm"],
"screenshots" : [ { "url":"screenshot.png" } ], "screenshots" : [ { "url":"screenshot.png" } ],
"storage": [ "storage": [
{"name":"widalarmeta.wid.js","url":"widget.js"} {"name":"widalarmeta.wid.js","url":"widget.js"}

View File

@ -6,6 +6,8 @@
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget,battery", "tags": "widget,battery",
"provides_widgets" : ["battery"],
"default" : true,
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"storage": [ "storage": [
{"name":"widbat.wid.js","url":"widget.js"} {"name":"widbat.wid.js","url":"widget.js"}

View File

@ -10,6 +10,7 @@
"readme": "README.md", "readme": "README.md",
"description": "Shows the current battery level status in the top right using the clocks colour theme", "description": "Shows the current battery level status in the top right using the clocks colour theme",
"tags": "widget,battery", "tags": "widget,battery",
"provides_widgets" : ["battery"],
"storage": [ "storage": [
{"name":"widbata.wid.js","url":"widbata.wid.js"} {"name":"widbata.wid.js","url":"widbata.wid.js"}
] ]

View File

@ -7,6 +7,7 @@
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget,battery", "tags": "widget,battery",
"provides_widgets" : ["battery"],
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"screenshots": [{"url":"widbatpc.full.jpg"},{"url":"widbatpc.part.jpg"}], "screenshots": [{"url":"widbatpc.full.jpg"},{"url":"widbatpc.part.jpg"}],

View File

@ -6,6 +6,7 @@
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget,battery", "tags": "widget,battery",
"provides_widgets" : ["battery"],
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"storage": [ "storage": [
{"name":"widbatv.wid.js","url":"widget.js"} {"name":"widbatv.wid.js","url":"widget.js"}

View File

@ -6,6 +6,8 @@
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget,bluetooth", "tags": "widget,bluetooth",
"provides_widgets" : ["bluetooth"],
"default" : true,
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"storage": [ "storage": [
{"name":"widbt.wid.js","url":"widget.js"} {"name":"widbt.wid.js","url":"widget.js"}

View File

@ -6,6 +6,7 @@
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget,bluetooth", "tags": "widget,bluetooth",
"provides_widgets" : ["bluetooth"],
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"storage": [ "storage": [
{"name":"widbt_notify.wid.js","url":"widget.js"}, {"name":"widbt_notify.wid.js","url":"widget.js"},

View File

@ -6,6 +6,7 @@
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget,bluetooth", "tags": "widget,bluetooth",
"provides_widgets" : ["bluetooth"],
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"storage": [ "storage": [
{"name":"widbthide.wid.js","url":"widget.js"} {"name":"widbthide.wid.js","url":"widget.js"}

View File

@ -10,6 +10,8 @@
"screenshots": [{"url": "screenshot.gif"}], "screenshots": [{"url": "screenshot.gif"}],
"dependencies" : { "messageicons":"module" }, "dependencies" : { "messageicons":"module" },
"provides_modules" : ["messagewidget"], "provides_modules" : ["messagewidget"],
"provides_widgets" : ["message"],
"default" : true,
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"messagewidget","url":"lib.js"}, {"name":"messagewidget","url":"lib.js"},

View File

@ -76,7 +76,7 @@ const APP_KEYS = [
'id', 'name', 'shortName', 'version', 'icon', 'screenshots', 'description', 'tags', 'type', 'id', 'name', 'shortName', 'version', 'icon', 'screenshots', 'description', 'tags', 'type',
'sortorder', 'readme', 'custom', 'customConnect', 'interface', 'storage', 'data', 'sortorder', 'readme', 'custom', 'customConnect', 'interface', 'storage', 'data',
'supports', 'allow_emulator', 'supports', 'allow_emulator',
'dependencies', 'provides_modules' 'dependencies', 'provides_modules', 'provides_widgets', "default"
]; ];
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports', 'noOverwrite']; const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports', 'noOverwrite'];
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate']; const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
@ -168,8 +168,8 @@ apps.forEach((app,appIdx) => {
if (app.dependencies) { if (app.dependencies) {
if (("object"==typeof app.dependencies) && !Array.isArray(app.dependencies)) { if (("object"==typeof app.dependencies) && !Array.isArray(app.dependencies)) {
Object.keys(app.dependencies).forEach(dependency => { Object.keys(app.dependencies).forEach(dependency => {
if (!["type","app","module"].includes(app.dependencies[dependency])) if (!["type","app","module","widget"].includes(app.dependencies[dependency]))
ERROR(`App ${app.id} 'dependencies' must all be tagged 'type/app/module' right now`, {file:metadataFile}); ERROR(`App ${app.id} 'dependencies' must all be tagged 'type/app/module/widget' right now`, {file:metadataFile});
if (app.dependencies[dependency]=="type" && !METADATA_TYPES.includes(dependency)) if (app.dependencies[dependency]=="type" && !METADATA_TYPES.includes(dependency))
ERROR(`App ${app.id} 'type' dependency must be one of `+METADATA_TYPES, {file:metadataFile}); ERROR(`App ${app.id} 'type' dependency must be one of `+METADATA_TYPES, {file:metadataFile});
}); });

2
core

@ -1 +1 @@
Subproject commit 3a953179b7bb9f574d4e77d5f34b6b7deee1e884 Subproject commit 2a89ea64f7874b9264572f68836fe8ecd0a6b191

View File

@ -1 +1 @@
["boot","launch","antonclk","health","setting","about","widbat","widbt","widlock","widid"] ["boot","launch","antonclk","health","setting","about","alarm","widbat","widbt","widlock","widid"]

View File

@ -20,7 +20,7 @@
"On": "Ein", "On": "Ein",
"Off": "Aus", "Off": "Aus",
"Ok": "OK", "Ok": "OK",
"New Timer": "Neuer Kurzzeitwecker", "New Timer": "Neuer Timer",
"(repeat)": "(Wiederholung)", "(repeat)": "(Wiederholung)",
"music": "Musik", "music": "Musik",
"Keep Msgs": "Nachrichten behalten", "Keep Msgs": "Nachrichten behalten",
@ -148,7 +148,7 @@
"Whitelist": "Whitelist", "Whitelist": "Whitelist",
"Select Clock": "Uhr auswählen", "Select Clock": "Uhr auswählen",
"Disable": "Deaktivieren", "Disable": "Deaktivieren",
"Timer": "Kurzzeitwecker", "Timer": "Timer",
"Error in settings": "Fehler in den Einstellungen", "Error in settings": "Fehler in den Einstellungen",
"Set Time": "Zeit einstellen", "Set Time": "Zeit einstellen",
"ALARM": "ALARM", "ALARM": "ALARM",

View File

@ -6,34 +6,34 @@
"Hours": "Timmar", "Hours": "Timmar",
"Minutes": "Minuter", "Minutes": "Minuter",
"Enabled": "Aktiverad", "Enabled": "Aktiverad",
"New Alarm": "Ny alarm", "New Alarm": "Nytt larm",
"Save": "Spara", "Save": "Spara",
"Back": "Tillbaka", "Back": "Tillbaka",
"Repeat": "Upprepning", "Repeat": "Upprepning",
"Delete": "Radera", "Delete": "Radera",
"ALARM!": "ALURH!", "ALARM!": "LARM!",
"Sleep": "Sömn", "Sleep": "Sömn",
"circle 3": "cirkel 3", "circle 3": "cirkel 3",
"circle 1": "cirkel 1", "circle 1": "cirkel 1",
"music": "musik", "music": "musik",
"week": "vecka", "week": "vecka",
"Keep Msgs": "Behåll meddelanden", "Keep Msgs": "Behåll meddelanden",
"Auto snooze": "Automatisk snooze", "Auto snooze": "Auto-snooza",
"step length": "steglängd", "step length": "steglängd",
"Circle": "Cirkel", "Circle": "Cirkel",
"data": "uppgifter", "data": "uppgifter",
"colorize icon": "färglägga ikonen", "colorize icon": "färglägg ikon",
"min. confidence": "Min. förtroende", "min. confidence": "min. konfidens",
"show widgets": "visa widgets", "show widgets": "visa widgetar",
"valid period": "giltig period", "valid period": "giltig period",
"Heartrate": "Hjärtfrekvens", "Heartrate": "Hjärtfrekvens",
"distance goal": "mål för distans", "distance goal": "distansmål",
"circle 4": "cirkel 4", "circle 4": "cirkel 4",
"circle count": "antal cirklar", "circle count": "antal cirklar",
"minimum": "minimum", "minimum": "minimum",
"maximum": "maximal", "maximum": "maximal",
"New Timer": "Ny timer", "New Timer": "Ny timer",
"battery warn": "batteri varning", "battery warn": "batterivarning",
"heartrate": "hjärtfrekvens", "heartrate": "hjärtfrekvens",
"circle 2": "cirkel 2", "circle 2": "cirkel 2",
"(repeat)": "(upprepning)", "(repeat)": "(upprepning)",
@ -42,30 +42,30 @@
"No Messages": "Inga meddelanden", "No Messages": "Inga meddelanden",
"Show clocks": "Visa klockor", "Show clocks": "Visa klockor",
"STEPS": "STEG", "STEPS": "STEG",
"TAP right top/bottom": "TAP höger upp/ner", "TAP right top/bottom": "KNACKA höger upp/ner",
"View Message": "Visa meddelande", "View Message": "Visa meddelande",
"Mark Unread": "Markera oläst", "Mark Unread": "Markera oläst",
"Are you sure": "Är du säker på att", "Are you sure": "Är du säker",
"Delete all messages": "Radera alla meddelanden", "Delete all messages": "Radera alla meddelanden",
"Record Run": "Rekordkörning", "Record Run": "Spåra löprunda",
"Unread timer": "Oläst timer", "Unread timer": "Oläst timer",
"Vibration": "Vibrationer", "Vibration": "Vibrationer",
"Utils": "Användaruppgifter", "Utils": "Verktyg",
"Quiet Mode": "Tyst läge", "Quiet Mode": "Tyst läge",
"Passkey BETA": "Passkey BETA", "Passkey BETA": "Passkey BETA",
"Dark BW": "Mörk BW", "Dark BW": "Mörk BW",
"BTNs 1:startlap 2:exit 3:reset": "BTN 1:startlap 2:exit 3:reset", "BTNs 1:startlap 2:exit 3:reset": "BTN 1:starta varv 2:lämna 3:återställ",
"start&lap/reset, BTN1: EXIT": "start&lap/återställning, BTN1: EXIT", "start&lap/reset, BTN1: EXIT": "starta&varv/återställ, BTN1: LÄMNA",
"BLE": "BLE", "BLE": "BLE",
"Programmable": "Programmerbar", "Programmable": "Programmerbar",
"Launcher Settings": "Inställningar för lanseringen", "Launcher Settings": "Inställningar för launchern",
"Vector font size": "Vektor teckensnittsstorlek", "Vector font size": "Storlek vektortypsnitt",
"Font": "Typsnitt", "Font": "Typsnitt",
"Yes\ndefinitely": "Ja\ndefinitivt", "Yes\ndefinitely": "Ja\ndefinitivt",
"App Source\nNot found": "App-källa\nEj funnen", "App Source\nNot found": "App-källa\nEj funnen",
"Make Connectable": "Gör det möjligt att ansluta", "Make Connectable": "Gör anslutningsbar",
"HID": "HID", "HID": "HID",
"Bluetooth": "Bluetooth", "Bluetooth": "Blåtand",
"Apps": "Appar", "Apps": "Appar",
"Piezo": "Piezo", "Piezo": "Piezo",
"LCD": "LCD", "LCD": "LCD",
@ -73,46 +73,46 @@
"Light BW": "Ljus BW", "Light BW": "Ljus BW",
"Background": "Bakgrund", "Background": "Bakgrund",
"Remove": "Ta bort", "Remove": "Ta bort",
"Highlight BG": "Markera BG", "Highlight BG": "Markering BG",
"Customize": "Anpassa", "Customize": "Anpassa",
"Highlight FG": "Highlight FG", "Highlight FG": "Markering FG",
"Background 2": "Bakgrund 2", "Background 2": "Bakgrund 2",
"LCD Brightness": "Ljusstyrka på LCD-skärmen", "LCD Brightness": "Ljusstyrka på LCD-skärmen",
"Add Device": "Lägg till enhet", "Add Device": "Lägg till enhet",
"Wake on BTN1": "Vakna BTN1", "Wake on BTN1": "Vakna av BTN1",
"Wake on BTN2": "Vakna BTN2", "Wake on BTN2": "Vakna av BTN2",
"Twist Timeout": "Twist Timeout", "Twist Timeout": "Vridning Timeout",
"Wake on Touch": "Vakna vid beröring", "Wake on Touch": "Vakna vid beröring",
"LCD Timeout": "LCD Timeout", "LCD Timeout": "LCD Timeout",
"Foreground": "Förgrund", "Foreground": "Förgrund",
"Connect device\nto add to\nwhitelist": "Anslut enhet\nför att lägga till\nvitlista", "Connect device\nto add to\nwhitelist": "Anslut enhet\nför att lägga till\ni vitlistan",
"Wake on FaceUp": "Vakna på FaceUp", "Wake on FaceUp": "Skärm upp väcker",
"Twist Threshold": "Tröskelvärde för vridning", "Twist Threshold": "Tröskelvärde för vridning",
"Wake on BTN3": "Wake på BTN3", "Wake on BTN3": "Vakna av BTN3",
"Clock Style": "Klockstil", "Clock Style": "Klockstil",
"Time Zone": "Tidszon", "Time Zone": "Tidszon",
"Twist Max Y": "Vridning Max Y", "Twist Max Y": "Vridning Max Y",
"Stay Connectable": "Håll dig tillgänglig", "Stay Connectable": "Håll anslutningsbar",
"This will remove everything": "Detta kommer att ta bort allt", "This will remove everything": "Detta kommer ta bort allt",
"Turn Off": "Stäng av", "Turn Off": "Stäng av",
"Connectable": "Anslutningsbar", "Connectable": "Anslutningsbar",
"Flattening battery - this can take hours.\nLong-press button to cancel": "Platta batterier - detta kan ta flera timmar.\nTryck länge på knappen för att avbryta", "Flattening battery - this can take hours.\nLong-press button to cancel": "Töm batteri - detta kan ta flera timmar.\nTryck länge på knappen för att avbryta",
"Reset to Defaults": "Återställ till standardvärden", "Reset to Defaults": "Återställ standardvärden",
"Utilities": "Verktyg", "Utilities": "Verktyg",
"Flatten Battery": "Platta batterier", "Flatten Battery": "Töm batteri",
"Debug Info": "Info om felsökning", "Debug Info": "Felsökningsinfo",
"Reset Settings": "Återställa inställningar", "Reset Settings": "Återställ inställningar",
"Wake on Twist": "Väckning på Twist", "Wake on Twist": "Vakna av vridning",
"Compact Storage": "Kompakt förvaring", "Compact Storage": "Komprimera lagring",
"Log": "Logg", "Log": "Logg",
"Rewrite Settings": "Omskrivning av inställningar", "Rewrite Settings": "Omskrivning av inställningar",
"Compacting...\nTakes approx\n1 minute": "Komprimering...\nTar ca.\n1 minut", "Compacting...\nTakes approx\n1 minute": "Komprimerar...\nTar ca.\n1 minut",
"Storage": "Lagring", "Storage": "Lagring",
"Second": "Andra", "Second": "Andra",
"App Settings": "App-inställningar", "App Settings": "App-inställningar",
"Invalid settings": "Ogiltiga inställningar", "Invalid settings": "Ogiltiga inställningar",
"Minute": "Protokoll", "Minute": "Minut",
"Sleep Phase Alarm": "Larm om sömnfas", "Sleep Phase Alarm": "Sömnfaslarm",
"No app has settings": "Ingen app har inställningar", "No app has settings": "Ingen app har inställningar",
"Hour": "Timme", "Hour": "Timme",
"No Clocks Found": "Inga klockor hittades", "No Clocks Found": "Inga klockor hittades",
@ -124,16 +124,16 @@
"TIMER": "TIMER", "TIMER": "TIMER",
"on": "på", "on": "på",
"OFF": "OFF", "OFF": "OFF",
"Side": "Sidan", "Side": "Sida",
"Sort Order": "Sortering", "Sort Order": "Sortering",
"Left": "Vänster", "Left": "Vänster",
"Right": "Höger", "Right": "Höger",
"Reset All": "Återställ alla", "Reset All": "Återställ alla",
"Widgets": "Widgets", "Widgets": "Widgetar",
"goal": "mål", "goal": "mål",
"Vibrate": "Vibrera", "Vibrate": "Vibrera",
"Message": "Meddelande", "Message": "Meddelande",
"Beep": "Piper", "Beep": "Pip",
"Disable": "Inaktivera", "Disable": "Inaktivera",
"Select Clock": "Välj klocka", "Select Clock": "Välj klocka",
"Locale": "Lokalisering", "Locale": "Lokalisering",
@ -145,14 +145,14 @@
"Timer": "Timer", "Timer": "Timer",
"BACK": "TILLBAKA", "BACK": "TILLBAKA",
"Error in settings": "Fel i inställningarna", "Error in settings": "Fel i inställningarna",
"Whitelist": "Whitelist", "Whitelist": "Vitlista",
"ALARM": "ALARM", "ALARM": "LARM",
"Hide": "Dölj", "Hide": "Dölj",
"Connected": "Ansluten", "Connected": "Ansluten",
"Show": "Visa", "Show": "Visa",
"On": "På", "On": "På",
"Ok": "Ok", "Ok": "Ok",
"No": "Ingen", "No": "Nej",
"Settings": "Inställningar", "Settings": "Inställningar",
"steps": "steg", "steps": "steg",
"back": "tillbaka", "back": "tillbaka",
@ -162,7 +162,7 @@
"Loading": "Laddar", "Loading": "Laddar",
"Music": "Musik", "Music": "Musik",
"color": "färg", "color": "färg",
"off": "off", "off": "av",
"Off": "Av", "Off": "Av",
"Theme": "Tema", "Theme": "Tema",
"one": "ett", "one": "ett",

View File

@ -18,28 +18,6 @@ so you may see the error "Module <module_name> not found" in the IDE when sendin
To fix this you have three options: To fix this you have three options:
### Host your own App Loader and upload from that
This is reasonably easy to set up, but it's more difficult to make changes and upload:
* Follow the steps here to set up your own App Loader: https://www.espruino.com/Bangle.js+App+Loader
* Make changes to that repository
* Refresh and upload your app from the app loader (you can have the IDE connected
at the same time so you can see any error messages)
### Upload the module to the Bangle's internal storage
This allows you to develop both the app and module very quickly, but the app is
uploaded in a slightly different way to what you'd get when you use the App Loader
or the method below:
* Load the module's source file in the Web IDE
* Click the down-arrow below the upload button, then `Storage`
* Click `New File`, type `your_module_name` as the name (with no `.js` extension), click `Ok`
* Now Click the `Upload` icon.
You can now upload the app direct from the IDE. You can even leave a second Web IDE window open
(one for the app, one for the module) to allow you to change the module.
### Change the Web IDE search path to include Bangle.js modules ### Change the Web IDE search path to include Bangle.js modules
@ -56,3 +34,30 @@ The next time you upload your app, the module will automatically be included.
**Note:** You can optionally use `https://raw.githubusercontent.com/espruino/BangleApps/master/modules|https://www.espruino.com/modules` **Note:** You can optionally use `https://raw.githubusercontent.com/espruino/BangleApps/master/modules|https://www.espruino.com/modules`
as the module URL to pull in modules direct from the development app loader (which could be slightly newer than the ones on https://banglejs.com/apps) as the module URL to pull in modules direct from the development app loader (which could be slightly newer than the ones on https://banglejs.com/apps)
### Host your own App Loader and upload from that
This is reasonably easy to set up, but it's more difficult to make changes and upload:
* Follow the steps here to set up your own App Loader: https://www.espruino.com/Bangle.js+App+Loader
* Make changes to that repository
* Refresh and upload your app from the app loader (you can have the IDE connected
at the same time so you can see any error messages)
### Upload the module to the Bangle's internal storage
This allows you to develop both the app and module very quickly, but the app is
uploaded in a slightly different way to what you'd get when you use the App Loader
or the method below:
* Load the module's source file in the Web IDE
* Click the down-arrow below the upload button, then `Storage`
* Click `New File`, type `your_module_name` as the name (with no `.js` extension), click `Ok`
* Now Click the `Upload` icon.
You can now upload the app direct from the IDE. You can even leave a second Web IDE window open
(one for the app, one for the module) to allow you to change the module.