I'm developing a set of complications that I would like have regardless of the other installed apps and watch faces. Yes, at some point I am reinventing the wheel, but at the same time I am using this as a learning project. This will also ensure that I always have all the complications I use, available and that they all have the same format and style, instead of relying on 3rd party apps to provide them separately.
The set will have complications for Heart rate, gps coordinates, hours, minutes, seconds, dd/MM date, dd/MM/yy date, battery, etc.
When I started programing all this I found several problematic pieces (most likely because this is the first time I develop complications, or an app for android wear for that matter) and hence this question.
Note that some of this behavior might be specific to the Huawei Watch 2 LTE.
1) Upgrade interval push / pull.
I understand complications as data providers, whose only responsibility is to provide the data to whatever watch face is calling them. This means that we are not certain (and we rely on the watch face developer) to know about the complication and request updates accordingly. This turns some complications completely useless if not updated in time (for example display the Seconds). Could also leave to complications displaying old data (for example old GPS coordinates, old heart rate bpm).
So ok, I decided to implement ProviderUpdateRequester
with the AlarmManager
to push data to the watch face. The problem again, is with complications that should happen faster, like seconds, as Android will block pending intents if they are schedule too often. So in order to get around that, I decided to use Android handlers within the same service instance, which turn out to be not a good idea because of the next topic.
2) Complication lifecycle
By debugging, I found out that the instance of the ComplicationProviderService
object that is executing onComplicationActivated
, onComplicationUpdate
, onComplicationDeactivated
can be different. This means that this is not a sticky service (single instance) that will be always running, but every update creates a new instance of the service. This is problematic because of heavy initialization complications: for example GPS, or Heart Rate monitor that need to listen for new values and it might take a while to retrieve the first value. And also, for those complications that can't rely on AlarmManager, and/or need to keep some sort of state between updates executions.
3) Display aware service
To get around the previous point , let's say you have static variables on your complication service , which are initialized onComplicationActivated
and disabled at onComplicationDeactivated
. For example, this could be getting a reference for the LocationProvider and starting listening for location updates. This will ensure that each invocation to onComplicationUpdate
will not have to perform the heavy/cold initialization and will have access to the most up-to-date data.
However, this also means that your logic will executed regardless if onComplicationUpdate
is called or not.
When in ambient mode (or screen off) the watch face can decide not to update the complication by not calling onComplicationUpdate
, but it's not aware of our static logic, nor the ComplicationProviderService
has a callback invocation for when the screen goes into ambient mode or turns on/off. This is a problem, because in our example, if the screen is off, we are still going to be listening for GPS coordinates, and most likely draining the battery.
Sure, we can deal with this by using a combination of BroadcastReceiver (Intent.ACTION_SCREEN_ON/OFF) and DisplayManager.DisplayListener
, but then again, not sure if i'm taking the correct path here, because this will mean that we are now creating services that need to be statically aware of the state of the display.
4) Detect screen on/off
The BroadcastReceiver
for Intent.ACTION_SCREEN_ON/OFF
works as expected when ambient mode is disabled, but it doesn't it's enabled. When ambient mode is enabled, Intent.ACTION_SCREEN_OFF
is dispatched when going into ambient mode, but Intent.ACTION_SCREEN_ON
is not dispatched when coming out of ambient mode. While a bit more complex, this can be accomplished by using DisplayManager.DisplayListener
to get updates on the onDisplayChanged
callback.
TL;RD
1) How do you ensure watch faces display your complications in a timely manner to always have correct and most up-to-date information?
2) How do you deal heavy/cold initialization of a ComplicationProviderService
if everytime onComplicationUpdate
is called the service instance is different?
3) Is making a long running service display-aware something crazy to do?
4) Technically the screen is still on when in ambient mode, so why is Intent.ACTION_SCREEN_OFF
being broadcasted? Why isn't Intent.ACTION_SCREEN_ON/OFF
symetrical when ambient mode is enabled?
5) Maybe complications shouldn't be use for exposing realtime information?
Thanks a lot
A couple of things to unpack:
ProviderUpdateRequester
is designed more for (on average infrequent) irregular updates like messages coming through a chat app.ComplicationText.TimeDifferenceBuilder
and ComplicationText.TimeFormatBuilder
.For your use case, a more appropriate thing maybe to consider an always-on app. User uses it for a certain time period for a specific purpose so they explicitly agree to use to use more battery to track things like GPS or heart rate. For example, a lot of running apps on Wear do this.
I hope this helps.