I'm currently developing an app that uses the activity recogition api.
I have a main activity and an intent service which is called using requestActivityUpdates() and returns data for the activity using the broadcast api.
According to the documentation, requestActivityUpdates' first parameter, detectionIntervalMillis, stands for "the desired time between activity detections. Larger values will result in fewer activity detections while improving battery life. A value of 0 will result in activity detections at the fastest possible rate.".
I'm using a values as low as 100ms and still I get updates once every 3 minutes on average. What could be the problem here? I tested on 2 different fully charged devices already, so I believe this has nothing to do with power saving configurations.
edit: took the devices for a walk and it seems that the rate of updates go up to ~8s. Still way more than expected. :/
Related
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:
Complications are not meant to be updated frequently (think minutes, not seconds) - this is to preserve battery.
ProviderUpdateRequester is designed more for (on average infrequent) irregular updates like messages coming through a chat app.
Time dependent complications - there are not an "update" as such but Wear provide ways for developers to count up / down from certain time and for displaying date related field (world clock, day of the month) without the provider sending the system updates all the time. For this last one, please refer to docs for 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.
In my Android application, I want to display a notification only when the user is not moving or only slowly moving. For example, when a user is driving, I don't want to display notifications. To accomplish this, I've looked into the Activity Recognition API and implemented a basic component -- and it basically works alright.
During some first test, I've noticed that occasionally see some strange output when I simply check for the activity with the highest confidence, e.g., getting a IN_VEHICLE while walking. Since this is based on statistics and what not, I'm not surprised of that. I, therefore, use simple sliding window over the last N activity types. Only when all values in the window are the same, I assume the respective activity.
Now I have 3 configuration parameters:
TIME_BETWEEN_REQUESTS (in milliseconds) as the minimum time between the request for the current activity value as part of the Activity Recognition API. The lower to value the more up-to-date but the higher the battery drain. For testing, I currently set it only to 5000 (=5sec)
MIN_CONFIDENCE (0..100) minimum confidence referring DetectedActivity.getConfidence(). Only if the confidence is larger than this threshold, I consider that the API is confident enough about the detected activity. On the Web, I see most commonly a MIN_CONFIDENCE >= 75
WINDOW_SIZE (>= 1) as the minimum number of the same activity types before I'm sure about the detected activity. Currently, I use WINDOW_SIZE = 3. So if a user is walking and the phone detects an occasional IN_VEHICLE I don't immediately assume that the users are driving. Only if the next two values are also IN_VEHICLE, I change the user's activity state.
Does this make sense? I'm a bit worried if I increase TIME_BETWEEN_REQUESTS -- What is a practical value anyway -- because it takes at least WINDOW_SIZE * TIME_BETWEEN_REQUEST before the activity state of a user will change. Right now it's 15sec, but if I would set TIME_BETWEEN_REQUESTS to 30sec and keep WINDOW_SIZE 3, it would require at least 90sec for a change to occur.
What are good/best practices here? Is the window even necessary or do larger values for TIME_BETWEEN_REQUESTS already do some kind of smoothing?
I managed to get the heart rate sensor and it is working, with some exceptions.
I start the measurement on click, and when there is at least one result with accuracy >= LOW I stop the measurement (by unregistering the listener).
This sequence works 1-2 times, and then it just do not return a value with accuracy >= LOW. I am waiting a few minutes before I give up and close the application. But after opening the app again, most often it works the first 1-2 measurements and then it stops working with the same issue. (but other times it needs a lot of re-launches of the app in order to start giving some results)
Another issue is that if I start the measurement when the watch is not on my wrist/skin but lets say it is floating in the air, then even if I put it on my hand and wait a few minutes it doesn't start giving any valid/accurate value.
Have anybody observed similar behaviour? Is there any specific sequence that needs to be done in order for the sensor to provide more reliable data? Maybe a calibration can be done somehow to make the sensor more sensitive about my skin type or something?
I am eager to know how ActivityRecognition which is in GooglePlay Services works?
I think activities are recognized by accelerometer data.Is it right?.Please give me the details how it goes?
I was looking for this answer also and your post was one of the top results on Google. I did a little more digging and found this https://developer.android.com/reference/com/google/android/gms/location/ActivityRecognitionApi.html which says:
The activities are detected by periodically waking up the device and reading short bursts of sensor data. It only makes use of low power sensors in order to keep the power usage to a minimum. For example, it can detect if the user is currently on foot, in a car, on a bicycle or still.
It doesn't explicitly say which sensors it uses, but we can safely assume that accelerometer is one of them. Since it says sensors (plural) it evidently uses others. But since it says low power you don't have to worry about it using GPS or anything like that. However, it does say that it wakes the device up which would bring it out of its super power saving mode. So, even though it is low power you still may not want it running every 5 seconds all day long. The documentation further down says:
A common use case is that an application wants to monitor activities in the background and perform an action when a specific activity is detected. To do this without needing a service that is always on in the background consuming resources, detected activities are delivered via an intent. The application specifies a PendingIntent callback (typically an IntentService) which will be called when activities are detected. See the documentation of PendingIntent for more details.
I am doing my Master thesis at the moment on WiFi positioning and in order to test my algorithms I needed to collect some data.
To do this I have written a short and very simple program for Android which simply collects the RSSI for all availible access points found by each scan and saves them to file. I have set up a BroadcastReceiver that listens on the event WifiManager.SCAN_RESULTS_AVAILABLE_ACTION and I use a Timer, here called tim, to initiate scans with a WifiManager, called wifi as follows:
tim.schedule(new TimerTask(){
#Override
public void run(){
wifi.startScan();
}
}, 0, 1000);
The problem I am having now is that the initiated scans don't seem to happen every second even if I succeed in initiating them and every now and then there are other scans initiated from some other app that gets recorded as well.
Is there any easy way to scan on a set interval and not receive the scans initiated by some other app?
The whole app can be found on https://github.com/while/RSSIMiner if it helps in any way.
Is there any easy way to scan on a set interval?
If this doesn't work well, I'm afraid not. From my experience, "hardware related" methods may not work exactly like their definition says. For example, I once created a small app which records your position every X minutes. So I call requestLocationUpdates with some minTime parameter. But my phone simply ignores the minTime value, and I get updates from the GPS as soon as they're available, whcih is not what I wanted. I posted a question about it here, and got this answer, from which we learn that prior to jelly bean, devices may simply ignore this value...
So it may be something similar now. I'd try to run this code on the latest Android version. And I don't understand that much in Wifi, but isn't 1 second a too frequent interval for scans? Perhaps the system doesn't ignore the scan request (So it returns true) but the hardware does?
Can we ignore the scans initiated by some other app?
As far as I know, it's negative here too. There are no extras contained in the SCAN_RESULTS_AVAILABLE_ACTION broadcast so you can't know which app initiated the scan.
The best solution will be to defnie your requirements. You can use the ScanResult.timestamp to determine if you should use this result or not. For example, if you're trying to get the RSSI for each access point each second, you can compare the current BSSID to previous BSSIDs. If the current BSSID was included in a scan result from the last second, you can simply ignore it. Then, it doesn't matter how many results you get.
Another, much more simple soltuion will be to create a boolean called scanInitiated and set it to true when starting a scan. When receiving the broacast, use the data only if scanInitiated is true, and then set it to false. This isn't so reliable when the intervals are short, but for long intervals it will work great.