From 67a394a7f4ccde1d50f5646093eb6175045bc24b Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Tue, 27 Sep 2022 22:01:57 +0200 Subject: [PATCH 01/28] Podcast addict remote initial release add shortName Add Podcast Addict Play Store Link Podcast Addict use official icon new layout changes to README.md and metadata description requires GB 71.0 screenshots --- apps/podadrem/ChangeLog | 6 + apps/podadrem/README.md | 21 ++ apps/podadrem/app-icon.js | 1 + apps/podadrem/app.js | 372 ++++++++++++++++++++++++++++++++++ apps/podadrem/app.png | Bin 0 -> 2120 bytes apps/podadrem/metadata.json | 18 ++ apps/podadrem/screenshot1.png | Bin 0 -> 1836 bytes apps/podadrem/screenshot2.png | Bin 0 -> 3220 bytes 8 files changed, 418 insertions(+) create mode 100644 apps/podadrem/ChangeLog create mode 100644 apps/podadrem/README.md create mode 100644 apps/podadrem/app-icon.js create mode 100644 apps/podadrem/app.js create mode 100644 apps/podadrem/app.png create mode 100644 apps/podadrem/metadata.json create mode 100644 apps/podadrem/screenshot1.png create mode 100644 apps/podadrem/screenshot2.png diff --git a/apps/podadrem/ChangeLog b/apps/podadrem/ChangeLog new file mode 100644 index 000000000..c26e40c0e --- /dev/null +++ b/apps/podadrem/ChangeLog @@ -0,0 +1,6 @@ +0.01: Inital release. +0.02: Misc fixes. Add Search and play. +0.03: Simplify "Search and play" function after some bugfixes to Podcast +Addict. +0.04: New layout. +0.05: Add widget field, tweak layout. diff --git a/apps/podadrem/README.md b/apps/podadrem/README.md new file mode 100644 index 000000000..3760e6b5b --- /dev/null +++ b/apps/podadrem/README.md @@ -0,0 +1,21 @@ +Requires Gadgetbridge 71.0 or later. Allow intents in Gadgetbridge in order for this app to work. + +Touch input: + +Press the different ui elements to control Podcast Addict and open menus. +Press left or right arrow to move backward/forward in current playlist. + +Swipe input: + +Swipe left/right to jump backward/forward within the current podcast episode. +Swipe up/down to change the volume. + +It's possible to start a podcast by searching with the remote. It's also possible to change the playback speed. + +The swipe logic was inspired by the implementation in [rigrig](https://git.tubul.net/rigrig/)'s Scrolling Messages. + +Podcast Addict Remote was created by [thyttan](https://github.com/thyttan/). + +Podcast Addict is developed by [Xavier Guillemane](https://twitter.com/xguillem) and can be installed via the [Google Play Store](https://play.google.com/store/apps/details?id=com.bambuna.podcastaddict&hl=en_US&gl=US). + +The Podcast Addict icon is used with permission. diff --git a/apps/podadrem/app-icon.js b/apps/podadrem/app-icon.js new file mode 100644 index 000000000..fc4406666 --- /dev/null +++ b/apps/podadrem/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwgHEhvdABnQDwwVNAAYtTGI4WSGAgWTGAYXUGAJGUGAQXXCyoXKmf/AAPznogQn4WCAAQYP6YWFDB4WFJQhFSA4gwMIYogEGBffLg0zKAYwKRgwTDBQP9Ix09n7DCpowBJBKNEBwIXBAQIsBMwgXKIQReCDoRgJOwYQDLQU/poMBC5B2DIAUzLwIKBnoXBPBAXEIQQVDA4IXNCIQXaWAgXNI4kzNQoXLO4wXLU4a+CU4gXR7ovBcIoXIBobMFPAgXILQKPDmgxCR5omDc4QAHC5ITCC6hgCC6hICC6owBC6phBC6zcFAAMzeogALdQjdBC6AZCeYfTmczAwfQhocOAAwXYgAXVgAXVFwMAJCgXCDCYWDJKYWEGKAtEA==")) diff --git a/apps/podadrem/app.js b/apps/podadrem/app.js new file mode 100644 index 000000000..a9b5514b8 --- /dev/null +++ b/apps/podadrem/app.js @@ -0,0 +1,372 @@ +/* +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 can be controlled through the sending of remote commands called 'Intents'. +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; +var backToMenu = false; + +// The main layout of the app +function gfx() { + R = Bangle.appRect; + marigin = 8; + // g.drawString(str, x, y, solid) + g.clearRect(R); + g.reset(); + + g.setFont("4x6:2"); + g.setFontAlign(1, 0, 0); + g.drawString("->", R.x2 - marigin, R.y + R.h/2); + + g.setFontAlign(-1, 0, 0); + g.drawString("<-", R.x + marigin, R.y + R.h/2); + + g.setFontAlign(-1, 0, 1); + g.drawString("<-", R.x + R.w/2, R.y + marigin); + + g.setFontAlign(1, 0, 1); + g.drawString("->", R.x + R.w/2, R.y2 - marigin); + + g.setFontAlign(0, 0, 0); + g.drawString("Play\nPause", R.x + R.w/2, R.y + R.h/2); + + g.setFontAlign(-1, -1, 0); + g.drawString("Menu", R.x + 2*marigin, R.y + 2*marigin); + + g.setFontAlign(-1, 1, 0); + g.drawString("Wake", R.x + 2*marigin, R.y + R.h - 2*marigin); + + g.setFontAlign(1, -1, 0); + g.drawString("Srch", R.x + R.w - 2*marigin, R.y + 2*marigin); + + g.setFontAlign(1, 1, 0); + g.drawString("Speed", R.x + R.w - 2*marigin, R.y + R.h - 2*marigin); +} + +// Touch handler for main layout +function touchHandler(_, xy) { + x = xy.x; + y = xy.y; + len = (R.wb-1 instead of a>b. + if ((R.x-1 { + if (ud) Bangle.musicControl(ud>0 ? "volumedown" : "volumeup"); + } + ); + Bangle.on("touch", touchHandler); + Bangle.on("swipe", swipeHandler); +} + +/* +The functions for interacting with Android and the Podcast Addict app +*/ + +pkg = "com.bambuna.podcastaddict"; +standardCls = pkg + ".receiver.PodcastAddictPlayerReceiver"; +updateCls = pkg + ".receiver.PodcastAddictBroadcastReceiver"; +speed = 1.0; + +simpleSearch = ""; + +function simpleSearchTerm() { // 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); + }); +} + +function searchPlayWOTags() { //make a search and play using entered terms + searchString = simpleSearch; + Bluetooth.println(JSON.stringify({ + t: "intent", + action: "android.media.action.MEDIA_PLAY_FROM_SEARCH", + package: pkg, + target: "activity", + extra: { + query: searchString + }, + flags: ["FLAG_ACTIVITY_NEW_TASK"] + })); +} + +function gadgetbridgeWake() { + 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" + })); +} + +// For stringing together the action for Podcast Addict to perform +function actFn(actName, activOrServ) { + return "com.bambuna.podcastaddict." + (activOrServ == "service" ? "service." : "") + actName; +} + +// Send the intent message to Gadgetbridge +function btMsg(activOrServ, cls, actName, xtra) { + + Bluetooth.println(JSON.stringify({ + t: "intent", + action: actFn(actName, activOrServ), + package: pkg, + class: cls, + target: "broadcastreceiver", + extra: xtra + })); +} + +// Get back to the main layout +function backToGfx() { + E.showMenu(); + g.clear(); + g.reset(); + Bangle.removeAllListeners("touch"); + Bangle.removeAllListeners("swipe"); + setUI(); + gfx(); + backToMenu = false; +} + +// Podcast Addict Menu +var paMenu = { + "": { + title: " ", + back: backToGfx + }, + "Controls": () => { + E.showMenu(controlMenu); + }, + "Speed Controls": () => { + E.showMenu(speedMenu); + }, + "Search and play": () => { + E.showMenu(searchMenu); + }, + "Navigate and play": () => { + E.showMenu(navigationMenu); + }, + "Wake the android": () => { + gadgetbridgeWake(); + gadgetbridgeWake(); + }, + "Exit PA Remote": ()=>{load();} +}; + + +var controlMenu = { + "": { + title: " ", + back: () => {if (backToMenu) E.showMenu(paMenu); + if (!backToMenu) backToGfx(); + } + }, + "Toggle Play/Pause": () => { + btMsg("service", standardCls, "player.toggle"); + }, + "Jump Backward": () => { + btMsg("service", standardCls, "player.jumpbackward"); + }, + "Jump Forward": () => { + btMsg("service", standardCls, "player.jumpforward"); + }, + "Previous": () => { + btMsg("service", standardCls, "player.previoustrack"); + }, + "Next": () => { + btMsg("service", standardCls, "player.nexttrack"); + }, + "Play": () => { + btMsg("service", standardCls, "player.play"); + }, + "Pause": () => { + btMsg("service", standardCls, "player.pause"); + }, + "Stop": () => { + btMsg("service", standardCls, "player.stop"); + }, + "Update": () => { + btMsg("service", updateCls, "update"); + }, + "Messages Music Controls": () => { + load("messagesmusic.app.js"); + }, +}; + +var speedMenu = { + "": { + title: " ", + back: () => {if (backToMenu) E.showMenu(paMenu); + if (!backToMenu) backToGfx(); + } + }, + "Regular Speed": () => { + speed = 1.0; + btMsg("service", standardCls, "player.1xspeed"); + }, + "1.5x Regular Speed": () => { + speed = 1.5; + btMsg("service", standardCls, "player.1.5xspeed"); + }, + "2x Regular Speed": () => { + speed = 2.0; + btMsg("service", standardCls, "player.2xspeed"); + }, + //"Faster" : ()=>{speed+=0.1; speed=((speed>5.0)?5.0: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 = { + "": { + title: " ", + + back: () => {if (backToMenu) E.showMenu(paMenu); + if (!backToMenu) backToGfx();} + + }, + "Search term": () => { + simpleSearchTerm(); + }, + "Execute search and play": () => { + btMsg("service", standardCls, "player.play"); + setTimeout(() => { + searchPlayWOTags(); + setTimeout(() => { + btMsg("service", standardCls, "player.play"); + }, 200); + }, 1500); + }, + "Simpler search and play" : searchPlayWOTags, +}; + +var navigationMenu = { + "": { + title: " ", + back: () => {if (backToMenu) E.showMenu(paMenu); + if (!backToMenu) backToGfx();} + }, + "Open Main Screen": () => { + btMsg("activity", standardCls, "openmainscreen"); + }, + "Open Player Screen": () => { + btMsg("activity", standardCls, "openplayer"); + }, +}; + +Bangle.loadWidgets(); +setUI(); +gfx(); diff --git a/apps/podadrem/app.png b/apps/podadrem/app.png new file mode 100644 index 0000000000000000000000000000000000000000..b9cdf4fedb180793d3dcce293c65284a9eeca7f0 GIT binary patch literal 2120 zcmdT`iBr=_7XC>Dl?;g2a%{<l z9aR7T)R4}I3yRfrYwiRoe9+vk4gdfFZZ4i@ovL378dqr{59ZTg)0JfB8;B} zSNQ)0a8isF09O2e6aT8iNj6r%*h$8m)Ge^WzOw#?`J(gJ>{pC}rPySDQP?7W2{;O= zubRnQ+0D&ORve5O17k!&*K&-RQP3?`4xAYaTg!$jYG%j7n9)#0k*ru4I{~J+i10a? zooKuo0FjG@GCq@f39>1bZpvj03{G%? zfrcno$bQE24D3(#LIxon4e^<)s6R~%oM~rrIv=Vl2Vt&lP{PV7Z~w&!e2ty ze?p3%=C9>j%lRxpu^ktWV#OMA@=!uT5WhNx5oRD6Zxwgmpoc=_JgS`gnReMo_>3f3 znPFvl(;}^z*Dp#x&GGJ~u8~W{e+&qR+i9UkxfKcQf?zh@pL08s-*A(af#MOe)_yIN z&JD@gAJ^}BNatS&N*rX&C3Yrqz54<8cF(5V(Z~G>BN}O8#+h;QFNlU3_|>t} zl`($td2v^Pbh>Bbbptc)d)|W|8L^gvLR-ngxMZ?Z&ZqJ5w(B*1lKEGh8=k_E4rbg* zI`+Gb#Yxd?%EruZpS^UMX)f`K_E&`~ntvaOOa*{#K;i|I2k?!&kU#`Y*FP33S9O~Z zyOG?om8F%u?%ucI*B(j(BP^rLI_sRCWb)KcLFi&|ZJX$4G*t?#xsRJO5C@bNDBAG< zcmPleNKf%D2$2YTPv_w)K>*15G$zkDUCV>`xd>$9}qH{P`dE(-2) zdv?{$NTnp-{x(vr2`{5PHK{Ff|)+09%Pcm z(JR#xFGFAM$O@LEeg9pR$`h8ao0^GsZGrlUvo)GY zI9#flS%XH66_XssO%KutE!w`n1oJ;Q=(oMIF=6^oOeKHhAPwt5e*C(+fJQQAr@C?^ z9>9!1jnCbRG6HSXHb883nn1P7=KhWW$F52!R9g!1x}eIixYA{k|I$k8`X|}XgB0Q(*n45plb&)c&0n_(;g(!z`(F*Fd;JaUof8xz#+#^sM3%p z?#uR0Ka;%5ksFktcB`%fU+mn+)J%Y+E0_=O2FI3GFtW2o=;n4PD*%$JLe=}%WqBhG@aIP z&6%wqO9-n&8^N>D6Q{Mf>$r*dyj zl?Jg$+ak=R`eLOjDi(voQ7DvshiJCX%C)slnnjeSy_2T9C1*W6?<{hBJM5=KcZVl= z^?a)?umh95y97slCh1Un9i>5^#MEH-*oWJqRagBgFVi>!iN(8)cX!l|=)cM()=g=j z?P>0?{Wd-!Z`8kLrb1b{q0Ip}qtRe9J!AKS63}7$_=Sw6w@dLu0FY-<2$Dlk_J05* C9lC@7 literal 0 HcmV?d00001 diff --git a/apps/podadrem/metadata.json b/apps/podadrem/metadata.json new file mode 100644 index 000000000..929269762 --- /dev/null +++ b/apps/podadrem/metadata.json @@ -0,0 +1,18 @@ +{ + "id": "podadrem", + "name": "Podcast Addict Remote", + "shortName": "PA Remote", + "version": "0.05", + "description": "Control Podcast Addict on your android device.", + "readme": "README.md", + "type": "app", + "tags": "remote,podcast,podcasts,radio,player,intent,intents,gadgetbridge,podadrem,pa remote", + "icon": "app.png", + "screenshots" : [ {"url":"screenshot1.png"}, {"url":"screenshot2.png"} ], + "supports": ["BANGLEJS2"], + "dependencies": { "textinput":"type"}, + "storage": [ + {"name":"podadrem.app.js","url":"app.js"}, + {"name":"podadrem.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/podadrem/screenshot1.png b/apps/podadrem/screenshot1.png new file mode 100644 index 0000000000000000000000000000000000000000..e028b331824cd45a6217ea520e68e91442e09215 GIT binary patch literal 1836 zcmeH|TT@e46o5|-2MC}{LIA;lC{aXip&$s-0LBmu6_m>i?P%a+P+^*ei^wGe1EEj_ zlTbzkK_wLw%hUjMFfd3kD1~w{kxmd50$9tXLQIC(q(}%ooxb)Tv=4i)J$tRS*ULBG zt~?nRz1@1JH2?tHX~!rDNbLN3p)HWTqx-iMB%pE=qRBwJ+~s!wuvnx~jwBVPs2krD z&3Iye0d(|{bTRq`Dgky zM%PvgA5++6wxu&>|7?WG&$XGee+(qG1yv1xw>O6X( zV?`XxI#K*yzF_W`e3DJAYILTTwC5$6Zk4{nN$wnez4gY`iD$rEWb>GFF)a>`^tq$e z>~pWCiIdnn)MFAeg7#B8A^$=skLWbfJ-$p@Tkf8yY3*qexFX~BBx!N5r9QB= ztOfw_Z|v0jOJ&@lB!9a|m~gsz6T-YyT(_ozNY`gwA^_ZQP8>J}@C4bFdR!zNk>=%O zwYwARH^BwbtLewh2`DJ|$-dVo0Ny6Q4c=QSqxMiXkP6W!4t(S(bbUqy3i>Y(bCHo0 zy6UiS5%J7pZu82}EcT*{B}mXJZBRVCd4K*ab6Bh%l!jesNFEct4tR?&$rBW+SV$7`9K;0!U}uO2<* zUTa(2fv;h11wF{N6p}fXZBO6Q$gt~F$|3iuOe+`Zk}}rdN)wkH_(2+bxrxY@EcSgZ zOt&u>+^nC@aVasC42(KOR~5FZX#$9!zr4(bOpme%nHACKY7b2;%b`<0Vo#du1!Bk# z4bL_E8Uh0#zmRCKj#c@-gI+DPMbAh8Opm*kKHz?vvm^2p-XOI>(Ef4*8xlfR=8rEE z`tx51bQr<|4)j)LUraZ!6vkmHlRUI@8Mf%lrf z1wb2y-8BSHT|;6#!f}Ovv%_<}tPz4S z9`WlCCPWnKPyt~+M_>KE82o>n4Wll8iEa9Ga!FUw;D%^5|Kz5e0uyF58BlNl6)^~; ze;S2oA|EECA!cBycw-(Pg6|r*TeUi`@q-5D{T;XN^%v>`YzkmVjx&wKOk2Ce_T`~I zwD;}5Ax%(U<|yZ`L^0!7=~?2$2X3>xmM3Uqrs~vBSA>q@&}u7V{ra_fT@Zr_jGAM3 zMcuYKVY>(1g^Jo_O1YJ;zU9+l{$Tf*bC8sDKqF_F@1irL-hLf-4c^Z@v(-NMXZzlz zYW`T}uG{iuc@vx)IVB^@O{)&UjcLfOX&Ri8d$xe}-X(Vfv$Fy(?3C<($hE5e7|@6# zrQWD9`cgtCGNL+svL8_uD(6ZZJxiThi=#jG)#hfj87;u9Eqkki`9qOswkfcY^Gh!i z*=FFX41bWlaKK$Vs#3w3=)NOYlbhVZjv3YV#c%#4^=pLVL@kL_HoJs;e*i5ij?zws G%l-msh8pqMA%$q zb1Eg&Ge*J2m+zMY4x7zBa`~nvl!99w7L*}+@)WB#gX&stN$rdi_?R&=3PSFY#MNh- z!BUemGWya@)c7oh=3EDIwqxn&8pDNoSF~%YsMMWv43Z5YY0jxeF%y>3xVrXfS6*%9 zaz!qCC3-n{Imh!&6$Of3;)xOo^*q;ujp9F3g6L0 zHZhA;hc--O2l9<=Yv+SgzzHayapQUkP;-KrHZl`P^?L z4zmt_m-?GS~qIFCt<#VmWW{5H~}un)p!){ScxF#4wk$1Z`&8V{la7Y zAG7RDUm@s7TskGc@jh!Zez9Yc%_P^CnlbhRH{Rgv!+^E$#S6f`Lyf@2PP#%9`gLWJ zg-|Nx;PL7NQi_=_%&j`l2W|dAgFmamJOQp?$D^PL7UZ^GJ)g}Z%b4E&~x zn&)kex{Y^waROk?6jwlzcV`%oK_uC)13 zWIEKe>Qc~S<4(j2ht>7ApVh3}_x8gqrw1F*Mh`ZQLXV;bbNrSZqPFGslaq@@J}nfs zmYH)`K=S21&xkB&#c*Dr3`ShK3WJwj9#y&Lh|tjN5P=68dfK|Ygke)%SsSJv~Dx}pujGZ@szty zTwi(ogsiiRnJ48_#9p;L_o(+tJMNHg+^_fJjJ|gpT)s|&??!;-LCFo0J#OmF`cLoaK{*9Y>pP{#1R3)txh_z}vJuoj zQy~wDr#b%RMorPBO$xH%F!%c4W3r_aobeaJrz7$l26aTK72{@&JjIeQ6$6gCbUg-A z(JEH(knF|deY3yz6hz7|c=`}_uW)(;+vz3CagF?8V`E+tZ`JF${w1rBHs|gg@jtRK zSr)_>|3{dX+xO|UcG-lVtYADRfwaRPK(VLOb|rave2?X>bXrJlj1b~D?-lv67l0KI z)_P2xW-VyryfCmdn{uFZPVI(5q2&*880iYPoAD^h0I=r%5dC1a00^8?{(V0gN9hgQ zEpG9=wuxt7ijC|o*6(ewmh1vqZF3Q{I{0J@17Kx*iw~wFJL?K0uFj=bP~S*Ocsn5;xNxUS|J*&fV`2Te-~ zr(4)SXKj*nOOaMK9J|Pz_5QzAIHgLOdX38WLmIO2B)gYj{tmuR;~7SG4QlKP>B)n8 z%L}zbG2>r!fR#0LJXsyaX#JxK*W@Y$Zg@a@q@4KVAS}*luRJd4CkRm`A6GKDcX|uu z^b>2{8a$nF&DN=`7G8+iBZP$PL?dY?EI|wc2%9Y zs@uwCqX$NtKUZP0L_9_t^(1vdXG$|83R*l8QzCBkhqXfB6Ro1(){|vL>coFd>|5U$ zjx!;)SA<sga6(5Ny^gamYpJ;ncW>;Sog9Rzt7S1uxzxswT6LEzc(lk8Ayuw!4&1y%g+%}VcoNrNI}D5aT6;9{5v~yeYb3;(__9rQ zJ7_J2cM?r>7mCMOb=23K6m>n=s4sn_3-?0RG7o(!+Bz@3k2i`{h9bY3*Yav0%-5v6 zwD}y_SUro9((vhhVB|*VDx7CD*mFX6%X^b%Pc#nm&b*~wZIHPG{(Cr6rqLg97a=TdvpW>!Ez1qtNN7dX1dsDkF>`oD%qIEOhQClIzu{!;Z)4YlayOR+W z7*@OOmh0}KwsOlDp%_Q1pAI9~n%)tjW-#GO>~>f6zq}iD?7b6rEEl z^rCILesr{Gy6zSQW~3Y#ll(hMOFLKtIIG0}#O8*Ys?MSv8Ch_A)aF!bh;mi=FasE9 zd03si%KT%6dRWle9#!Dvw*Gq!D2DZsFnsEgCrV1%XS*$VP9h19-XV)z=@3bw8RL;< zf7Xo@+t`UrC(Tj~%>1)*=fYXLAto_Cc=V)*GT}pvP8cGj#QB&u-IE=0>_#ISUtG;7QOkIaC<|oP-c32gWgAjnRwO7-}Y*d@}&HFh^*Eh5sM_ E1z;gAOaK4? literal 0 HcmV?d00001 From eece22750863ab0703c540dabdf2f5422a3dd6ce Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Mon, 19 Sep 2022 20:31:25 +0200 Subject: [PATCH 02/28] Spotify remote initial release restructure menu Requires Gadgetbridge 71.0 fix textinput->search menu screenshots --- apps/spotrem/ChangeLog | 5 + apps/spotrem/README.md | 21 +++ apps/spotrem/app-icon.js | 1 + apps/spotrem/app.js | 244 +++++++++++++++++++++++++++++++++++ apps/spotrem/app.png | Bin 0 -> 1877 bytes apps/spotrem/metadata.json | 17 +++ apps/spotrem/screenshot1.png | Bin 0 -> 1816 bytes apps/spotrem/screenshot2.png | Bin 0 -> 3282 bytes 8 files changed, 288 insertions(+) create mode 100644 apps/spotrem/ChangeLog create mode 100644 apps/spotrem/README.md create mode 100644 apps/spotrem/app-icon.js create mode 100644 apps/spotrem/app.js create mode 100644 apps/spotrem/app.png create mode 100644 apps/spotrem/metadata.json create mode 100644 apps/spotrem/screenshot1.png create mode 100644 apps/spotrem/screenshot2.png diff --git a/apps/spotrem/ChangeLog b/apps/spotrem/ChangeLog new file mode 100644 index 000000000..8e3d8b652 --- /dev/null +++ b/apps/spotrem/ChangeLog @@ -0,0 +1,5 @@ +0.01: New app. +0.02: Restructure menu. +0.03: change handling of intent extras. +0.04: New layout. +0.05: Add widgets field. Tweak layout. diff --git a/apps/spotrem/README.md b/apps/spotrem/README.md new file mode 100644 index 000000000..346ec9eba --- /dev/null +++ b/apps/spotrem/README.md @@ -0,0 +1,21 @@ +Requires Gadgetbridge 71.0 or later. Allow intents in Gadgetbridge in order for this app to work. + +Touch input: + +Press the different ui elements to control Podcast Addict and open menus. Press left or right arrow to go to previous/next track. + +Swipe input: + +Swipe left/right to go to previous/next track. Swipe up/down to change the volume. + +It's possible to start tracks by searching with the remote. Search term without tags will override search with tags. + +To start playing 'from cold' the command for previous/next track via touch or swipe can be used. Pressing just play/pause is not guaranteed to initiate spotify in all circumstances (this will probably change with subsequent releases). + +In order to search to play or start music from the 'Saved' menu the Android device must be awake and unlocked. The remote can wake and unlock the device if the Bangle.js has been added as a 'trusted device' under Android Settings->Security->Smart Lock->Trusted devices. + +The swipe logic was inspired by the implementation in [rigrig](https://git.tubul.net/rigrig/)'s Scrolling Messages. + +Spotify Remote was created by [thyttan](https://github.com/thyttan/). + +Spotify icon by Icons8 diff --git a/apps/spotrem/app-icon.js b/apps/spotrem/app-icon.js new file mode 100644 index 000000000..8da55b9a5 --- /dev/null +++ b/apps/spotrem/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwhC/AFV3AAQVVDKQWHDB0HC5NwCyoYMCxZJKFxgwKCxowJC6xGOJBAWPGA4MGogXOIwdCmf/AAczkhIKC4VzCogAD+YZEC49PC5AABmgXO+czJYoYDC4gfCuRYGoUjDAZ4GUJlyn4XNukjIwMzmVHBAU/+YXKoZ0GmQLCDgQXIU5IVDC5JVCIwIECDA5HIR4hkBDAX0C5YAHOoIXJa4QRDoUikiOEm7vKE4YADmZ1FC5N/R48nC5tzFQMiokimYYHC4h4KJwX3Ow6QMOwoXGSAoAKIwrBNFxIXZJBxGHGB4WIGBouJDBgWLJJYWMDBIWODIwVRAH4AXA=")) diff --git a/apps/spotrem/app.js b/apps/spotrem/app.js new file mode 100644 index 000000000..2e68fb0fb --- /dev/null +++ b/apps/spotrem/app.js @@ -0,0 +1,244 @@ +/* +Bluetooth.println(JSON.stringify({t:"intent", action:"", flags:["flag1", "flag2",...], categories:["category1","category2",...], mimetype:"", data:"", package:"", class:"", target:"", extra:{someKey:"someValueOrString"}})); +*/ + +var R; +var backToMenu = false; +var isPaused = true; + +// The main layout of the app +function gfx() { + //Bangle.drawWidgets(); + R = Bangle.appRect; + marigin = 8; + // g.drawString(str, x, y, solid) + g.clearRect(R); + g.reset(); + + g.setFont("4x6:2"); + g.setFontAlign(1, 0, 0); + g.drawString("->", R.x2 - marigin, R.y + R.h/2); + + g.setFontAlign(-1, 0, 0); + g.drawString("<-", R.x + marigin, R.y + R.h/2); + + g.setFontAlign(-1, 0, 1); + g.drawString("<-", R.x + R.w/2, R.y + marigin); + + g.setFontAlign(1, 0, 1); + g.drawString("->", R.x + R.w/2, R.y2 - marigin); + + g.setFontAlign(0, 0, 0); + g.drawString("Play\nPause", R.x + R.w/2, R.y + R.h/2); + + g.setFontAlign(-1, -1, 0); + g.drawString("Menu", R.x + 2*marigin, R.y + 2*marigin); + + g.setFontAlign(-1, 1, 0); + g.drawString("Wake", R.x + 2*marigin, R.y + R.h - 2*marigin); + + g.setFontAlign(1, -1, 0); + g.drawString("Srch", R.x + R.w - 2*marigin, R.y + 2*marigin); + + g.setFontAlign(1, 1, 0); + g.drawString("Saved", R.x + R.w - 2*marigin, R.y + R.h - 2*marigin); +} + +// Touch handler for main layout +function touchHandler(_, xy) { + x = xy.x; + y = xy.y; + len = (R.wb-1 instead of a>b. + if ((R.x-1 { + if (ud) Bangle.musicControl(ud>0 ? "volumedown" : "volumeup"); + } + ); + Bangle.on("touch", touchHandler); + Bangle.on("swipe", swipeHandler); +} + + + +// Get back to the main layout +function backToGfx() { + E.showMenu(); + g.clear(); + g.reset(); + Bangle.removeAllListeners("touch"); + Bangle.removeAllListeners("swipe"); + setUI(); + gfx(); + backToMenu = false; +} + +/* +The functions for interacting with Android and the Spotify app +*/ + +simpleSearch = ""; +function simpleSearchTerm() { // 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);}); +} + +artist = ""; +function artistSearchTerm() { // input artist to search for + require("textinput").input({text:artist}).then(result => {artist = result;}).then(() => {E.showMenu(searchMenu);}); +} + +track = ""; +function trackSearchTerm() { // input track to search for + require("textinput").input({text:track}).then(result => {track = result;}).then(() => {E.showMenu(searchMenu);}); +} + +album = ""; +function albumSearchTerm() { // input album to search for + require("textinput").input({text:album}).then(result => {album = result;}).then(() => {E.showMenu(searchMenu);}); +} + +function searchPlayWOTags() {//make a spotify search and play using entered terms + 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"]})); +} + +function searchPlayWTags() {//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+"\"")); + 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 + 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 + 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 + 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 + 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 + 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) { + Bluetooth.println(JSON.stringify({t:"intent", action:("com.spotify.mobile.android.ui.widget."+action), package:"com.spotify.music", target:"broadcastreceiver"})); +} + +function gadgetbridgeWake() { + 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() { + 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"*/]})); +} + +// Spotify Remote Menu +var spotifyMenu = { + "" : { title : " ", + back: backToGfx }, + "Controls" : ()=>{E.showMenu(controlMenu);}, + "Search and play" : ()=>{E.showMenu(searchMenu);}, + "Saved music" : ()=>{E.showMenu(savedMenu);}, + "Wake the android" : function() {gadgetbridgeWake();gadgetbridgeWake();}, + "Exit Spotify Remote" : ()=>{load();} +}; + + +var controlMenu = { + "" : { title : " ", + back: () => {if (backToMenu) E.showMenu(spotifyMenu); + if (!backToMenu) backToGfx();} }, + "Play" : ()=>{Bangle.musicControl("play");}, + "Pause" : ()=>{Bangle.musicControl("pause");}, + "Previous" : ()=>{spotifyWidget("PREVIOUS");}, + "Next" : ()=>{spotifyWidget("NEXT");}, + "Play (widget, next then previous)" : ()=>{spotifyWidget("NEXT"); spotifyWidget("PREVIOUS");}, + "Messages Music Controls" : ()=>{load("messagesmusic.app.js");}, +}; + +var searchMenu = { + "" : { title : " ", + back: () => {if (backToMenu) E.showMenu(spotifyMenu); + if (!backToMenu) backToGfx();} }, + "Search term w/o tags" : ()=>{simpleSearchTerm();}, + "Execute search and play w/o tags" : ()=>{searchPlayWOTags();}, + "Search term w tag \"artist\"" : ()=>{artistSearchTerm();}, + "Search term w tag \"track\"" : ()=>{trackSearchTerm();}, + "Search term w tag \"album\"" : ()=>{albumSearchTerm();}, + "Execute search and play with tags" : ()=>{searchPlayWTags();}, + "Play \"Vreden\" by Sara Parkman via uri-link" : ()=>{playVreden();}, + "Play \"Vreden\" by Sara Parkman via search&play" : ()=>{searchPlayVreden();}, + "Open \"The Blue Room\" EP (no autoplay)" : ()=>{openAlbum();}, + "Play \"The Blue Room\" EP via search&play" : ()=>{searchPlayAlbum();}, + "Play playlist Discover Weekly" : ()=>{spotifyPlaylistDW();}, +}; + +var savedMenu = { + "" : { title : " ", + back: () => {if (backToMenu) E.showMenu(spotifyMenu); + if (!backToMenu) backToGfx();} }, + "Play \"Vreden\" by Sara Parkman via uri-link" : ()=>{playVreden();}, + "Open \"The Blue Room\" EP (no autoplay)" : ()=>{openAlbum();}, + "Play \"The Blue Room\" EP via search&play" : ()=>{searchPlayAlbum();}, + "Play playlist Discover Weekly" : ()=>{spotifyPlaylistDW();}, +}; + +Bangle.loadWidgets(); +setUI(); +gfx(); diff --git a/apps/spotrem/app.png b/apps/spotrem/app.png new file mode 100644 index 0000000000000000000000000000000000000000..3c0d65eee994757cfcee51e2191164cc49f3179b GIT binary patch literal 1877 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 literal 0 HcmV?d00001 diff --git a/apps/spotrem/screenshot2.png b/apps/spotrem/screenshot2.png new file mode 100644 index 0000000000000000000000000000000000000000..7801a3034027861558ee960b030561276d578bbc GIT binary patch literal 3282 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 qw~cha>S{eO_ zTl(gA!A{;qL3?54gB$#AVp6WbCvq&5JNp^fwCA5~u_INpxVw`_gAoXQzlP7K{ZnX+ z6Ow*eu7R}7{c6mY*4X_iPnwn@Z4PZcW&lcPt|=6gPBrL};wr#iB{e@h-_^+j40&_@ z9q!a24TJwt^^J{fO@{6Dp#C{o^~NKD{^=1d+|9CROuW~*di7WjIEf81qs--4S)?Rf zQSY1ITMga3+PFFpm$QC4u@~m^p?2evwM9U0%cVu+e8gHwUq_R@)+CAd$52%J$b3-S z9=_n7g93b#UXnn8)~Uh8dd7EpY)MT$j_7I$$O^G+$(T|Yh7+&6ufOO%v~uDO939WB zb)4Ws7j$wNt6x|$lDva#_E2A^nB3phc&P@nMvFEszx=}8-=|~I)4(;UgSzl_^KcB! z9K-hJ)ekU7K^gqVU-LR~lKahD@4uGAQ7 zr;tQtcy%YF0+3o z#l16VU`hW{tq8l`$98=t4!*Ms(fwzjfa5BQN3_B=;jx^iK-FSl?W)R>%3_{{i@9P1OJZ literal 0 HcmV?d00001 From 4c3504a8974378ad2342988ed190243dae4adbe5 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Mon, 10 Oct 2022 18:44:46 +0200 Subject: [PATCH 03/28] change ui color --- apps/podadrem/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/podadrem/app.js b/apps/podadrem/app.js index a9b5514b8..c7db1f781 100644 --- a/apps/podadrem/app.js +++ b/apps/podadrem/app.js @@ -77,6 +77,7 @@ function gfx() { g.reset(); g.setFont("4x6:2"); + g.setColor(0xFD20); g.setFontAlign(1, 0, 0); g.drawString("->", R.x2 - marigin, R.y + R.h/2); From 6d839dd6c7f55ca007594f6a4263eb596590f914 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Mon, 10 Oct 2022 18:46:49 +0200 Subject: [PATCH 04/28] change ui color --- apps/spotrem/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/spotrem/app.js b/apps/spotrem/app.js index 2e68fb0fb..9ac03d1ff 100644 --- a/apps/spotrem/app.js +++ b/apps/spotrem/app.js @@ -15,6 +15,7 @@ function gfx() { g.clearRect(R); g.reset(); + g.setColor(0x07E0); g.setFont("4x6:2"); g.setFontAlign(1, 0, 0); g.drawString("->", R.x2 - marigin, R.y + R.h/2); From 6f80f875373b623c9924724cf8746e175c4e1555 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Tue, 11 Oct 2022 00:55:51 +0200 Subject: [PATCH 05/28] dark/light theme --- apps/spotrem/app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/spotrem/app.js b/apps/spotrem/app.js index 9ac03d1ff..f0787b57d 100644 --- a/apps/spotrem/app.js +++ b/apps/spotrem/app.js @@ -5,6 +5,7 @@ Bluetooth.println(JSON.stringify({t:"intent", action:"", flags:["flag1", "flag2" var R; var backToMenu = false; var isPaused = true; +var dark = g.theme.dark; // bool // The main layout of the app function gfx() { @@ -15,7 +16,7 @@ function gfx() { g.clearRect(R); g.reset(); - g.setColor(0x07E0); + if (dark) {g.setColor(0x07E0);} else {g.setColor(0x03E0);} // Green on dark theme, DarkGreen on light theme. g.setFont("4x6:2"); g.setFontAlign(1, 0, 0); g.drawString("->", R.x2 - marigin, R.y + R.h/2); From 558025b1cb398208b38112e2806bd304d08d4780 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Tue, 11 Oct 2022 01:02:52 +0200 Subject: [PATCH 06/28] dark/light theme --- apps/podadrem/app.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/podadrem/app.js b/apps/podadrem/app.js index c7db1f781..b04d80b17 100644 --- a/apps/podadrem/app.js +++ b/apps/podadrem/app.js @@ -67,17 +67,19 @@ com.bambuna.podcastaddict.service.player.togglespeed – This will toggle the Pl var R; var backToMenu = false; +var dark = g.theme.dark; // bool // The main layout of the app function gfx() { + //Bangle.drawWidgets(); R = Bangle.appRect; marigin = 8; // g.drawString(str, x, y, solid) g.clearRect(R); g.reset(); - + + if (dark) {g.setColor(0xFD20);} else {g.setColor(0xF800);} // Orange on dark theme, RED on light theme. g.setFont("4x6:2"); - g.setColor(0xFD20); g.setFontAlign(1, 0, 0); g.drawString("->", R.x2 - marigin, R.y + R.h/2); From 2a55ebbfca118150a4dba2d7ca16f6bcd8716b24 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Tue, 11 Oct 2022 01:24:55 +0200 Subject: [PATCH 07/28] Spotify generated playlists --- apps/spotrem/app.js | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/apps/spotrem/app.js b/apps/spotrem/app.js index f0787b57d..50767ae13 100644 --- a/apps/spotrem/app.js +++ b/apps/spotrem/app.js @@ -190,6 +190,38 @@ function spotifyPlaylistDW() { 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() { + 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 spotifyPlaylistDM2() { + 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 spotifyPlaylistDM3() { + 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 spotifyPlaylistDM4() { + 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 spotifyPlaylistDM5() { + 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 spotifyPlaylistDM6() { + 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 spotifyPlaylistDD() { + 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 spotifyPlaylistRR() { + 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"*/]})); +} + // Spotify Remote Menu var spotifyMenu = { "" : { title : " ", @@ -235,10 +267,18 @@ var savedMenu = { "" : { title : " ", back: () => {if (backToMenu) E.showMenu(spotifyMenu); if (!backToMenu) backToGfx();} }, + "Play playlist Discover Weekly" : ()=>{spotifyPlaylistDW();}, + "Play Daily Mix 1" : ()=>{spotifyPlaylistDM1();}, + "Play Daily Mix 2" : ()=>{spotifyPlaylistDM2();}, + "Play Daily Mix 3" : ()=>{spotifyPlaylistDM3();}, + "Play Daily Mix 4" : ()=>{spotifyPlaylistDM4();}, + "Play Daily Mix 5" : ()=>{spotifyPlaylistDM5();}, + "Play Daily Mix 6" : ()=>{spotifyPlaylistDM6();}, + "Play Daily Drive" : ()=>{spotifyPlaylistDD();}, + "Play Release Radar" : ()=>{spotifyPlaylistRR();}, "Play \"Vreden\" by Sara Parkman via uri-link" : ()=>{playVreden();}, "Open \"The Blue Room\" EP (no autoplay)" : ()=>{openAlbum();}, "Play \"The Blue Room\" EP via search&play" : ()=>{searchPlayAlbum();}, - "Play playlist Discover Weekly" : ()=>{spotifyPlaylistDW();}, }; Bangle.loadWidgets(); From 2cee9ac556db418a90c65faef9b0992523f129a0 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Tue, 11 Oct 2022 01:33:22 +0200 Subject: [PATCH 08/28] clean up --- apps/spotrem/app.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/spotrem/app.js b/apps/spotrem/app.js index 50767ae13..1e969f3c9 100644 --- a/apps/spotrem/app.js +++ b/apps/spotrem/app.js @@ -256,18 +256,13 @@ var searchMenu = { "Search term w tag \"track\"" : ()=>{trackSearchTerm();}, "Search term w tag \"album\"" : ()=>{albumSearchTerm();}, "Execute search and play with tags" : ()=>{searchPlayWTags();}, - "Play \"Vreden\" by Sara Parkman via uri-link" : ()=>{playVreden();}, - "Play \"Vreden\" by Sara Parkman via search&play" : ()=>{searchPlayVreden();}, - "Open \"The Blue Room\" EP (no autoplay)" : ()=>{openAlbum();}, - "Play \"The Blue Room\" EP via search&play" : ()=>{searchPlayAlbum();}, - "Play playlist Discover Weekly" : ()=>{spotifyPlaylistDW();}, }; var savedMenu = { "" : { title : " ", back: () => {if (backToMenu) E.showMenu(spotifyMenu); if (!backToMenu) backToGfx();} }, - "Play playlist Discover Weekly" : ()=>{spotifyPlaylistDW();}, + "Play Discover Weekly" : ()=>{spotifyPlaylistDW();}, "Play Daily Mix 1" : ()=>{spotifyPlaylistDM1();}, "Play Daily Mix 2" : ()=>{spotifyPlaylistDM2();}, "Play Daily Mix 3" : ()=>{spotifyPlaylistDM3();}, From e5e9394ef5afd05be14f8868a9db67a2f0b8a44d Mon Sep 17 00:00:00 2001 From: thyttan <97237430+thyttan@users.noreply.github.com> Date: Tue, 11 Oct 2022 01:40:56 +0200 Subject: [PATCH 09/28] fix generated spotify playlists --- apps/spotrem/app.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/spotrem/app.js b/apps/spotrem/app.js index 1e969f3c9..7e76d84bc 100644 --- a/apps/spotrem/app.js +++ b/apps/spotrem/app.js @@ -191,35 +191,35 @@ function spotifyPlaylistDW() { } function spotifyPlaylistDM1() { - 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:37i9dQZF1E365VyzxE0mxF:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); } function spotifyPlaylistDM2() { - 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:37i9dQZF1E38LZHLFnrM61:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); } function spotifyPlaylistDM3() { - 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:37i9dQZF1E36RU87qzgBFP:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); } function spotifyPlaylistDM4() { - 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:37i9dQZF1E396gGyCXEBFh:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); } function spotifyPlaylistDM5() { - 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:37i9dQZF1E37a0Tt6CKJLP:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); } function spotifyPlaylistDM6() { - 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:37i9dQZF1E36UIQLQK79od:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); } function spotifyPlaylistDD() { - 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:37i9dQZF1EfWFiI7QfIAKq:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]})); } function spotifyPlaylistRR() { - 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: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 From dd44c49d1ff29302c93589c420541d2089eae839 Mon Sep 17 00:00:00 2001 From: thyttan <97237430+thyttan@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:06:33 +0200 Subject: [PATCH 10/28] Add files via upload --- apps/spotrem/screenshot1.png | Bin 1816 -> 2361 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/spotrem/screenshot1.png b/apps/spotrem/screenshot1.png index 3edd7a1c607290afd68eff2f989783b27c306964..730e98547c7e4ed5b73ee82cdf6fd4c5cf649da3 100644 GIT binary patch literal 2361 zcmdT`>pPT-7Jt3-;*BFlNw*OO?V=$iGDxy#h+HPd)Qs50(^{n;$p2hNTBdV!vRRI7% z&DqJ3BuDOl3|5kR4sV1m2Zc){q66@>dHXm3z;n)yhrJl|sf@wM9P586eU` z%6Pdvl9WkpiVY)ZMe6!~cVIF&$1E1qdfiGt zg#U$(X(?H8dKc?xF`ddUn>~5d#<+cKF=v<>*c;DVv+0uBIhq%S zf-@>sm_=ne0=Py>P_|SdHWGz+YP_p(zTD2_UiEpL|vr7D&H?;-dc1_%YP^oBWEo3~oruXf``rHX$FxB#Ot)}ejCq0Sp^3+Tf zT~Hn{?%tUg`ZbpyG#VR`&W7TaestS%=M~MWmGe*&k7Vzv#r1nw>Wb`S%OZZ&l>{@b zh3XkMn!D_4>Z6L1&9~<2L`IpYq;szW%02yKhh;Pw=NXnh$wLR$m_lnlurady?jbYf ztg>B<3cB=~N3J)MKHue%!TBa!utAd6(Rv%Dc8FEAGMSMQ4;RDqVFsuEv%B7DgXwPJ zPlJR^m3)Q>d_jm7XTBk3qQr)ECzaV+v~?E>NsEZchq*Unu$IKb|c9aci#VmPw) zVyiKAZ3!hM?M8m-?fqTcx{79;K9oM7RT`SqW8MyhMhNFuQ;fs38U2(~2P#7!#|`HT z7CY>ss($c#)O^)P(h>?HKA+Ua$zGP=XjA+>WL8?kvG0}B=2A`Bui}}?hk6L7`ilAc zqX1GyUNf2?45V!|pD@?3IzM{hStwNJtXGCZ{nwDx#6Y~lXp4PA@N=(+C!Mts!B|IK zbZLbu&-s#49*WtHL2UEM0s4|gkA2C?3XnAG=!Yb<{XkOoX?=ANo;-Z_{tm2}shOq8 zlz++h%_S?5tS_>^)3@kygE(P0_F2)mBJS)xA4#jv2br*Nj-2wkFzvzCW;60blsh8PxVS&>-QHS$#f9nV{=203_Lc~wOj_k)F51)MNr`D>Cjc~#&nPZ+a)9}nzG(pxRTl1iM79p z(f#y+k`WQkJ0e(E-MyUFP01cAgenTfP2hqUDcO{7p9%f7w*Sq%6ntU{0LjodeAp@J zhYU6VKJoDj0pOa*)wZA{{lCor-9wFgu$Hm3X3J^ITO+l;JCh}F{#2k9XQeb!OONz&6#)uVIRCIi%{jBjBDW*n zub6K{wX7q4KVKQM0*z@RL*EEW1Z^tW``4xKms+v(63o~I_6vZ8a;e*R&7@h1qbR8<8Dw|exDi6<9v3~d%qm$_b2RBBvtBv;T zLcUybs~)9eN#bp4qVO(hL3v-}`Ozm`ZX4&e0`8~wmXZYsdSM4ba)RvseQr1<2@X!3 zuD}+or=8|B@Dhtuv(m4(Z1Jfb7xOzinsInT1F*OBz>TZ?J$fyXP7|v9thZ6?5k6c~ z-VYZ)hmK16+yu-Gl)nAEA=8o!-&r_;A9+C0(P01>b3IE0kSU7hu6O`cwhuar2jyMf zc2a;etN`odJ!pGB{5?k%-FUvjrw46`fSkvG1Sml3SyxiE+wgR{se< z!T7&RoQ;K9z3ZBstj2rPA{v@Or!^JNOls;E0ZpmmIJ6P_kOz`VG zKX_TZy$XB5%}Po97v>Eq=;Z}F#ZJmB8}KxUiabG!l+N&En@ z<=1fsS1MSi%fJ9dOQT46^ c&I5;xDp0mpn*%-MzYyU3vzy~n2fyF`0WPOujQ{`u literal 1816 zcmeH|*;7*o6vmTFE+$tIkRXUkEkZ(#HCRwF5Vi)8Di9U}6^yYu21IBmRR}@hR%*2p zR8Tf0pm9N2wZ#NQyyMzpAQMEQCSY)ZpdcU#AQwn)XY5P=h4$f`@8x`RX3qTPJ4e`I z!4{AMgu~%1LN>4>F>Uy}h~`+Ym%Zjc5t4&E^HhmgNBS zZXdgPgwf!E2e~))uj0acd5Hkj{39BlvbLTgXwg^%~#{3!8Nq!lS`@7<>O>#u2eK6wEAexOuTCo3ok$E zs}4TRkW4mteNPvlghu}Ps#29v+1TD{q8v9azJ4LI(Ns)r0DQkTXy$4~G_j;XKjm(S zFaS2vDsMd4wyK5B{dd5>PsVq5)2Xd$U**brC;?JAm+8$>g##v1>cebMlJ{rRV&5bXmw)P9< z;$xILo5>ObRD(RsD;-P$9myHDYniX!n;gry+$uZhU-h)TI(cmF4Uy?87H}|$5&icI6o)Laz$!>)l>J_B{43Vx5yZjI?CGvQ*Z+z97gH*@dqCd@Acvng@SA zL$RX$Fls4F)*LXIK(D0c$lPaZ3mr)Ip?duoH2tDwYbwn_%USEmqu*?C{2|lz~XM<`EuSTrAwGp<>NJMQtJp&As zT?Sf9dY8Bzk35JSi5wed2^b)<7B sHPspq<`aeccLR|3UQvAfKUQShrqO)nn;S$a_UdpUfnlt=0Nzi30p0^OSO5S3 From 64a440c6b5a95836483535fea725ebddc3afe426 Mon Sep 17 00:00:00 2001 From: thyttan <97237430+thyttan@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:07:39 +0200 Subject: [PATCH 11/28] Add files via upload --- apps/podadrem/screenshot1.png | Bin 1836 -> 2757 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/podadrem/screenshot1.png b/apps/podadrem/screenshot1.png index e028b331824cd45a6217ea520e68e91442e09215..50f3f17f1918f7f5d962ba754a93d9e91bd0d525 100644 GIT binary patch literal 2757 zcmeHJ`#;nBAD>U-Fe#VE)WajUSR`8Ih}Uzc`}(H8Ba}hqQV{9{q^@VrGz0>pp*@acs1ft`z~pkr?Vbf& zTVLif)b2PI9f7p^F~J)6ZEM}lEu`t4?{rBor5ai9IkkKb~;v#?zD2bJk!5PD zu3(~@!Ch~~g~$`3kGXw4^f$tvlV~VCQsr{Zv2fcbQ#-*-H0&jLdZPG8JcC!$p*c#| zj7Vw0WyZ2g0)Q}8&ho-W8uwM5+beYl=uf$<&y45Tkq=+!tBz-=wFfJCA;d~S*`Ns zxTH4?hzWyO#nGvCwU_W)7qlmjj>OE3+aqJNV)>(j<+jP#lVC8Ag;Z_y1lZ+F` zdqYejEB?gUjo)e}Yz?wVTJn7dU*c#wH`Q8lxb1^dmmVas&nb4>M3grkA!70InSAoS zLI}XLr{uKAuKSs61D|T&2bhR6IhP6`fLTYihRN6J{&yms-9PyIK|sBJ`*h?=Dm!m| z--odsbbj`G2@;3j^bLy^$TjGEjpqjsAQ=uSOBUvU1;^%}@ex3B9Fgn>U_@7IruTyo zz``BzlN+EumS^XJ(2muot{qyO-LNOnyzrZ4ho<^7ockBr?lvi&V^4?ChFza&t}7hY z_mURBU%2SA`%7_(ai^P_!9G&M&Urv^ma_5A5eH9|Jv^#6(Cv09)9s`x4*sF@)g2V$ z)8LD~gCwygTD-juYi74BXsNvN8?2@yRvg(?-YNa0X>CP1wyn$VPRZvP>Va$B%Ys0# zJkr#sY*^t_&*!2{1_rz1I@mYz_gC1<;2kWhsuy020AzndI*;E0Bk=YI+F}foRQZZm z8q`!&?Ss6j2jRpgYKiwA4BM_+HnYnl;_;GCxZHWbiDKFkFG{BCw+0NuzRpS5;Q%%Y zd$k2V-t{z`C^^{f>^^HiRl$7I;|5Htrf_Ao@dSo(lBawrLI2~Z>Aa~n8UmVK&}BQR zKo@fF?l99ZA({I{lP#|C$MgH6%vGSd73i7;sCKhcvSW_IKP8ec4oyCBE_$+>?%FEz@QGK& zFG|ZRq^ER(HP);7T#K6rfnD>RG$swnJXEq_?!(+6iKi>?DRun~FWAxOTDqFX66fqz zBGcikK17`Wmkk%xzm~l`iHNV206Sc5)}UD*Rm%{tgBRW_QLR&y5)--lb%ufgQA?k4 zg)mIEq!{1P`eSEbGX6mdp*CN@#xe8FIJ(UZyui`3mIq>=2h>W=fLMqGLYMN zZvH#fw?L*?t}BthJIJZ>5awRkI(dzeL4t1$>6CG*3nCq#!GFZ>7Y=Umrs@l26Vwni z)>DQfMLJ4{48Q~M)!59OI+6U$2g+M@f>MZa{n?k4gu>(VuGT(!i>E|B%j7UKPVhEb z>)&|U#W4xHew&d_vMtPp1PU+0_ z9C!Kr{XScJr_<%__^}$su)({vjQ&t3WK#<0sfx4xw080Hm*QDfa>a&v3@#>65d!nh zm()MHzqpzvbO2~p3xue6Mk5Z5RplrjP!@4m;+yPTxwW}oi90^vk~5LyQWp`G2f)~T zF@%LgFm8mG7N5<`DtnE39ny1VK%i^J@=LIufd?)&8M$4_tEhVwPCN~Uov#Z0rCM{n zYtjr$T!B?BMEx}~c*WU~IS<^dY-;N1&1V%+j^sF`#J}a@e93f{8?wJ1zs7XQm~=ZG zZ^`uviWg7ff2T~T!-Ic5^RBD2Hl-lf$gL!k10$3^sfANjf_@jjM|dXmdV&IZ1# zUB~VruUiVvfq5z2sWIAF5e2fBR?r2%G|uK<1}U7+SG9fsQ3CDBQN%UA#;-qpO10Ro zcxI~OU`iG*w5juSGzyP-ctibt+lGe3%JTUJ laoJS`+H>A_XEti?P%a+P+^*ei^wGe1EEj_ zlTbzkK_wLw%hUjMFfd3kD1~w{kxmd50$9tXLQIC(q(}%ooxb)Tv=4i)J$tRS*ULBG zt~?nRz1@1JH2?tHX~!rDNbLN3p)HWTqx-iMB%pE=qRBwJ+~s!wuvnx~jwBVPs2krD z&3Iye0d(|{bTRq`Dgky zM%PvgA5++6wxu&>|7?WG&$XGee+(qG1yv1xw>O6X( zV?`XxI#K*yzF_W`e3DJAYILTTwC5$6Zk4{nN$wnez4gY`iD$rEWb>GFF)a>`^tq$e z>~pWCiIdnn)MFAeg7#B8A^$=skLWbfJ-$p@Tkf8yY3*qexFX~BBx!N5r9QB= ztOfw_Z|v0jOJ&@lB!9a|m~gsz6T-YyT(_ozNY`gwA^_ZQP8>J}@C4bFdR!zNk>=%O zwYwARH^BwbtLewh2`DJ|$-dVo0Ny6Q4c=QSqxMiXkP6W!4t(S(bbUqy3i>Y(bCHo0 zy6UiS5%J7pZu82}EcT*{B}mXJZBRVCd4K*ab6Bh%l!jesNFEct4tR?&$rBW+SV$7`9K;0!U}uO2<* zUTa(2fv;h11wF{N6p}fXZBO6Q$gt~F$|3iuOe+`Zk}}rdN)wkH_(2+bxrxY@EcSgZ zOt&u>+^nC@aVasC42(KOR~5FZX#$9!zr4(bOpme%nHACKY7b2;%b`<0Vo#du1!Bk# z4bL_E8Uh0#zmRCKj#c@-gI+DPMbAh8Opm*kKHz?vvm^2p-XOI>(Ef4*8xlfR=8rEE z`tx51bQr<|4)j)LUraZ!6vkmHlRUI@8Mf%lrf z1wb2y-8BSHT|;6#!f}Ovv%_<}tPz4S z9`WlCCPWnKPyt~+M_>KE82o>n4Wll8iEa9Ga!FUw;D%^5|Kz5e0uyF58BlNl6)^~; ze;S2oA|EECA!cBycw-(Pg6|r*TeUi`@q-5D{T;XN^%v>`YzkmVjx&wKOk2Ce_T`~I zwD;}5Ax%(U<|yZ`L^0!7=~?2$2X3>xmM3Uqrs~vBSA>q@&}u7V{ra_fT@Zr_jGAM3 zMcuYKVY>(1g^Jo_O1YJ;zU9+l{$Tf*bC8sDKqF_F@1irL-hLf-4c^Z@v(-NMXZzlz zYW`T}uG{iuc@vx)IVB^@O{)&UjcLfOX&Ri8d$xe}-X(Vfv$Fy(?3C<($hE5e7|@6# zrQWD9`cgtCGNL+svL8_uD(6ZZJxiThi=#jG)#hfj87;u9Eqkki`9qOswkfcY^Gh!i z*=FFX41bWlaKK$Vs#3w3=)NOYlbhVZjv3YV#c%#4^=pLVL@kL_HoJs;e*i5ij?zws G%l-msh8 Date: Fri, 14 Oct 2022 12:33:24 +0200 Subject: [PATCH 12/28] change name --- apps/spotrem/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/spotrem/metadata.json b/apps/spotrem/metadata.json index 7353be36d..a0261ba13 100644 --- a/apps/spotrem/metadata.json +++ b/apps/spotrem/metadata.json @@ -1,6 +1,6 @@ { "id": "spotrem", - "name": "Spotify Remote", + "name": "Remote for Spotify", "version": "0.05", "description": "Control spotify on your android device.", "readme": "README.md", From 13d17b25a6e34d4de4b1756504c91b97d9e5da0f Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Thu, 20 Oct 2022 19:24:06 +0200 Subject: [PATCH 13/28] setting - Adds setting for launcher --- apps/setting/settings.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 36af93a8a..d7b81fd59 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -87,6 +87,7 @@ function showSystemMenu() { /*LANG*/'LCD': ()=>showLCDMenu(), /*LANG*/'Locale': ()=>showLocaleMenu(), /*LANG*/'Select Clock': ()=>showClockMenu(), + /*LANG*/'Select Launcher': ()=>showLauncherMenu(), /*LANG*/'Date & Time': ()=>showSetTimeMenu() }; @@ -671,6 +672,35 @@ function showClockMenu() { } return E.showMenu(clockMenu); } +function showLauncherMenu() { + var launcherApps = require("Storage").list(/\.info$/) + .map(app => {var a=storage.readJSON(app, 1);return (a&&a.type == "launch")?a:undefined}) + .filter(app => app) // filter out any undefined apps + .sort((a, b) => a.sortorder - b.sortorder); + const launcherMenu = { + '': { + 'title': /*LANG*/'Select Launcher', + }, + '< Back': ()=>showSystemMenu(), + }; + launcherApps.forEach((app, index) => { + var label = app.name; + if ((!settings.launcher && index === 0) || (settings.launcher === app.src)) { + label = "* " + label; + } + launcherMenu[label] = () => { + if (settings.launcher !== app.src) { + settings.launcher = app.src; + updateSettings(); + showMainMenu(); + } + }; + }); + if (launcherApps.length === 0) { + launcherMenu[/*LANG*/"No Launchers Found"] = () => { }; + } + return E.showMenu(launcherMenu); +} function showSetTimeMenu() { d = new Date(); From 1e3e1af2f96713d93ef25861b26c239613bde41c Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Thu, 20 Oct 2022 21:22:38 +0200 Subject: [PATCH 14/28] setting - Bump version --- apps/setting/ChangeLog | 1 + apps/setting/metadata.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index a11995dd4..12358f88e 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -55,3 +55,4 @@ 0.48: Allow reading custom themes from files 0.49: Now reloads settings properly after 'Calibrate Battery' 0.50: Add Bangle.js 2 touchscreen calibration - for 2v16 or 2v15 cutting edge builds +0.51: Add setting for configuring a launcher diff --git a/apps/setting/metadata.json b/apps/setting/metadata.json index c114e9daf..210ebf0f4 100644 --- a/apps/setting/metadata.json +++ b/apps/setting/metadata.json @@ -1,7 +1,7 @@ { "id": "setting", "name": "Settings", - "version": "0.50", + "version": "0.51", "description": "A menu for setting up Bangle.js", "icon": "settings.png", "tags": "tool,system", From a1b279ad79332273527a1d833d04beeade8c542a Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Fri, 21 Oct 2022 17:19:39 +0200 Subject: [PATCH 15/28] iconlaunch - Cache icon images on demand --- apps/iconlaunch/app.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/apps/iconlaunch/app.js b/apps/iconlaunch/app.js index 0f20efa67..d9f6e7280 100644 --- a/apps/iconlaunch/app.js +++ b/apps/iconlaunch/app.js @@ -22,10 +22,6 @@ }) }; s.writeJSON("launch.cache.json", launchCache); } - let apps = launchCache.apps; - apps.forEach((app) => { - if (app.icon) app.icon = s.read(app.icon); - }); let scroll = 0; let selectedItem = -1; const R = Bangle.appRect; @@ -37,12 +33,13 @@ g.clearRect(r.x, r.y, r.x + r.w - 1, r.y + r.h - 1); let x = 0; for (let i = itemI * appsN; i < appsN * (itemI + 1); i++) { - if (!apps[i]) break; + if (!launchCache.apps[i]) break; x += whitespace; - if (!apps[i].icon) { + if (!launchCache.apps[i].icon) { g.setFontAlign(0, 0, 0).setFont("12x20:2").drawString("?", x + r.x + iconSize / 2, r.y + iconSize / 2); } else { - g.drawImage(apps[i].icon, x + r.x, r.y); + if (!launchCache.apps[i].icondata) launchCache.apps[i].icondata = s.read(launchCache.apps[i].icon); + g.drawImage(launchCache.apps[i].icondata, x + r.x, r.y); } if (selectedItem == i) { g.drawRect( @@ -69,7 +66,7 @@ }; let lastIsDown = false; let drawText = function(i) { - const selectedApp = apps[selectedItem]; + const selectedApp = launchCache.apps[selectedItem]; const idy = (selectedItem - (selectedItem % 3)) / 3; if (!selectedApp || i != idy) return; const appY = idxToY(idy) + iconSize / 2; @@ -87,13 +84,13 @@ let selectItem = function(id, e) { const iconN = E.clip(Math.floor((e.x - R.x) / itemSize), 0, appsN - 1); const appId = id * appsN + iconN; - if( settings.direct && apps[appId]) + if( settings.direct && launchCache.apps[appId]) { - loadApp(apps[appId].src); + loadApp(launchCache.apps[appId].src); return; } - if (appId == selectedItem && apps[appId]) { - const app = apps[appId]; + if (appId == selectedItem && launchCache.apps[appId]) { + const app = launchCache.apps[appId]; if (!app.src || s.read(app.src) === undefined) { E.showMessage( /*LANG*/ "App Source\nNot found"); } else { @@ -125,7 +122,7 @@ }; drawItems(); g.flip(); - const itemsN = Math.ceil(apps.length / appsN); + const itemsN = Math.ceil(launchCache.apps.length / appsN); let onDrag = function(e) { g.setColor(g.theme.fg); g.setBgColor(g.theme.bg); @@ -189,7 +186,8 @@ loadApp = function(name) { Bangle.setUI(); if (watch) clearWatch(watch); - apps = []; + delete launchCache; + delete launchHash; delete drawItemAuto; delete drawText; delete selectItem; From 5fdbccf1974fb4350a706b54d03358592d1c4146 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Fri, 21 Oct 2022 17:20:09 +0200 Subject: [PATCH 16/28] iconlaunch - Bump version --- apps/iconlaunch/ChangeLog | 1 + apps/iconlaunch/metadata.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/iconlaunch/ChangeLog b/apps/iconlaunch/ChangeLog index afea3263a..18d3af4ac 100644 --- a/apps/iconlaunch/ChangeLog +++ b/apps/iconlaunch/ChangeLog @@ -4,3 +4,4 @@ 0.04: Support new fast app switching 0.05: Allow to directly eval apps instead of loading 0.06: Cache apps for faster start +0.07: Read app icons on demand diff --git a/apps/iconlaunch/metadata.json b/apps/iconlaunch/metadata.json index d544da73e..ba2619fc2 100644 --- a/apps/iconlaunch/metadata.json +++ b/apps/iconlaunch/metadata.json @@ -2,7 +2,7 @@ "id": "iconlaunch", "name": "Icon Launcher", "shortName" : "Icon launcher", - "version": "0.06", + "version": "0.07", "icon": "app.png", "description": "A launcher inspired by smartphones, with an icon-only scrollable menu.", "tags": "tool,system,launcher", From 3c57eeeffd1bde8307a570bbf908ee273e9a8dae Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Fri, 21 Oct 2022 17:32:53 +0200 Subject: [PATCH 17/28] new widget: widmsggrid: Messages Grid Widget Displays multiple notification icons in a grid. --- apps/widmsggrid/ChangeLog | 1 + apps/widmsggrid/README.md | 23 +++++++++ apps/widmsggrid/metadata.json | 16 ++++++ apps/widmsggrid/screenshot.png | Bin 0 -> 10727 bytes apps/widmsggrid/widget.js | 92 +++++++++++++++++++++++++++++++++ apps/widmsggrid/widget.png | Bin 0 -> 10457 bytes 6 files changed, 132 insertions(+) create mode 100644 apps/widmsggrid/ChangeLog create mode 100644 apps/widmsggrid/README.md create mode 100644 apps/widmsggrid/metadata.json create mode 100644 apps/widmsggrid/screenshot.png create mode 100644 apps/widmsggrid/widget.js create mode 100644 apps/widmsggrid/widget.png diff --git a/apps/widmsggrid/ChangeLog b/apps/widmsggrid/ChangeLog new file mode 100644 index 000000000..4be6afb16 --- /dev/null +++ b/apps/widmsggrid/ChangeLog @@ -0,0 +1 @@ +0.01: New widget! \ No newline at end of file diff --git a/apps/widmsggrid/README.md b/apps/widmsggrid/README.md new file mode 100644 index 000000000..7c85d5c31 --- /dev/null +++ b/apps/widmsggrid/README.md @@ -0,0 +1,23 @@ +# Messages Grid Widget + +Widget that displays multiple notification icons in a grid. +The widget has a fixed size: if there are multiple notifications it uses smaller +icons. +It shows a single icon per application, so if you have two SMS messages, the +grid only has one SMS icon. +If there are multiple messages waiting, the total number is shown in the +bottom-right corner. + +Example: one SMS, one Signal, and two WhatsApp messages: +![screenshot](screenshot.png) + +## Installation +This widget needs the [`messages`](/?id=messages) app to handle notifications. + +## Settings +This widget uses the `Widget` settings from the `messages` app: + +### Widget +* `Flash icon` Toggle flashing of the widget icons. + +* `Widget messages` Not used by this widget, but you should select `Hide` to hide the default widget. \ No newline at end of file diff --git a/apps/widmsggrid/metadata.json b/apps/widmsggrid/metadata.json new file mode 100644 index 000000000..b624f5c23 --- /dev/null +++ b/apps/widmsggrid/metadata.json @@ -0,0 +1,16 @@ +{ + "id": "widmsggrid", + "name": "Messages Grid Widget", + "version": "0.01", + "description": "Widget that display notification icons in a grid", + "icon": "widget.png", + "type": "widget", + "dependencies": {"messages":"app"}, + "tags": "tool,system", + "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"widmsggrid.wid.js","url":"widget.js"} + ], + "screenshots": [{"url":"screenshot.png"}] +} diff --git a/apps/widmsggrid/screenshot.png b/apps/widmsggrid/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..b1cdb2a5af45dc0ef9f2460ed747f5e7ac376f19 GIT binary patch literal 10727 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 literal 0 HcmV?d00001 diff --git a/apps/widmsggrid/widget.js b/apps/widmsggrid/widget.js new file mode 100644 index 000000000..7c5882e6c --- /dev/null +++ b/apps/widmsggrid/widget.js @@ -0,0 +1,92 @@ +(function () { + if (global.MESSAGES) return; // don't load widget while in the app + let settings = require('Storage').readJSON("messages.settings.json", true) || {}; + const s = { + flash: (settings.flash === undefined) ? true : !!settings.flash, + showRead: !!settings.showRead, + }; + delete settings; + WIDGETS["msggrid"] = { + area: "tl", width: 0, + flash: s.flash, + showRead: s.showRead, + init: function() { + // runs on first draw + delete w.init; // don't run again + Bangle.on("touch", w.touch); + Bangle.on("message", w.listener); + w.listener(); // update status now + }, + draw: function () { + if (w.init) w.init(); + // If we had a setTimeout queued from the last time we were called, remove it + if (w.t) { + clearTimeout(w.t); + delete w.t; + } + if (!w.width) return; + const b = w.flash && w.status === "new" && ((Date.now() / 1000) & 1), // Blink(= inverse colors) on this second? + // show multiple icons in a grid, by scaling them down + cols = Math.ceil(Math.sqrt(w.srcs.length - 0.1)); // cols===rows, -0.1 to work around rounding error + g.reset().clearRect(w.x, w.y, w.x + w.width - 1, w.y + 24) + .setClipRect(w.x, w.y, w.x + w.width - 1, w.y + 24); // guard against oversized icons + let r = 0, c = 0; // row, column + const offset = pos => Math.floor(pos / cols * 24); // pixel offset for position in row/column + w.srcs.forEach(src => { + const appColor = require("messages").getMessageImageCol(src, require("messages").getMessageImageCol("alert")); + let colors = [g.theme.bg, g.setColor(appColor).getColor()]; + if (b) { + if (colors[1] == g.theme.fg) colors = colors.reverse(); + else colors[1] = g.theme.fg; + } + g.setColor(colors[1]).setBgColor(colors[0]); + g.drawImage(require("messages").getMessageImage(src, "alert"), w.x+offset(c), w.y+offset(r), { scale: 1 / cols }); + if (++c >= cols) { + c = 0; + r++; + } + }); + if (w.total > 1) { + // show total number of messages in bottom-right corner + g.reset(); + if (w.total < 10) g.fillCircle(w.x + w.width - 5, w.y + 20, 4); // single digits get a round background, double digits fill their rectangle + g.setColor(g.theme.bg).setBgColor(g.theme.fg) + .setFont('6x8').setFontAlign(1, 1) + .drawString(w.total, w.x + w.width - 1, w.y + 24, w.total > 9); + } + if (w.flash && w.status === "new") w.t = setTimeout(w.draw, 1000); // schedule redraw while blinking + }, show: function () { + w.width = 24; + w.srcs = require("messages").getMessages() + .filter(m => !['call', 'map', 'music'].includes(m.id)) + .filter(m => m.new || w.showRead) + .map(m => m.src); + w.total = w.srcs.length; + w.srcs = w.srcs.filter((src, i, uniq) => uniq.indexOf(src) === i); // keep unique entries only + Bangle.drawWidgets(); + Bangle.setLCDPower(1); // turns screen on + }, hide: function () { + w.width = 0; + w.srcs = []; + w.total = 0; + Bangle.drawWidgets(); + }, touch: function (b, c) { + if (!w || !w.width) return; // widget not shown + if (process.env.HWVERSION < 2) { + // Bangle.js 1: open app when on clock we touch the side with widget + if (!Bangle.CLOCK) return; + const m = Bangle.appRect / 2; + if ((w.x < m && b !== 1) || (w.x > m && b !== 2)) return; + } + // Bangle.js 2: open app when touching the widget + else if (c.x < w.x || c.x > w.x + w.width || c.y < w.y || c.y > w.y + 24) return; + load("messages.app.js"); + }, listener: function () { + w.status = require("messages").status(); + if (w.status === "new" || (w.status === "old" && w.showRead)) w.show(); + else w.hide(); + } + }; + delete s; + const w = WIDGETS["msggrid"]; +})(); \ No newline at end of file diff --git a/apps/widmsggrid/widget.png b/apps/widmsggrid/widget.png new file mode 100644 index 0000000000000000000000000000000000000000..ce6e7b7ac81d667283a308096d19a04e822d1724 GIT binary patch literal 10457 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 literal 0 HcmV?d00001 From 1b066ac83e80c5f57aac28f4441f5d5b9007d72a Mon Sep 17 00:00:00 2001 From: David Peer Date: Sun, 23 Oct 2022 15:43:55 +0200 Subject: [PATCH 18/28] The probability of horizontal lines is correlated with the battery level. --- apps/aiclock/ChangeLog | 3 ++- apps/aiclock/README.md | 8 +++++--- apps/aiclock/aiclock.app.js | 11 +++++++++-- apps/aiclock/metadata.json | 2 +- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/apps/aiclock/ChangeLog b/apps/aiclock/ChangeLog index 68f77be10..31c55aef1 100644 --- a/apps/aiclock/ChangeLog +++ b/apps/aiclock/ChangeLog @@ -1,2 +1,3 @@ 0.01: New app! -0.02: Design improvements and fixes. \ No newline at end of file +0.02: Design improvements and fixes. +0.03: Indicate battery level through line occurrence. \ No newline at end of file diff --git a/apps/aiclock/README.md b/apps/aiclock/README.md index a3855a166..9e23de3a6 100644 --- a/apps/aiclock/README.md +++ b/apps/aiclock/README.md @@ -8,14 +8,16 @@ The original output of stable diffusion is shown here: ![](orig.png) -And my implementation is shown here: +My implementation is shown below. Note that horizontal lines occur randomly, but the +probability is correlated with the battery level. So if your screen contains only +a few lines its time to charge your bangle again ;) ![](impl.png) # Thanks to -The great open source community: I used an open source diffusion model (https://github.com/CompVis/stable-diffusion) -to generate a watch face for the open source smartwatch BangleJs. +The great open-source community: I used an open-source diffusion model (https://github.com/CompVis/stable-diffusion) +to generate a watch face for the open-source smartwatch BangleJs. ## Creator - [David Peer](https://github.com/peerdavid). \ No newline at end of file diff --git a/apps/aiclock/aiclock.app.js b/apps/aiclock/aiclock.app.js index ecac5f2dc..dbd053f2c 100644 --- a/apps/aiclock/aiclock.app.js +++ b/apps/aiclock/aiclock.app.js @@ -37,8 +37,15 @@ function drawBackground() { g.setFontAlign(0,0); g.setColor(g.theme.fg); - y = 0; + var bat = E.getBattery() / 100.0; + var y = 0; while(y < H){ + // Show less lines in case of small battery level. + if(Math.random() > bat){ + y += 5; + continue; + } + y += 3 + Math.floor(Math.random() * 10); g.drawLine(0, y, W, y); g.drawLine(0, y+1, W, y+1); @@ -103,7 +110,7 @@ function drawDate(){ g.setFontAlign(0,0); g.setFontGochiHand(); - var text = ("0"+date.getDate()).substr(-2) + "/" + ("0"+date.getMonth()).substr(-2); + var text = ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2); var w = g.stringWidth(text); g.setColor(g.theme.bg); g.fillRect(cx-w/2-4, 20, cx+w/2+4, 40+12); diff --git a/apps/aiclock/metadata.json b/apps/aiclock/metadata.json index 5ab0c5bb3..2124b1b7e 100644 --- a/apps/aiclock/metadata.json +++ b/apps/aiclock/metadata.json @@ -3,7 +3,7 @@ "name": "AI Clock", "shortName":"AI Clock", "icon": "aiclock.png", - "version":"0.02", + "version":"0.03", "readme": "README.md", "supports": ["BANGLEJS2"], "description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.", From 81ba4e17fa06b5319c0d4f0768c67eb1a2c3f561 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Wed, 19 Oct 2022 21:04:46 +0200 Subject: [PATCH 19/28] gpstrek - Change from leftright UI to custom with swipe --- apps/gpstrek/app.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/gpstrek/app.js b/apps/gpstrek/app.js index 0c3579d4b..4db9a945d 100644 --- a/apps/gpstrek/app.js +++ b/apps/gpstrek/app.js @@ -318,16 +318,22 @@ function triangle (x, y, width, height){ ]; } +function onSwipe(dir){ + if (dir < 0) { + nextScreen(); + } else if (dir > 0) { + switchMenu(); + } else { + nextScreen(); + } +} + function setButtons(){ - Bangle.setUI("leftright", (dir)=>{ - if (dir < 0) { - nextScreen(); - } else if (dir > 0) { - switchMenu(); - } else { - nextScreen(); - } - }); + let options = { + mode: "custom", + swipe: onSwipe + }; + Bangle.setUI(options); } function getApproxFileSize(name){ From 038fb1a21b01509b708e6016b9684a329cf9aa20 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Fri, 21 Oct 2022 17:04:53 +0200 Subject: [PATCH 20/28] gpstrek - Actually remove listeners on stopping background service --- apps/gpstrek/app.js | 4 +++- apps/gpstrek/widget.js | 22 +++++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/apps/gpstrek/app.js b/apps/gpstrek/app.js index 4db9a945d..242d0c81b 100644 --- a/apps/gpstrek/app.js +++ b/apps/gpstrek/app.js @@ -331,7 +331,9 @@ function onSwipe(dir){ function setButtons(){ let options = { mode: "custom", - swipe: onSwipe + swipe: onSwipe, + btn: nextScreen, + touch: nextScreen }; Bangle.setUI(options); } diff --git a/apps/gpstrek/widget.js b/apps/gpstrek/widget.js index 8d9831e06..4423d7602 100644 --- a/apps/gpstrek/widget.js +++ b/apps/gpstrek/widget.js @@ -23,10 +23,6 @@ function onGPS(fix) { if(fix.fix) state.currentPos = fix; } -Bangle.on('accel', function(e) { - state.acc = e; -}); - function onMag(e) { if (!state.compassHeading) state.compassHeading = e.heading; @@ -73,12 +69,17 @@ function onPressure(e) { } } +function onAcc (e){ + state.acc = e; +} + function start(bg){ Bangle.on('GPS', onGPS); Bangle.on("HRM", onPulse); Bangle.on("mag", onMag); Bangle.on("step", onStep); Bangle.on("pressure", onPressure); + Bangle.on('accel', onAcc); Bangle.setGPSPower(1, "gpstrek"); Bangle.setHRMPower(1, "gpstrek"); @@ -96,8 +97,19 @@ function stop(bg){ if (bg){ if (state.active) bgChanged = true; state.active = false; - saveState(); + } else if (!state.active) { + Bangle.setGPSPower(0, "gpstrek"); + Bangle.setHRMPower(0, "gpstrek"); + Bangle.setCompassPower(0, "gpstrek"); + Bangle.setBarometerPower(0, "gpstrek"); + Bangle.removeListener('GPS', onGPS); + Bangle.removeListener("HRM", onPulse); + Bangle.removeListener("mag", onMag); + Bangle.removeListener("step", onStep); + Bangle.removeListener("pressure", onPressure); + Bangle.removeListener('accel', onAcc); } + saveState(); Bangle.drawWidgets(); } From 48ca9543514a87df1ece4a9ab3ae0eaeea8f3925 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 23 Oct 2022 19:45:59 +0200 Subject: [PATCH 21/28] gpstrek - Bump version --- apps/gpstrek/ChangeLog | 2 ++ apps/gpstrek/metadata.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/gpstrek/ChangeLog b/apps/gpstrek/ChangeLog index 2f1a9ce45..c9be2af94 100644 --- a/apps/gpstrek/ChangeLog +++ b/apps/gpstrek/ChangeLog @@ -1,2 +1,4 @@ 0.01: New App! 0.02: Make selection of background activity more explicit +0.03: Fix listener for accel always active + Use custom UI with swipes instead of leftright diff --git a/apps/gpstrek/metadata.json b/apps/gpstrek/metadata.json index 67f71566e..e8863c706 100644 --- a/apps/gpstrek/metadata.json +++ b/apps/gpstrek/metadata.json @@ -1,7 +1,7 @@ { "id": "gpstrek", "name": "GPS Trekking", - "version": "0.02", + "version": "0.03", "description": "Helper for tracking the status/progress during hiking. Do NOT depend on this for navigation!", "icon": "icon.png", "screenshots": [{"url":"screen1.png"},{"url":"screen2.png"},{"url":"screen3.png"},{"url":"screen4.png"}], From 31ced9ea24cfc052bb9d21864a8fdbb494ee4f4e Mon Sep 17 00:00:00 2001 From: Marco H Date: Tue, 25 Oct 2022 08:11:19 +0200 Subject: [PATCH 22/28] Support to show big weather info --- apps/circlesclock/ChangeLog | 1 + apps/circlesclock/README.md | 3 +- apps/circlesclock/app.js | 58 +++++++++++++++++++++++---------- apps/circlesclock/default.json | 3 +- apps/circlesclock/metadata.json | 2 +- apps/circlesclock/settings.js | 5 +++ 6 files changed, 52 insertions(+), 20 deletions(-) diff --git a/apps/circlesclock/ChangeLog b/apps/circlesclock/ChangeLog index c398a89b6..d74badcad 100644 --- a/apps/circlesclock/ChangeLog +++ b/apps/circlesclock/ChangeLog @@ -26,3 +26,4 @@ 0.12: Allow configuration of update interval 0.13: Load step goal from Bangle health app as fallback Memory optimizations +0.14: Support to show big weather info diff --git a/apps/circlesclock/README.md b/apps/circlesclock/README.md index aa429d5ec..160cacbec 100644 --- a/apps/circlesclock/README.md +++ b/apps/circlesclock/README.md @@ -35,4 +35,5 @@ The color of each circle can be configured. The following colors are available: Marco ([myxor](https://github.com/myxor)) ## Icons -Icons taken from [materialdesignicons](https://materialdesignicons.com) under Apache License 2.0 +Most of the icons are taken from [materialdesignicons](https://materialdesignicons.com) under Apache License 2.0 except the big weather icons which are from +[icons8](https://icons8.com/icon/set/weather/small--static--black) diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index fc501a5d0..d4cb5069f 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -22,7 +22,7 @@ let settings = Object.assign( if (settings.stepGoal == undefined) { let d = storage.readJSON("health.json", true) || {}; settings.stepGoal = d != undefined && d.settings != undefined ? d.settings.stepGoal : undefined; - + if (settings.stepGoal == undefined) { d = storage.readJSON("wpedom.json", true) || {}; settings.stepGoal = d != undefined && d.settings != undefined ? d.settings.goal : 10000; @@ -39,6 +39,7 @@ let location = getLocation(); const showWidgets = settings.showWidgets || false; const circleCount = settings.circleCount || 3; +const showBigWeather = settings.showBigWeather || false; let hrtValue; let now = Math.round(new Date().getTime() / 1000); @@ -116,17 +117,40 @@ function draw() { // time g.setFontRobotoRegular50NumericOnly(); - g.setFontAlign(0, -1); g.setColor(colorFg); - g.drawString(locale.time(new Date(), 1), w / 2, h1 + 6); + if (!showBigWeather) { + g.setFontAlign(0, -1); + g.drawString(locale.time(new Date(), 1), w / 2, h1 + 6); + } + else { + g.setFontAlign(-1, -1); + g.drawString(locale.time(new Date(), 1), 5, h1 + 6); + } now = Math.round(new Date().getTime() / 1000); // date & dow g.setFontRobotoRegular21(); - g.setFontAlign(0, 0); - g.drawString(locale.date(new Date()), w / 2, h2); - g.drawString(locale.dow(new Date()), w / 2, h2 + dowOffset); - + if (!showBigWeather) { + g.setFontAlign(0, 0); + g.drawString(locale.date(new Date()), w / 2, h2); + g.drawString(locale.dow(new Date()), w / 2, h2 + dowOffset); + } else { + g.setFontAlign(-1, 0); + g.drawString(locale.date(new Date()), 5, h2); + g.drawString(locale.dow(new Date()), 5, h2 + dowOffset, 1); + } + + // weather + if (showBigWeather) { + const weather = getWeather(); + const tempString = weather ? locale.temp(weather.temp - 273.15) : undefined; + g.setFontAlign(1, 0); + if (tempString) g.drawString(tempString, w, h2 + dowOffset); + + const code = weather ? weather.code : -1; + g.drawImage(getWeatherIconByCode(code, true), w - 64, h1); + } + drawCircle(1); drawCircle(2); drawCircle(3); @@ -417,7 +441,6 @@ function drawWeather(w) { } } - function drawSunProgress(w) { if (!w) w = getCircleXPosition("sunprogress"); const percent = getSunProgress(); @@ -557,19 +580,20 @@ function windAsBeaufort(windInKmh) { * Choose weather icon to display based on weather conditition code * https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2 */ -function getWeatherIconByCode(code) { +function getWeatherIconByCode(code, big) { const codeGroup = Math.round(code / 100); + if (big == undefined) big = false; // weather icons: - const weatherCloudy = atob("EBCBAAAAAAAAAAfgD/Af8H/4//7///////9//z/+AAAAAAAA"); - const weatherSunny = atob("EBCBAAAAAYAQCBAIA8AH4A/wb/YP8A/gB+ARiBAIAYABgAAA"); - const weatherMoon = atob("EBCBAAAAAYAP8B/4P/w//D/8f/5//j/8P/w//B/4D/ABgAAA"); - const weatherPartlyCloudy = atob("EBCBAAAAAAAYQAMAD8AIQBhoW+AOYBwwOBBgHGAGP/wf+AAA"); - const weatherRainy = atob("EBCBAAAAAYAH4AwwOBBgGEAOQAJBgjPOEkgGYAZgA8ABgAAA"); - const weatherPartlyRainy = atob("EBCBAAAAEEAQAAeADMAYaFvoTmAMMDgQIBxhhiGGG9wDwAGA"); - const weatherSnowy = atob("EBCBAAAAAAADwAGAEYg73C50BCAEIC50O9wRiAGAA8AAAAAA"); + const weatherCloudy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4AAAAAAAfg+AAAAAAAAfHwAAAAAAAA+eAAAAAAAAB54AAAAAAAAHvAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD3gAAAAAAAAeeAAAAAAAAB58AAAAAAAAPj4AAAAAAAB8H4AAAAAAAfgP////////8Af////////gA////////8AAf//////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAAAAAAfgD/Af8H/4//7///////9//z/+AAAAAAAA"); + const weatherSunny = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAwADwADAAAAHgAPAAeAAAAfAA8AD4AAAA+ADwAfAAAAB8APAD4AAAAD4B+AfAAAAAHw//D4AAAAAPv//fAAAAAAf///4AAAAAA/4H/AAAAAAB+AH4AAAAAAPgAHwAAAAAA8AAPAAAAAAHwAA+AAAAAAeAAB4AAAAAB4AAHgAAAAAPAAAPAAAA//8AAA//8AD//wAAD//wAP//AAAP//AA//8AAA//8AAADwAADwAAAAAHgAAeAAAAAAeAAB4AAAAAB8AAPgAAAAADwAA8AAAAAAPgAHwAAAAAAfgB+AAAAAAD/gf8AAAAAAf///4AAAAAD7//3wAAAAAfD/8PgAAAAD4B+AfAAAAAfADwA+AAAAD4APAB8AAAAfAA8AD4AAAB4ADwAHgAAADAAPAAMAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAYAQCBAIA8AH4A/wb/YP8A/gB+ARiBAIAYABgAAA"); + const weatherMoon = big ? atob("QEDBAP//wxgAAAYAAAAPAAAAD4AAAA8AAAAPwAAADwAAAA/gAAAPAAAAB/APAP/wAAAH+A8A//AAAAf4DwD/8AAAB/wPAP/wAAAH/gAADwAAAAe+AAAPAAAAB54AAA8AAAAHngAADwAAAAePAAAAAAAAD48OAAAAAAAPDw+AAAAAAB8PD8AAAAAAHg8P4AAAAAA+DwPwAAAAAHwfAfgAAAAB+D4A/AAA8AfwfgB/8AD//+D+AD/8AP//wfgAH/4Af/8B8AAf/wB//APgAAgfgD+AA8AAAAfAH8AHwAAAA+AP8B+AAAAB4Af//4AAAAHgA///gAAAAPAA//8AAAAA8AAf/wAAAADwAAAAAAAAAPAAAAAAAAAA8AcAAAAAAADwD+AAAAAAAfAfgAAAAAAB+D4AAAAAAAB8fAAAAAAAAD54AAAAAAAAHngAAAAAAAAe8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAPeAAAAAAAAB54AAAAAAAAHnwAAAAAAAA+PgAAAAAAAHwfgAAAAAAB+A/////////wB////////+AD////////wAB///////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAYAP8B/4P/w//D/8f/5//j/8P/w//B/4D/ABgAAA"); + const weatherPartlyCloudy = big ? atob("QEDBAP//wxgAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAABwAPAA4AAAAHgA8AHgAAAAfADwA+AAAAA+AfgHwAAAAB8P/w+AAAAAD7//3wAAAAAH///+BAAAAAP+B/wOAAAAAfgB+B8AAAAD4AD8H4AAAAPAA/wPwAAAB8AH+Af/AAAHgA/AA//AAAeAH4AB/+AADwAfAAH/8A//AD4AAIH4D/8AfAAAAHwP/wB4AAAAPg//AHgAAAAeAA8B+AAAAB4AB4fwAAAADwAHn/AAAAAPAAff8AAAAA8AA/8AAAAADwAD/AAAAAAPAEH4AAAAAA8A4PgAAAAAHwHgcAAAAAAfg+AwAAAAAAfHwAAAAAAAA+eAAAAAAAAB54AAAAAAAAHvAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD3gAAAAAAAAeeAAAAAAAAB58AAAAAAAAPj4AAAAAAAB8H4AAAAAAAfgP////////8Af////////gA////////8AAf//////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAAAYQAMAD8AIQBhoW+AOYBwwOBBgHGAGP/wf+AAA"); + const weatherRainy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4APAA8AAfg+AA8ADwAAfHwADwAPAAA+eAAPAA8AAB54AAAAAAAAHvAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AADw8PDwAP8AAPDw8PAA/wAA8PDw8AD3gADw8PDwAeeAAAAAAAAB58AAAAAAAAPj4AAAAAAAB8H4AAAAAAAfgP/w8PDw8P8Af/Dw8PDw/gA/8PDw8PD8AAfw8PDw8OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAPAAAAAAAPAA8AAAAAAA8ADwAAAAAADwAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAYAH4AwwOBBgGEAOQAJBgjPOEkgGYAZgA8ABgAAA"); + const weatherPartlyRainy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4AAAA8AAfg+AAAADwAAfHwAAAAPAAA+eAAAAA8AAB54AAAADwAAHvAAAAAPAAAP8AAAAA8AAA/wAAAADwAAD/AAAA8PAAAP8AAADw8AAA/wAAAPDwAAD3gAAA8PAAAeeAAADw8AAB58AAAPDwAAPj4AAA8PAAB8H4AADw8AAfgP//8PDw//8Af//w8PD//gA///Dw8P/8AAf/8PDw/+AAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAEEAQAAeADMAYaFvoTmAMMDgQIBxhhiGGG9wDwAGA"); + const weatherSnowy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4AAAADwAfg+AAAAAPAAfHwAAAAA8AA+eAAAAADwAB54AA8AD/8AHvAADwAP/wAP8AAPAA//AA/wAA8AD/8AD/AA//AA8AAP8AD/8ADwAA/wAP/wAPAAD3gA//AA8AAeeAAPAAAAAB58AA8AAAAAPj4ADwAAAAB8H4APAAAAAfgP/wAA8A//8Af/AADwD//gA/8AAPAP/8AAfwAA8A/+AAAAAA//AAAAAAAAD/8AAAAAAAAP/wAAAAAAAA//AAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAAADwAGAEYg73C50BCAEIC50O9wRiAGAA8AAAAAA"); const weatherFoggy = atob("EBCBAAAAAAADwAZgDDA4EGAcQAZAAgAAf74AAAAAd/4AAAAA"); - const weatherStormy = atob("EBCBAAAAAYAH4AwwOBBgGEAOQMJAgjmOGcgAgACAAAAAAAAA"); + const weatherStormy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4AAAAAAAfg+AAAAAAAAfHwAAAAAAAA+eAAAAAAAAB54AAAAD/AAHvAAAAAf4AAP8AAAAB/gAA/wAAAAP8AAD/AAAAA/gAAP8AAAAH+AAA/wAAAAfwAAD3gAAAD/AAAeeAAAAP4AAB58AAAB/AAAPj4AAAH8AAB8H4AAA/gAAfgP//+D//D/8Af//4f/4P/gA///B//B/8AAf/8P/8P+AAAAAAAPgAAAAAAAAB8AAAAAAAAAHwAAAAAAAAA+AAAAAAAAADwAAAAAAAAAfAAAAAAAAAB4AAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAYAH4AwwOBBgGEAOQMJAgjmOGcgAgACAAAAAAAAA"); switch (codeGroup) { case 2: diff --git a/apps/circlesclock/default.json b/apps/circlesclock/default.json index ea00dc347..6247c058c 100644 --- a/apps/circlesclock/default.json +++ b/apps/circlesclock/default.json @@ -22,5 +22,6 @@ "circle3colorizeIcon": true, "circle4colorizeIcon": false, "hrmValidity": 60, - "updateInterval": 60 + "updateInterval": 60, + "showBigWeather": false } diff --git a/apps/circlesclock/metadata.json b/apps/circlesclock/metadata.json index 837fcaa88..a1a291546 100644 --- a/apps/circlesclock/metadata.json +++ b/apps/circlesclock/metadata.json @@ -1,7 +1,7 @@ { "id": "circlesclock", "name": "Circles clock", "shortName":"Circles clock", - "version":"0.13", + "version":"0.14", "description": "A clock with three or four circles for different data at the bottom in a probably familiar style", "icon": "app.png", "screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}], diff --git a/apps/circlesclock/settings.js b/apps/circlesclock/settings.js index fb23f8d5e..349e3fdf2 100644 --- a/apps/circlesclock/settings.js +++ b/apps/circlesclock/settings.js @@ -68,6 +68,11 @@ return x + 's'; }, onchange: x => save('updateInterval', x), + }, + /*LANG*/'show big weather': { + value: settings[showBigWeather] || false, + format: () => (settings[showBigWeather] ? 'Yes' : 'No'), + onchange: x => save(showBigWeather, x), } }; E.showMenu(menu); From e0d86d60d89187234443ac5a7808e8814f0fa0ab Mon Sep 17 00:00:00 2001 From: Marco H Date: Tue, 25 Oct 2022 08:40:39 +0200 Subject: [PATCH 23/28] Fix settings and crash when no weather is available --- apps/circlesclock/app.js | 5 ++++- apps/circlesclock/settings.js | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index d4cb5069f..1554b24ba 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -148,7 +148,10 @@ function draw() { if (tempString) g.drawString(tempString, w, h2 + dowOffset); const code = weather ? weather.code : -1; - g.drawImage(getWeatherIconByCode(code, true), w - 64, h1); + if (code > -1) { + const icon = getWeatherIconByCode(code, true); + if (icon) g.drawImage(icon, w - 48, h1, {scale:0.75}); + } } drawCircle(1); diff --git a/apps/circlesclock/settings.js b/apps/circlesclock/settings.js index 349e3fdf2..0aa8dc826 100644 --- a/apps/circlesclock/settings.js +++ b/apps/circlesclock/settings.js @@ -70,9 +70,9 @@ onchange: x => save('updateInterval', x), }, /*LANG*/'show big weather': { - value: settings[showBigWeather] || false, - format: () => (settings[showBigWeather] ? 'Yes' : 'No'), - onchange: x => save(showBigWeather, x), + value: !!settings.showBigWeather, + format: () => (settings.showBigWeather ? 'Yes' : 'No'), + onchange: x => save('showBigWeather', x), } }; E.showMenu(menu); From e553463fab186b13a947e822d7ad62732cd5271d Mon Sep 17 00:00:00 2001 From: Marco H Date: Tue, 25 Oct 2022 08:45:37 +0200 Subject: [PATCH 24/28] Fix position of temperature --- apps/circlesclock/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index 1554b24ba..7cd2ad0ec 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -145,7 +145,7 @@ function draw() { const weather = getWeather(); const tempString = weather ? locale.temp(weather.temp - 273.15) : undefined; g.setFontAlign(1, 0); - if (tempString) g.drawString(tempString, w, h2 + dowOffset); + if (tempString) g.drawString(tempString, w, h2); const code = weather ? weather.code : -1; if (code > -1) { From fc71c746d18f73fd07cd54f894546b650b441a9f Mon Sep 17 00:00:00 2001 From: Marco H Date: Tue, 25 Oct 2022 08:49:16 +0200 Subject: [PATCH 25/28] Update README --- apps/circlesclock/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/circlesclock/README.md b/apps/circlesclock/README.md index 160cacbec..0084da5fa 100644 --- a/apps/circlesclock/README.md +++ b/apps/circlesclock/README.md @@ -9,10 +9,11 @@ It can show the following information (this can be configured): * Steps distance * Heart rate (automatically updates when screen is on and unlocked) * Battery (including charging status and battery low warning) - * Weather (requires [weather app](https://banglejs.com/apps/#weather)) + * Weather (requires [OWM weather provider](https://banglejs.com/apps/?id=owmweather)) * Humidity or wind speed as circle progress * Temperature inside circle * Condition as icon below circle + * Big weather icon next to clock * Time and progress until next sunrise or sunset (requires [my location app](https://banglejs.com/apps/#mylocation)) * Temperature, air pressure or altitude from internal pressure sensor From 1be8c502f0aad79a74f19c4e361e972eb5738720 Mon Sep 17 00:00:00 2001 From: Marco H Date: Tue, 25 Oct 2022 08:59:25 +0200 Subject: [PATCH 26/28] Improve positioning with big weather enabled --- apps/circlesclock/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index 7cd2ad0ec..1f53eccf4 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -124,7 +124,7 @@ function draw() { } else { g.setFontAlign(-1, -1); - g.drawString(locale.time(new Date(), 1), 5, h1 + 6); + g.drawString(locale.time(new Date(), 1), 2, h1 + 6); } now = Math.round(new Date().getTime() / 1000); @@ -136,8 +136,8 @@ function draw() { g.drawString(locale.dow(new Date()), w / 2, h2 + dowOffset); } else { g.setFontAlign(-1, 0); - g.drawString(locale.date(new Date()), 5, h2); - g.drawString(locale.dow(new Date()), 5, h2 + dowOffset, 1); + g.drawString(locale.date(new Date()), 2, h2); + g.drawString(locale.dow(new Date()), 2, h2 + dowOffset, 1); } // weather From ee61b7200e0d65bfad7d3e6c6f843ded61f419c3 Mon Sep 17 00:00:00 2001 From: Marco H Date: Tue, 25 Oct 2022 11:50:03 +0200 Subject: [PATCH 27/28] Add missing icon --- apps/circlesclock/app.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index 1f53eccf4..824445ff6 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -148,10 +148,8 @@ function draw() { if (tempString) g.drawString(tempString, w, h2); const code = weather ? weather.code : -1; - if (code > -1) { - const icon = getWeatherIconByCode(code, true); - if (icon) g.drawImage(icon, w - 48, h1, {scale:0.75}); - } + const icon = getWeatherIconByCode(code, true); + if (icon) g.drawImage(icon, w - 48, h1, {scale:0.75}); } drawCircle(1); @@ -595,9 +593,10 @@ function getWeatherIconByCode(code, big) { const weatherRainy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4APAA8AAfg+AA8ADwAAfHwADwAPAAA+eAAPAA8AAB54AAAAAAAAHvAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AADw8PDwAP8AAPDw8PAA/wAA8PDw8AD3gADw8PDwAeeAAAAAAAAB58AAAAAAAAPj4AAAAAAAB8H4AAAAAAAfgP/w8PDw8P8Af/Dw8PDw/gA/8PDw8PD8AAfw8PDw8OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAPAAAAAAAPAA8AAAAAAA8ADwAAAAAADwAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAYAH4AwwOBBgGEAOQAJBgjPOEkgGYAZgA8ABgAAA"); const weatherPartlyRainy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4AAAA8AAfg+AAAADwAAfHwAAAAPAAA+eAAAAA8AAB54AAAADwAAHvAAAAAPAAAP8AAAAA8AAA/wAAAADwAAD/AAAA8PAAAP8AAADw8AAA/wAAAPDwAAD3gAAA8PAAAeeAAADw8AAB58AAAPDwAAPj4AAA8PAAB8H4AADw8AAfgP//8PDw//8Af//w8PD//gA///Dw8P/8AAf/8PDw/+AAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAEEAQAAeADMAYaFvoTmAMMDgQIBxhhiGGG9wDwAGA"); const weatherSnowy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4AAAADwAfg+AAAAAPAAfHwAAAAA8AA+eAAAAADwAB54AA8AD/8AHvAADwAP/wAP8AAPAA//AA/wAA8AD/8AD/AA//AA8AAP8AD/8ADwAA/wAP/wAPAAD3gA//AA8AAeeAAPAAAAAB58AA8AAAAAPj4ADwAAAAB8H4APAAAAAfgP/wAA8A//8Af/AADwD//gA/8AAPAP/8AAfwAA8A/+AAAAAA//AAAAAAAAD/8AAAAAAAAP/wAAAAAAAA//AAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAAADwAGAEYg73C50BCAEIC50O9wRiAGAA8AAAAAA"); - const weatherFoggy = atob("EBCBAAAAAAADwAZgDDA4EGAcQAZAAgAAf74AAAAAd/4AAAAA"); + const weatherFoggy = big ? atob("QEDBAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAwADwADAAAAHgAPAAeAAAAfAA8AD4AAAA+ADwAfAAAAB8APAD4AAAAD4B+AfAAAAAHw//D4AAAAAPv//fAAAAAAf///4AAAAAA/4H/AAAAAAB+AH4AAAAAAPgAHwAAAAAA8AAPAAAAAAHwAA+AAAAAAeAAB4AAAAAB4AAHgAAAAAPAAAPAAAAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AD///AADwAAAP//8AAeAAAA///wAB4AAAD///AAPgAAAAAAAAA8AAAAAAAAAHwAAAAAAAAB+AAAAAAAAAf8AAAAD///D/4AAAAP//8P3wAAAA///w8PgAAAD///CAfAAAAAAAAAA+AAAAAAAAAB8AAAAAAAAAD4AAAAAAAAAHgAAP//8PAAMAAA///w8AAAAAD///DwAAAAAP//8PAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAAADwAZgDDA4EGAcQAZAAgAAf74AAAAAd/4AAAAA"); const weatherStormy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4AAAAAAAfg+AAAAAAAAfHwAAAAAAAA+eAAAAAAAAB54AAAAD/AAHvAAAAAf4AAP8AAAAB/gAA/wAAAAP8AAD/AAAAA/gAAP8AAAAH+AAA/wAAAAfwAAD3gAAAD/AAAeeAAAAP4AAB58AAAB/AAAPj4AAAH8AAB8H4AAA/gAAfgP//+D//D/8Af//4f/4P/gA///B//B/8AAf/8P/8P+AAAAAAAPgAAAAAAAAB8AAAAAAAAAHwAAAAAAAAA+AAAAAAAAADwAAAAAAAAAfAAAAAAAAAB4AAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAYAH4AwwOBBgGEAOQMJAgjmOGcgAgACAAAAAAAAA"); - + const unknown = big ? atob("QEDBAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAf/4AAAAAAAH//4AAAAAAA///wAAAAAAH+B/gAAAAAA/AA/AAAAAAH4AB+AAAAAA/AAD4AAAAAD4H4HwAAAAAfB/4PgAAAAB8P/weAAAAAHg//h4AAAAA+Hw+HwAAAAD4eB8PAAAAAP/wDw8AAAAA//APDwAAAAD/8A8PAAAAAH/gDw8AAAAAAAAfDwAAAAAAAH4fAAAAAAAB/B4AAAAAAAf4HgAAAAAAD/A+AAAAAAAfwHwAAAAAAD8A+AAAAAAAPgH4AAAAAAB8B/AAAAAAAHgf4AAAAAAA+H+AAAAAAADwfwAAAAAAAPD8AAAAAAAA8PAAAAAAAAD/8AAAAAAAAP/wAAAAAAAA//AAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf+AAAAAAAAD/8AAAAAAAAP/wAAAAAAAA//AAAAAAAADw8AAAAAAAAPDwAAAAAAAA8PAAAAAAAADw8AAAAAAAAP/wAAAAAAAA//AAAAAAAAD/8AAAAAAAAH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : undefined; + switch (codeGroup) { case 2: return weatherStormy; @@ -634,7 +633,7 @@ function getWeatherIconByCode(code, big) { return weatherCloudy; } default: - return undefined; + return unknown; } } From 9a9f75b262626461ae9a061795336966e62fa9c0 Mon Sep 17 00:00:00 2001 From: Marco H Date: Tue, 25 Oct 2022 12:01:28 +0200 Subject: [PATCH 28/28] New screenshot with big weather --- apps/circlesclock/README.md | 2 ++ .../screenshot-light-with-big-weather.png | Bin 0 -> 4474 bytes 2 files changed, 2 insertions(+) create mode 100644 apps/circlesclock/screenshot-light-with-big-weather.png diff --git a/apps/circlesclock/README.md b/apps/circlesclock/README.md index 0084da5fa..8c8fbe4ae 100644 --- a/apps/circlesclock/README.md +++ b/apps/circlesclock/README.md @@ -28,6 +28,8 @@ The color of each circle can be configured. The following colors are available: ![Screenshot light theme](screenshot-light.png) ![Screenshot dark theme with four circles](screenshot-dark-4.png) ![Screenshot light theme with four circles](screenshot-light-4.png) +![Screenshot light theme with big weather enabled](screenshot-light-with-big-weather.png) + ## Ideas * Show compass heading diff --git a/apps/circlesclock/screenshot-light-with-big-weather.png b/apps/circlesclock/screenshot-light-with-big-weather.png new file mode 100644 index 0000000000000000000000000000000000000000..d1d569247ed490ed730675d6e1263140a40cb679 GIT binary patch literal 4474 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 M07*qoM6N<$f(^od0RR91 literal 0 HcmV?d00001