Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5c7c738
Add selection for shortcuts
RKBoss6 Dec 12, 2025
f9fcbf4
Add shortcut display, hides if no shortcuts are selected
RKBoss6 Dec 12, 2025
2af7d10
add shortcut setting default
RKBoss6 Dec 12, 2025
fb433a0
Add shortcut apps option to tag menu
RKBoss6 Dec 12, 2025
25dc225
Add files via upload
RKBoss6 Dec 12, 2025
a5988ff
Update version and enhance description in metadata
RKBoss6 Dec 12, 2025
087466a
Update shortName from 'Taglauncher' to 'Tag Launch'
RKBoss6 Dec 12, 2025
9565856
Update README.md
RKBoss6 Dec 12, 2025
9b53b3d
Update shortcut removal to use E.showPrompt
RKBoss6 Dec 12, 2025
2bdde12
Adjust main menu height based on shortcuts availability
RKBoss6 Dec 12, 2025
37852a8
Refactor promise handling in settings.js
RKBoss6 Dec 12, 2025
9842caf
Refactor loading of shortcuts and app sources
RKBoss6 Dec 12, 2025
f8af165
Update apps/taglaunch/settings.js
RKBoss6 Dec 12, 2025
46ec2e8
Update apps/taglaunch/settings.js
RKBoss6 Dec 12, 2025
cf2dbcb
Update apps/taglaunch/app.js
RKBoss6 Dec 12, 2025
13823c4
Update apps/taglaunch/app.js
RKBoss6 Dec 12, 2025
555999d
Update apps/taglaunch/settings.js
RKBoss6 Dec 12, 2025
8acb419
Update apps/taglaunch/app.js
RKBoss6 Dec 12, 2025
f033d39
Update app.js
RKBoss6 Dec 12, 2025
41af700
Update apps/taglaunch/settings.js
RKBoss6 Dec 12, 2025
e2ee087
Update apps/taglaunch/settings.js
RKBoss6 Dec 12, 2025
7fe9020
Update shortName in metadata.json
RKBoss6 Dec 12, 2025
0db209c
Add delay to loadShortcut function for buzz effect
RKBoss6 Dec 15, 2025
742d518
Update apps/taglaunch/app.js
RKBoss6 Dec 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/taglaunch/ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
0.06: Fixed a crash if an app has no tags (app.tags is undefined)
0.07: Clear cached app list when updating showClocks setting
0.08: Add haptic feedback option when selecting app or category in menu, increase vector size limit in settings
0.09: Add option to set up to 3 shortcut apps that you can access straight from the tag menu for quicker access
5 changes: 3 additions & 2 deletions apps/taglaunch/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
Tag Launcher
========

Based on the default launcher but puts all applications in a submenu by their tag.
Based on the default launcher but puts all applications in a submenu by their tag. There are three customizable spots at the top of the launcher for quicker access to your favorite apps.

With many applications installed this can result in a faster applications selection than the linear access of the default launcher.

Currently the following tags are supported: clock, game, tool, bluetooth, outdoors, health and misc.

Settings
--------

- `Shortcuts` - Change the app shortcuts at the top of the launcher. If no apps are set as the shortcut, the launcher will not have a section at the top, instead beginning with the first tag. (Default: No shortcuts)
- `Font` - The font used (`4x6`, `6x8`, `12x20`, `6x15` or `Vector`). Default `12x20`.
- `Vector Font Size` - The size of the font if `Font` is set to `Vector`. Default `10`.
- `Haptic Feedback` - Whether or not to vibrate slightly when selecting an app or category in the launcher. Default `No`.
Expand Down
58 changes: 46 additions & 12 deletions apps/taglaunch/app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{ // must be inside our own scope here so that when we are unloaded everything disappears
let s = require("Storage");

// TODO: Allow to change sortorder in settings
let tags = {"clock": {name: /*LANG*/"Clocks", icon: () => atob("MDCEBERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERESIiIiIREREREREREREREREREREREREiIiIiIiIiIhERERERERERERERERERESIiIiIiIiIiIiIREREREREREREREREREiIiIiIiIiIiIiIhERERERERERERERESIiIiIgz//8ziIiIiIREREREREREREREiIiIg////////ziIiIhERERERERERERIiIiD//////////84iIiERERERERERESIiIg/////8A/////ziIiIRERERERERESIiI//////8A//////+IiIREREREREREiIiP//////8A///////4iIhERERERERIiIg///////8A///////ziIiERERERERIiIP///////8A////////OIiERERERESIiI////////8A////////+IiIRERERESIiD////////8A////////84iIRERERESIiP////////8A/////////4iIRERERESIiP////////8A/////////4iIREREREiIg/////////8A/////////ziIhEREREiIg/////////IAL////////ziIhEREREiIj////////yAAAv////////iIhEREREiIgiIv/////wCIAP/////yIiiIhEREREiIgiIv/////wCIAP/////yIiiIhEREREiIj////////yAAAD////////iIhEREREiIg/////////IAABP//////ziIhEREREiIg///////////IAE//////ziIhERERESIiP//////////8gAT/////4iIRERERESIiP///////////yABP////4iIRERERESIiD////////////IAL///84iIRERERESIiI////////////8i////+IiIRERERERIiIP/////////////////OIiERERERERIiIg////////////////ziIiEREREREREiIiP///////////////4iIhERERERERESIiI//////////////+IiIRERERERERESIiIg/////8i/////ziIiIRERERERERERIiIiD////8i////84iIiEREREREREREREiIiIg///8i///ziIiIhERERERERERERESIiIiIgz8i8ziIiIiIREREREREREREREREiIiIiIiIiIiIiIhERERERERERERERERESIiIiIiIiIiIiIREREREREREREREREREREiIiIiIiIiIhERERERERERERERERERERERESIiIiIRERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERA==")},
"game": {name: /*LANG*/"Games", sortorder: 1, icon: () => atob("MDCEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMzMzMzMzMzMzMzMzMzMwAAAAAAAAAzMzMzMzMzMzMzMzMzMzMzMwAAAAAAAAAzIiIiIiIiIiIiIiIiIiIiMwAAAAAAAAAzL///IiIi////IiIi///yMwAAAAAAAAAzL///IiIi////IiIi///yMwAAAAAAAAAzL///IiIi////IiIi///yMwAAAAAAAAAzL///IiIi////IiIi///yMwAAAAAAAAAzL///IiIi////IiIi///yMwAAAAAAAAAzIiIi////IiIi////IiIiMwAAAAAAAAAzIiIi////IiIi////IiIiMwAAAAAAAAAzIiIi////IiIi////IiIiMwAAAAAAAAAzIiIi////IiIi////IiIiMwAAAAAAAAAzIiIi////IiIi////IiIiMwAAAAAAAAAzIiIi////IiIi////IiIiMwAAAAAAAAAzL///IiIi////IiIi///yMwAAAAAAAAAzL///IiIi////IiIi///yMwAAAAAAAAAzL///IiIi////IiIi8R/xERABEAAAAAAzL///IiIi////IiIi8R/xERABEAAAAAAzL///IiIi////IiIi8R/xERABEAAAAAAzL///IiIi////IiIi8RMxERABEAAAAAAzIiIi////IiIi////IREREREREAAAAAAzIiIi////IiIi////IREREREREAAAAAAzIiIi////IiIi////IREREREREAAAAAAzIiIi////IiIi////IhERERERAAAAAAAzIiIi////IiIi////IiMzMzMwAAAAAAAzIiIi////IiIi////IiMzMzMwAAAAAAAzL///IiIi////IiIi///xERAAAAAAAAAzL///IiIi////IiIi//8xERAAAAAAAAAzL///IiIi////IiIi//8hEREAAAAAAAAzL///IiIi////IiIi//8REREAAAAAAAAzL///IiIi////IiIi//MREREAAAAAAAAzIiIiIiIiIiIiIiIiIzMzMzMzMAAAAAAzMzMzMzMzMzMzMzMzMzMzMzMzMAAAAAAzMzMzMzMzMzMzMzMzMhEREREREAAAAAAAAAAAAAAAAAAAAAAAEREREREREQAAAAAAAAAAAAAAAAAAAAAAEREREREREQAAAAAAAAAAAAAAAAAAAAAAEREREREREQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==")},
Expand All @@ -18,7 +17,8 @@ let font = g.getFonts().includes("12x20") ? "12x20" : "6x8:2";
let settings = Object.assign({
showClocks: true,
fullscreen: false,
buzz:false
buzz:false,
shortcuts:["","",""]
}, s.readJSON("taglaunch.json", true) || {});
if ("vectorsize" in settings)
vectorval = parseInt(settings.vectorsize);
Expand Down Expand Up @@ -84,6 +84,15 @@ const unload = () => {
Bangle.removeListener("lock", lockHandler);
};

let shortcuts=settings.shortcuts;
let noShortcuts=shortcuts[0]==""&&shortcuts[1]==""&&shortcuts[2]=="";

let loadShortcut=function(idx){
if(shortcuts[idx]!=""){
const p = settings.buzz ? Bangle.buzz(25) : Promise.resolve();
p.then(() => load(shortcuts[idx]+".app.js"));
}
};
// 10s of inactivity goes back to clock
Bangle.setLocked(false); // unlock initially
let lockTimeout;
Expand Down Expand Up @@ -137,21 +146,46 @@ let showTagMenu = (tag) => {

let showMainMenu = () => {
E.showScroller({
h : 64*scaleval, c : tagKeys.length,
h : 64*scaleval, c : noShortcuts ? tagKeys.length : tagKeys.length+1,
draw : (i, r) => {
let tag = tagKeys[i];
g.clearRect((r.x),(r.y),(r.x+r.w-1), (r.y+r.h-1));
g.setFont(font).setFontAlign(-1,0).drawString(tags[tag].name,64*scaleval,r.y+(32*scaleval));

if(!noShortcuts)i-=1;
if(i==-1){
for(let j=0;j<3;j++){
const img = s.read(shortcuts[j]+".img");
if (img) {
try {
g.drawImage(img,8*scaleval+((g.getWidth()/3)*j), r.y+(8*scaleval), {scale: scaleval});}
catch(e){}
}
}


}else{
let tag = tagKeys[i]; g.setFont(font).setFontAlign(-1,0).drawString(tags[tag].name,64*scaleval,r.y+(32*scaleval));

const img = tags[tag].icon ? tags[tag].icon() : s.read("taglaunch." + tag + ".img");
if (img) {
try {g.drawImage(img,8*scaleval, r.y+(8*scaleval), {scale: scaleval});} catch(e){}
const img = tags[tag].icon ? tags[tag].icon() : s.read("taglaunch." + tag + ".img");
if (img) {
try {g.drawImage(img,8*scaleval, r.y+(8*scaleval), {scale: scaleval});} catch(e){}
}
}
},
select : i => {
if(settings.buzz)Bangle.buzz(25);
let tag = tagKeys[i];
showTagMenu(tag);
select : (i,e) => {
if(!noShortcuts)i-=1;
if(i==-1){
if(e.x<g.getWidth()/3){
loadShortcut(0)
}else if(e.x<(g.getWidth()/3)*2){
loadShortcut(1)
}else{
loadShortcut(2)
}
}else{
if(settings.buzz)Bangle.buzz(25);
let tag = tagKeys[i];
showTagMenu(tag);
}
},
back : Bangle.showClock, // button press or tap in top left shows clock now
remove : unload
Expand Down
6 changes: 3 additions & 3 deletions apps/taglaunch/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
"id": "taglaunch",
"name": "Tag Launcher",
"shortName": "Taglauncher",
"version": "0.08",
"version": "0.09",
"author": "nxdefiant",
"description": "Launcher that puts all applications into submenus based on their tag. With many applications installed this can result in a faster application selection than the linear access from the default launcher.",
"description": "Launcher that puts all applications into submenus based on their tag, with three customizable app shortcuts at the top. With many applications installed this can result in a faster application selection than the linear access from the default launcher.",
"readme": "README.md",
"icon": "app.png",
"type": "launch",
"tags": "tool,system,launcher",
"supports": ["BANGLEJS2"],
"screenshots": [ {"url":"screenshot.png"} ],
"screenshots": [ {"url":"screenshot.png"},{"url":"screenshot2.png"} ],
"storage": [
{"name":"taglaunch.app.js","url":"app.js"},
{"name":"taglaunch.settings.js","url":"settings.js"}
Expand Down
Binary file added apps/taglaunch/screenshot2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
90 changes: 88 additions & 2 deletions apps/taglaunch/settings.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,105 @@
// make sure to enclose the function in parentheses
(function(back) {

let launchCache;
let settings = Object.assign({
showClocks: true,
fullscreen: false,
buzz:false
buzz:false,
shortcuts:["","",""]
}, require("Storage").readJSON("taglaunch.json", true) || {});

let fonts = g.getFonts();
function save(key, value) {
settings[key] = value;
require("Storage").write("taglaunch.json",settings);
}

function setShortcut(id,spot,bk){
let shortcuts=settings.shortcuts;
shortcuts[spot]=id;
save("shortcuts",shortcuts);
bk();
}

function showAppList(spot,bk){
//make sure cache is present
launchCache=require("Storage").readJSON("taglaunch.cache.json");
if (!launchCache || !launchCache.appsByTag) {
E.showAlert("Caching apps, returning to launcher","Loading").then(function(v){
Bangle.load("taglaunch.app.js");
});
} else {
var appsByTag = launchCache.appsByTag;
var appsById = {};
for (var tag in appsByTag) {
var list = appsByTag[tag];
for (var i = 0; i < list.length; i++) {
var app = list[i];
// Use app.id as key to deduplicate
if (!appsById[app.id]) {
appsById[app.id] = {
name: app.name,
src: app.src || app.name,
id: app.id
};
}
}
}
var apps = Object.values(appsById);

var menu = {};
var spotText="";
if(spot==0)spotText+="Left ";
if(spot==1)spotText+="Center ";
if(spot==2)spotText+="Right ";
spotText+="App";
menu[""] = { "title" : spotText }; // Title for the menu

menu["None"] = function(){

E.showPrompt("Remove the "+spotText+" shortcut?",{
title:"Confirm",
buttons: {"Cancel":false,"Ok":true}
}).then(function(v){
if(v)setShortcut("",spot,bk);
else showAppList(spot,bk)
})
}
apps.forEach(a => {
menu[a.name] = function(){
E.showPrompt("Set "+a.name+" as the "+spotText+"?",{
title:"Confirm",
buttons: {"Cancel":false,"Ok":true}
}).then(function(v){
if(v)setShortcut(a.id,spot,bk);
else showAppList(spot,bk)
})
}
});
menu["< Back"] = bk;

// Show it
E.showMenu(menu);
}
}

function showSpotMenu(){
E.showMenu({
"":{ "title" : "Select Spot"}, // Title for the menu
"< Back":function(){
eval(require("Storage").read("taglaunch.settings.js"))(()=>load());
},
"Left": function() { showAppList(0,showSpotMenu); },
"Center": function() { showAppList(1,showSpotMenu); },
"Right": function() { showAppList(2,showSpotMenu);}
})
}
const appMenu = {
"": { "title": /*LANG*/"Tag Launcher" },
"< Back": back,
/*LANG*/"Shortcuts": function(){
showSpotMenu()
},
/*LANG*/"Font": {
value: fonts.includes(settings.font)? fonts.indexOf(settings.font) : fonts.indexOf("12x20"),
min:0, max:fonts.length-1, step:1,wrap:true,
Expand Down