Android Proximity Sensor - Incorrect value - android

I've run into an issue trying to create an application that keeps track of Proximity Sensor values. The intent of the application is to record the instantaneous proximity value on regular intervals (say 30 seconds).
Following the API Documentation, the standard listener is created and attached for that type of sensor. However, the value is ALWAYS reported as '5.0' (5cm, the max value of the sensor) even when I cover the sensor with my hand.
However, if I start a DIFFERENT application (that also monitors Proximity sensor values) both applications start to correctly report proximity. When I close the other application (Android Sensor Box) my application reverts to just reporting 5.0 all the time.
I have debugged the application and set a breakpoint in the 'onSensorChanged' event to double-check what SensorEvent object is being passed to the onSensorChanged method, and it's always 5.0
EDIT #2: It doesn't appear to be limited to the Proximity sensor. The Gyroscope sensor behaves in the same manner while Accelerometer and Magnetometer appear to show the correct and up-to-date values. All four are referenced and accessed in the same fashion.
Any ideas?

Well, it turns out that I will once again be answering my own question.
The way the application was structured (and there was a good reason for this) meant that a Listener was registered and the first reported value from the sensor(s) was taken as the reading without continuously accepting new readouts. This works fine for Accelerometer readings but it turned out that some sensors reported their DEFAULT value in this first reading (such as 'FAR' for the proximity sensor or 0.0/0.0/0.0 for the Gyroscope). It was only after 2-3 'onSensorChanged' events that correct values start to be reported.
I am assuming this has something to do with power saving and certain sensors needing time to become 'ready' to report data. I have no idea why the acceptable practice is to fire off a sensorChanged event with incorrect values but that's what appears to be happening.
Nonetheless, the fix for me was to discard the first 2-3 readings (they come in at millisecond intervals anyway) and simply use the fourth one as the more reliable reading.

Related

Android Wear 2.0 Architecture issues for realtime complications

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.

Moto 360 heart rate sensor rarely provides data

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?

Android accelerometer sensor listener frequency changing unexpectedly

I have an application which calls sensorManager.registerListener() for the accelerometer, gyroscope, and magnetometer, which registers all sensors for the handler and they are each set to SENSOR_DELAY_NORMAL. This works fine with no issues the vast majority of the time.
However, when looking at some logs I noticed the accelerometer would seemingly randomly change is frequency from roughly 179ms (which seems the be the average SENSOR_DELAY_NORMAL on my phone) to about 20ms.
After doing a fair amount of digging and testing, I found the cause to be when the phone is shaken rapidly. When this happens all other sensors will maintain their ~179ms rate, but only the accelerometer will increase its rate to ~20ms. After some period of time the rate will eventually decrease from ~20ms back to the set rate of ~179ms.
I'm not sure how long it takes for it to return to the ~179ms rate, I've tried uninstalled and reinstalling the application and if enough time has not passed yet the accelerometer will still be firing events at ~20ms.
I wanted to see if I could resolve the issue but un-registering and re-registering the listener at the correct rate when this happens, however the accelerometer will keep going at ~20ms irregardless of what I reset it to. I did find out though that I can unregister the accelerometer listener, and that seems to work, but it doesn't solve my problem.
Anybody know why the accelerometer listener would change its rate at which it's firing, and how I might be able to resolve this?
The delay that you request Android for is only a suggested delay. Android system and other applications can change this. Source
The reason why this can happen is quite simple -
There are only limited number of physical sensors available on the device, 1 accelerometer, 1 magnetometer, 1 gyroscope
Say your application, registers for all events from accelerometer every 100ms.
Another application, requests for all events from accelerometer every 10ms
Now since there is only one sensor and there 2 different needs, Android enables the accelerometer to provide the data at the lowest of all delay requests made by all the apps and then Android reports all events at this delay frequency only.
In this case, it is up to the application developer to disregard events when they are in excess to what is required.

Getting the initial temperature

I am writing my first Android app and I am running into what I thought would be a simple issue. I am trying to get the temperature of the device (using the 2.1 APIs). I have followed this example.
If the temperature changes, the app updates as expected. If the device is sitting on my desk, the temperature does not change and I never get an onSensorChanged() callback.
Although this sounds right (i.e. the sensor didn't change, so no update), I can't figure out how to get the current temperature when the app starts out. I was hoping that onSensorChanged() would be called when the app starts, but that is not the case.
Is there some API I have missed? I can't believe that there is no way to display the temperature until it changes.
Thank you.

"Custom" sensor event rates don't seem to work with SensorManager.registerListener(SensorEventListener listener, Sensor sensor, int rate)

UPDATED:
I was able to solve the specific problem I was having by introducing a class-scope static counter and just ignoring ever x number of events. But I'd still like to know what I'm doing wrong re: registering the listener with a hint in microseconds instead of using one of the four given constants.
An Activity in my app is engaging the sensors to obtain the orientation of the device, determine the roll, and utilize it.
I am using
SensorManager.registerListener(SensorEventListener listener, Sensor sensor, int rate)
to register my sensors. From the Android Documentation for this method:
Parameters
[...]
rate
The rate sensor events are delivered at. This is only a hint to the system. Events may be received faster or slower than the specified rate. Usually events are received faster. The value must be one of SENSOR_DELAY_NORMAL, SENSOR_DELAY_UI, SENSOR_DELAY_GAME, or SENSOR_DELAY_FASTEST or, the desired delay between events in microsecond.
If I use one of the 4 predefined constants then the app works fine; however these constants all provide rate hints that are too fast for my needs. I have to send out a UDP packet containing some information with every event change, and the receiving end seems to be getting completely inundated with messages using any of the predefined rates. Using an integer number like 30000 (since the API specifies quantities in microseconds) causes the app to stop reporting sensor events all together.
What am I missing that is preventing me from using my own event rate hints?
I'm pretty sure sensor listener rate did what it's supposed to do. In your question you wrote 30000, which is 30 milliseconds. In the doc, it says the rate is usually faster than the hint. So you are doing faster than 30ms. Is it possible that your other network related routines have been going too fast? It may have caused some blocking that lead you to believe sensor reporting is stopped.
In my application I too find the given NORMAL rate too high. Therefore I set a rate to 250000. I also used moving average calculation to smooth the number by 5. I find the resulting behavior close to the iPhone compass.
Nonetheless, I wouldn't suggest that you do network reporting in a sensor listener. It's not supposed to be done this way. You can however, do some simple calculation in the listener and record the value. Then, use a timer like Handler.postDelayed with a high number, to handle network sending among other things.
This question was asked in 2011, nevertheless answering it as a lot has changed since then; from API 19 (2013+) onwards, there is a new variant of the registration API, in which you could mention at what interval you would like to receive the sensor readings. From the docs:
boolean registerListener (SensorEventListener listener,
Sensor sensor,
int samplingPeriodUs,
int maxReportLatencyUs) Registers a SensorEventListener for the given sensor at the given sampling
frequency and the given maximum reporting latency.
This function is similar to registerListener(SensorEventListener,
Sensor, int) but it allows events to stay temporarily in the hardware
FIFO (queue) before being delivered. The events can be stored in the
hardware FIFO up to maxReportLatencyUs microseconds. Once one of the
events in the FIFO needs to be reported, all of the events in the FIFO
are reported sequentially. This means that some events will be
reported before the maximum reporting latency has elapsed.

Categories

Resources