-
You would probably have to implement a handler for the gps event in boot.js#20 of the android bangle app, that then calls something like
Bangle.emit("gps",data);
do make the GPS info usable for other apps on the bangle. At least for a proof of concept that should work fine.
Then there would need to be some handling of the internal GPS. Maybe switching it off as long as GB sends gps events or something similar. -
-
-
While trying to find methods using a lot of time I thought of automatically wrapping all functions in an object to get some statistics. I came up with this: https://gist.github.com/halemmerich/a2e90fc2ea1aefd2bed8a20d3fb3bdf0
This does not seem to work for things like
Bangle.#ontouch
. While I can doObject.keys(Bangle.["#ontouch"]).length
from the IDE terminal to detect if there is an array of functions in there, the code in the gist line 9 does not seem get the array. Generally the recursion path is not taken, as if everything is type function.Ideas anyone?
-
Perhaps we should consider making drawWidgets actually only queue up a redraw, so if it gets called >1 time before we next idle there is only ever one full redraw?
One call will not be enough when at least one widget changes it's width during draw. But we can do with one or two draws, first draw executed instantly and then capture all other
drawWidgets
and queue one for next idle. I have tried it and it works fine.The changes to the 4 widgets are working well, I could remove about half of the code of
widgetupdate
while keeping it working the same way. It now essentially only handles automatically hiding of the widgets and the queuing ofdrawWidgets
-
-
I get about 3-5 calls to
drawWidgets
with around 100ms each. So the bangle is computing, drawing and using power for 400ms on average while not doing anything useful on each clock<->launcher transition.Since the main use of the update method is prevention of additional calls to
drawWidgets
, how about something like the following duringdrawWidgets
:let origDraw = Bangle.drawWidgets; let drawCount = 0; Bangle.drawWidgets = ()=>{drawCount++;}; for (wd of WIDGETS) wd.draw(wd); Bangle.drawWidgets = origDraw; if (drawCount > 0) setTimeout(Bangle.drawWidgets,0);
That would reduce drawing the widgets to one additional time after every change for width/area has been done. Still one call more than needed when using update methods, but less overhead on memory and code size.
appRect
could be updated there as well to keep it dynamic.The wrapping of
draw
to check if widgets are currently hidden could be replaced by just settingdraw = ()=>{}
and restoring that on showing the widgets. Actually thewidget_utils
module could now be used there. That would probably remove the need for my some of myBangle.*Widgets
-methods altogether. -
-
I think there's a bit of a worry with what you're doing with E.showMenu/etc. For a simple app that just shows the menu it's fine, but imagine something like https://banglejs.com/apps/?id=espruinoteĀrm where it's done something (opened a bluetooth connection in this case) and then shown a menu. Now when you want to switch apps you'll just move right on to the next app without clearing up.
That is why I tried to add the
remove
handler to the options given toE.show*
. Apps then can cleanup after themselves if I decide to leave them when they are in a menu. The default is still not cleaning up and loading normally. See setting a remove handler for the scroller in launch to make fastloading out of it possible https://github.com/halemmerich/BangleApps/commit/74d0499976bbeb845ffda4a4fbb15ca40884bc18.This looks great as a proof of concept and it's really cool to have it on https://halemmerich.github.io/BangleAppsĀ/ for everyone to use if they want to, but just to warn you that I think right now this is just too much to try and merge back into the default app loader.
I think we need at least a solution for the widgets not beeing updated correctly either in draw methods (multiple calls to
Bangle.drawWidgets()
=> slow) or with update-methods in Firmware (part of what widgetupdate does). The rest I am completely fine with keeping separate.For instance I just checked the messages app...
You can completely ignore that, there I just tried to have a look at how far I could go without completely borking everything. I will probably revert that completely since it is massively complex to even test every feature. Preferred solution to that would be an alternative implementation like is discussed in https://forum.espruino.com/conversations/375794/.
If the function wrapping is to stop functions getting defined outside of scope, I think changing: function myfun(...) { ... } to let myfun = function(...) { ... } should have the desired effect.
Yes, tried that for some apps and works fine. Didn't think about the code reading/parsing implications of wrapping in a function.
I'm all for making Bangle.js faster, but I don't want to be in the position where I'm making it slower for everyone else just so that it can be faster for those that choose to install fastload.
No, fastload must be optional and as free of changes for all other stuff as possible. In the end normal loading will probably be the default for a long time. But stuff similar to fastloading will happen inside of apps (showing menus, scrollers and using
setUI()
for different parts of the app etc.) and I think we should have working cleanup for those scenarios. -
Spotify remote does not implement a remove handler for setUI so normal loading is expected. Quicklaunch replaces the setUI function on clock faces and when fastloading to somewhere else that is not cleaned up until a normal load. That is probably the reason for changing behaviour.
Widget editor throws the loaded and modified widgets away and reloads everything manually. That probably creates problems with the wrapped draw methods. There should be a way to resort the existing widgets objects to prevent that.It seems there are still some things to catch :)
I have today seen a problem where menus are functional, but sometimes not updating the display correctly. It seems there is one draw on tap missing. Scrolling up and down then shows correct content.Edit: D'oh! Forgot to return the resulting object when wrapping
E.show*
, will fix that this evening. -
Every widget that changes it's width during draw needs to schedule a redraw. This seems somewhat wasteful since at least with simple timeouts all calls will eventually be executed. The update methods can all be called without needing to schedule additional draws. Drawing in that case only needs to be done if an app actually calls
drawWidgets
.The current state of affairs is at https://github.com/halemmerich/BangleApps/tree/experimental/widgetUpdate and my apploader at https://halemmerich.github.io/BangleApps/
There are now two apps that should work indepent from each other: fastload and widgetupdate
fastload mostly wraps
load
to automatically decide between reboot and fast loading. That keeps the apps a tiny bit simpler. The rest just basically wraps methods to be able to show a different loading screen and add handlingremove
-methods in theE.show*
methods. This will probably stay an app on my apploader for the foreseeable future and I suspect to have missed a lot of edgecases.widgetupdate is a bit more interesting as it handles the hiding of widgets (including wrapping touch,drag and swipe handlers) as well as setting a correct appRect and detection of an app loading widgets or not. That should work for all apps without changes in the app. This also does essentially the same thing for the drawing as the newest
widget_utils
module does but at widget loading time. Newly available methods areBangle.updateWidgets()
andBangle.hideWidgets()
.There also is a small modification in bootloader which adds
Bangle.showClock
. That is used inwidclose
andlaunch
to get the clock loaded while beeing able to be modified byfastload
if installed. Essentially this prevents using eval there, which can not be overriden to modify its behaviour. It always calls load, which is then optionally overriden byfastload
with the eval approach depending on the existence ofBangle.uiRemove
.Maybe some of the widget handling could be integrated into the firmware to fix the current bad behaviour with regard to Bangle.CLOCK changes.
Alternatively the fast loading using
eval
from clock to launcher could be reverted to get the widgets working like they were before and then optionally reinstated by installingfastload
and/orwidgetupdate
. That would however mean implementing remove handlers in watchfaces/apps and update methods in widgets that are not used if those two apps are not installed.TL;DR: To try it out install bleeding edge firmware, install fastload and widgetupdate ,update everything my apploader has newer versions of and probably expect bugs (especially messages is currently practically not tested at all) :)
-
-
There remains one small thing to get the current state working correctly with regard to widgets.
Currently some widgets prevent themselves from being loaded depending onBangle.CLOCK
.
I have checked and only foundwidclk
,widclkbttm
,widclose
andwidcloselaunch
to have this problem. We could solve this by just checkingBangle.CLOCK
on every draw and react accordingly or we could define widgets to have an update method to tell them to recheck their environment.
Implementation on the Bangle side of things could be done by calling all available update methods on widgets at the end ofBangle.setUI
andBangle.loadWidgets
. Update method is better when widgets change their width depending onCLOCK
and prevents additional calls todrawWidgets
which would be needed whendraw
changes the width.The small negative impact of this would be loading the 4 mentioned widgets even if a clock app is active. Since they are all on the order of 10 lines of code this is probably not a problem. Solving the problem for theoretical future big widgets would be possible by having the update method un/load the big pieces of code on demand.
I have tried the update method way and have not found any problems with it the last few days.
-
As far as I know the IPX7 rating does not include IPX5/6 which would be necessary for any relevant protection against splashes and jets of water from all directions. So even dropping an IPX7 device from some distance into 1m deep water is probably not tested so I would only expect such a device to be able to be slowly submerged to this depth without damage.
For my Bangle I would not shower or swim with it, but I expect it to survive rain,sweat and the occasional few drops during washing my hands.
An 5ATM rating like my old BIP S would be very nice, that watch survived showers just fine. But even for that rating Amazfit suggests not showering/bathing with it and warns against pressing the button when under water. -
I took your caching code and integrated that into iconlaunch. Updating the cache takes about 1.5s while using the cache "only" needs 500ms. Pretty awesome improvement, would not have expected this.
Finding the launcher every time is also slow. I have implemented something similar to what is done for clocks with the possibility to configure one in the settings. That takes the time for finding the launcher from 194ms down to 7ms on my close to stock bangle. Probably a lot more on the other one.
-
-
Loading the widgets and manipulating the loadWidgets/drawWidgets/draw-methods to just do nothing in case an app has not loaded widgets seems to work a treat. I believe the only way for widgets to disturb graphics is now drawing outside of the draw method. This could be handled by giving the widgets a dedicated buffer like widhide does.
I have implemented some changes for logging boot times:
Boot times: sensortools.0.boot.js: 302ms bthrm.0.boot.js: 151ms agpsdata.boot.js: 14ms android.boot.js: 61ms calibration.boot.js: 17ms fastload.boot.js: 17ms health.boot.js: 44ms owmweather.boot.js: 68ms qmsched.boot.js: 24ms quicklaunch.boot.js: 622ms sched.boot.js: 49ms swscroll.boot.js: 11ms Boot took 1592ms
So actually BTHRM is not even that bad ;)
Sensortools is easy to fix, just moving everything into a lib and requiring only if enabled should help. BTHRM not that easy, tried 2 times now and did not manage meaningful size reduction without losing features I want. Quicklaunch is probably easy to fix as @rigrig said.
Iconlauncher takes about a second to load on my 40 app bangle, probably relatively easy to alleviate that by loading apps/icons as needed instead of all at once as you suggested.But, currently I can with very little effort remove these 1.6 seconds altogether by just not needing to execute the boot files between app changes (or at least from launcher to app). Besides that I think optimizing single apps is currently more important than fastloading, even if I personally like the efficiency of not tearing down the environment for every app change. That will probably bring down the time to around half a second on a big install like mine. Probably good enough for most usecases.
Comparison between nearly empty bangle on 2v15 without fastloading and 2v15.40 with fastloading shows a nearly halved time from clock to launcher and about 10% faster the other way round (Anton and Launch). Although in this setting both seem to be very similar to the naked eye.
Espruino itself seems to take roundabout 80ms betweenload()
and the first line of.boot0
There remains one point where fastloading has a real benefit:
Not losing state in BT and sensor stuff. Reconnecting in BTHRM takes some time and I think keeping a gps fix alive is also beneficial in comparison to aquiring it new on every app change. If I use GPS I currently think twice about changing the app as not to loose a fix which I then sometimes can not get back.I will keep playing around with the fastboot idea in my apploader and try to get some PR in for low hanging fruit :)
-
How about having curated "Collections" in the apploader? Like an additional tab where you could install the Fitness-Collection and get Heart Rate Monitor,Run and Recorder for example.
Essentially a list of app IDs with its own icon and description.
Maybe the included apps could then even be reduced to Settings, Welcome, About and of course Launcher and Anton. -
My tests with implementing a remove method on widgets work well but would need implementation in all widgets. Maybe an alternative to this would be keeping the widgets loaded, but hiding them in apps not loading widgets? RAM could be an issue, but at least on bangle 2 it seems to be not that problematic for most apps.
A lot easier to implement and probably all of it in a bit of boot code. Actually really similar to what widhide does right now.As far as the starting time goes, the biggest block is the quicklaunch app at about half a second for scanning the 40+ apps I have installed. The rest is not that big, but there are some apps that need a bit more time. Sensor tools and BTHRM have a lot of code. I do not know where the 2.23 seconds not explained by the 1.6 seconds from .boot are from. There could be some time from initial drawing and loading widgets for the launcher in there. I timed from the first frame showing the loading screen to the icons from iconlauncher showing.
-
I too would prefer @Gordon s resources to be spend on improving system wide performance. There are lots more people doing experimentation on the JS side of things than on firmware level.
My daily driver bangle install currently needs 1.6s of time between the first line of .boot and the first line of the default launched clock after reboot. Switching from clock to launcher currently needs 3.83 seconds for me, which feels like a LOT. It is actually the most remarked on thing if I show the bangle to other people. Fastloading reduces this to basically nothing in case of apps without significant own loading times. Worst offender beeing imageclock adding at least a few hundred ms to those nearly four seconds ;)
I created my fair share of memory leaking boot code, so the problem generally is not a new one :)
The problem for fastloading can be split in a few parts. Some are not as critical for memory leaks.
Clock <-> Launcher: Should be relatively easy to check for memory leaks during development and is one of the most done transitions. This works well on current firmware with setUI-remove and the eval style of loading the clock.
Launcher -> App: The only point that should not be leaking memory is the launcher, since it needs to remove itself to free up memory for the app. The app should not see any difference to beeing loaded after the normal boot code then.
App -> Launcher/Clock: Technically the same as launcher to app, but a lot more apps to check and validate. Modification of all apps and keeping them clean in regard of memory use is not really feasible. Current state of affairs is a reboot of the system if no remove-method is implemented. This also mitigates smaller leaks by rebooting every few app launches. This waiting time is not really a problem, since closing an app often means stopping use of the watch anyway. At least for me the delay after using an app is a lot less impactful than the waiting for an app to open.
Widgets: This one is currently not that easy to solve. I have experimented with tracking which listeners and timeouts etc. a widget registers, but trying to clean that up later opens a whole can of worms that probably better stays closed (think reused timeout numbers).
I think some change in handling the widgets would be nice, maybe returning the widget as an object from the wid.js with some conventions on how to structure them could work well (a bit like the recorder modules are loaded and added). This would also allow to return a stub object that loads the widgets code from a file on first call to keep the memory use minimal in case an app does not
loadWidgets
. This would solve some things like the widget name not matching the file name or widget JS loading no or more than one widget on some condition (not easily recoverable from if widgets are not filterable for this behaviour).My current experiments include a fastload-app, which modifies the Bangle object to add some handling for unloading widgets and additionally widgets need to implement a remove method to properly work (at least those that use timeouts/listeners etc.). An optional update method is used for notifying widgets of changes like
Bangle.CLOCK
.
This seems to work well for apps with and without widgets. Could be more efficient, since it effectively hard unloads and later reloads all widgets if an app does not callloadWidgets
immediately. There is of course the problem of not (yet) modified widgets, but this could be handled by disabling fastload in this style if one of those is loaded. I'm sure there are still lots of edge case that I have not yet triggered.
Main magic hides here: fastload.boot.jsCurrent state of experimentation in my apploader:
Firmware: at least 2v15.20
Clock: Anton
Fastload enabler:
fastload
Modifed for fastloading:
iconlaunch
gpstrek
widclk
widbat
widlock
widbars
widcloselaunchNot really needed for fast loading but very nice effect:
@Sir_Indy s slightly modified widhide for swiping the widget bar from the top on clocks.But all of this might well be a case of:
-
I think as a first step the implementation as a remove handler set for setUI is probably enough. It should be possible for apps like quicklaunch to just call setUI() to trigger the removal of the currently running main app.
I did a small demonstration of the combination of UI removal, @Sir_Indy s hidable widget bar, modified gpstrek/iconlaunch apps and widgets implementing an update method. This may be the loading-screen-less future ;)
Not yet bug free, far from optimal but very cool to see the Bangle be about as fast as my old Amazfit BIP S.
-
I think the customizer will not be a problem, 700k in a variable in a browser should be normal stuff. Imagine images etc. beeing loaded into variables.
In the gpstrek app, the route and its waypoint are loaded from one big file by storing offsets into that file in a variable. I read the file completely and build this index on the first load, but you could build your index in the customizer and upload it as json. Imageclock does something like that for the resources file containing all images for the watchface.
You could just read a few k into memory at a time and store the indices for the previous and upcoming chunk somewhere to load them as soon as needed. The chunk size would need to be a tradeoff between index size and used RAM per chunk.
I had really big files from the recorder, so I would expect the file system to handle those files just fine. -
This would be great. During hiking I had my phone tracking the location anyway because of the much better GPS reception. Using that to cheaply (in terms of battery) providing a position to the bangle is a good thought.
@user148386 having a new method would mean all apps would have to be changed to use this. Overriding the original method (possibly as a config option) solves this. This can also allow an automatic fallback to internal GPS should the watch get disconnected from the phone.
Are you on 2v15 or on a cutting edge build? This seems to be the widgets being drawn and the inner watchface then drawing over them. So watchface probably draws the outer dial, then widgets are drawn and the inner dial is then refreshed, probably every minute or so. That could be a side effect of the fast loading of the clock after the launcher, but that would only occur on the cutting edge build.