I stumbled upon this library by Anothem. Basically it provides a SeekBar similar to the default Android one, but with two thumb controls allowing a range to be selected, and some other extras as well.
As at the moment the seekbar can select range between numbers/digits but cannot be used for actual time range selection in 12 hours system like 12am-3pm.
I am tring to achieve the above but with no success.
After looking through stackoverflow i stumbled again at a similar issue but cannot seem to reuse or implement for my case.
Similarly a github repo promised to solve the issue but after running the class, i simply cannot see the changes.
Any suggestions will be highly appreciated!
Here is the class to modify.
How i used it:
RangeSeekBar rangeSeekBar = (RangeSeekBar) findViewById(R.id.seekbar);
rangeSeekBar.setRangeValues(15, 90);
rangeSeekBar.setSelectedMinValue(20);
rangeSeekBar.setSelectedMaxValue(88);
Why are you setting the range values from 15 to 90? If you are trying to depict time, you should set the range values as:
rangeSeekBar.setRangeValues(0,24 * SMALLEST_HOUR_FRACTION);
where SMALLEST_HOUR_FRACTION is the smallest block of time you can select (2 to select every half hour, 4 for every quarter hour (15 minutes), 60 for every minute).
Then, to determine the time the user selected, use:
int minHour = rangeSeekBar.getSelectedMinValue() / SMALLEST_HOUR_FRACTION;
int minMinute = SMALLEST_HOUR_FRACTION * (rangeSeekBar.getSelectedMinValue() % SMALLEST_HOUR_FRACTION);
int maxHour = rangeSeekBar.getSelectedMaxValue() / SMALLEST_HOUR_FRACTION;
int maxMinute = SMALLEST_HOUR_FRACTION * (rangeSeekBar.getSelectedMaxValue() % SMALLEST_HOUR_FRACTION);
Related
I am trying to get the current heart beat rate using Samsung health. I am using Samsung's SDK for Android basing my code on the example provided to count the steps. Here is how I adapted the code for some tests I did at 6pm:
HealthDataResolver resolver = new HealthDataResolver(mStore, null);
// Set time range from start time of today to the current time
long startTime = 16 * 60 * 60 * 1000;
long endTime = startTime + 2 * 60 * 60 * 1000;
HealthDataResolver.Filter filter = HealthDataResolver.Filter.and(
HealthDataResolver.Filter.greaterThanEquals(HealthConstants.HeartRate.START_TIME, startTime),
HealthDataResolver.Filter.lessThanEquals(HealthConstants.HeartRate.END_TIME, endTime));
HealthDataResolver.ReadRequest request = new ReadRequest.Builder()
.setDataType(HealthConstants.HeartRate.HEALTH_DATA_TYPE)//.setDataType(HealthConstants.StepCount.HEALTH_DATA_TYPE)
.setProperties(new String[] {HealthConstants.HeartRate.HEART_RATE})
.setLocalTimeRange(
HealthConstants.StepCount.START_TIME,
HealthConstants.StepCount.TIME_OFFSET,
startTime, endTime)
.setFilter(filter)
.build();
try {
resolver.read(request).setResultListener(mListener);
} catch (Exception e) {
Log.e(MainActivity.APP_TAG, "Getting step count fails.", e);
}
So basically, the problem is that originally, the steps code meassured the interval of the whole day (endTime = startTime + ONE_DAY_IN_MILISECONDS). I tried to adapt this code to obtain the heart beat average of the last 20 seconds, but when I shrink the margin between the startTime and the endTime to an interval lower than one hour, the average I retrieve is zero. Therefore, my question is: is this the right way to obtain the current heart beat? If so, what I am doing wrong or how can this be achieved?
I finally found the answer. Samsung Health updates it's data with a very low frequency. That cannot be changed and therefore, when trying to access to small time intervals, it does not find any register and retrieves a zero value. The correct way to do this is to directly connect to the wristband via BLE using the Samsung SDK for Devices. There are a bunch of guidelines in order to obtain the different meassurements. For this case, it should be used the Heart Rate Monitor Guide.
I'm successfully registering a SipProfile with 1 hour expiration. I see the REGISTER message in Wireshark on the SIP server machine, re-sent with authorization.
Brekeke replies immediately with a STATUS: 200 OK, passing back 'Expires:3600'.
Both Xamarin and Android docs tell me that lExpire should be duration in seconds before the registration expires, so it represents an interval. I want to expose received values, so multiply by TimeSpan.TicksPerSecond (to convert to TimeSpan), 3600 would result in 0.01:00:00:
void ISipRegistrationListener.OnRegistrationDone( string lclPrfUri, long lExpire )
{
long l= DateTime.Now.Ticks;
double d= (double)l / lExpire;
string s= string.Format( "RegSucced( '{0}', {1} ), {2}, {3}", lclPrfUri, lExpire,
new TimeSpan( lExpire * TimeSpan.TicksPerSecond )
.ToString( "d\\.hh\\:mm\\:ss" ), d );
..
}
But i'm getting lExpire values in the range of 1.5 trillions (1'541'195'345'242)!
As i saw it constantly growing with time, i thought it might be related to Ticks, so i exposed both and gathered the following statistics (H == T + 1 hour -- expected expiration):
# DT.Now.Ticks (T) lExpire (E) T/E ratio T+36000000000 (H) H/E ratio
1 636767624266839520 1541183611836 413167.918 636767660266839520 413167.941
2 636767669188704010 1541188122398 413166.738 636767705188704010 413166.761
3 636767670260843180 1541188229643 413166.710 636767706260843180 413166.733
4 636767670974718350 1541188301027 413166.691 636767706974718350 413166.715
5 636767693193745790 1541190522922 413166.110 636767729193745790 413166.133
And the ratios look surprisingly consistent, though the magic of 413166 escapes me.. And from that lExpire looks more like a reference to a point in time, than an interval, no?
But according to docs i should get 3600 without any scaling factors, right? What is going on??!
UPDATE (2019-Jan-25)
Finally got closer to an answer. Digging through Android source files (e.g. https://android.googlesource.com/platform/frameworks/base/+/431bb2269532f2514861b908d5fafda8fa64da79/voip/java/com/android/server/sip/SipService.java) i found the following fragment:
#Override
public void onRegistrationDone(ISipSession session, int duration) {
if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
synchronized (SipService.this) {
if (notCurrentSession(session)) return;
mProxy.onRegistrationDone(session, duration);
if (duration > 0) {
mSession.clearReRegisterRequired();
mExpiryTime = SystemClock.elapsedRealtime() + (duration * 1000);
..
SystemClock.elapsedRealtime() returns milliseconds since boot, including time spent in sleep.
Time in UNIX/Linux/Java is kept as number of seconds since epoch (1970-01-01T00:00:00Z).
Wikipedia's page shows Current Unix time as 1548450313 (2019-01-25T21:05:13+00:00), which is only a 1000 times (s-to-ms multiplier!) different in range from lExpire values i observe. The formula for mExpiryTime in the last line kinda gives hope.. "Eureka!"?
Let's check if they conform to "# of ms since epoch" theory:
DateTime dtEpoch = new DateTime( 1970, 1, 1, 0, 0, 0, DateTimeKind.Utc );
void ISipRegistrationListener.OnRegistrationDone( string lclPrfUri, long lExpire )
{
DateTime dt = dtEpoch.AddMilliseconds( lExpire ).ToLocalTime( );
string s= string.Format( "RegSucced( '{0}', {1}, {2} )", lclPrfUri, lExpire,
dt.ToString( "yyyy-MM-dd HH:mm:ss.fff" ) );
..
}
Yesterday's screenshots - with that addition (magenta arrows mark exposed values):
As you can see, the difference between lExpire converted into time and DateTime.Now (captured in the log) is negligible - in ms range!
I actually like the idea of being given an expiration moment instead of duration (which need to be added to an undefined starting point). Was about to answer my question..
But still, there are follow-up unresolved mysteries:
Why documentation says the argument is duration in seconds?
Xamarin may be just copying Android docs, but the source is way wrong!
Is this really how lame Android devs are? Reckon that should be rethorical ((..
Does anybody care that requested expiration period may be trumped [down] by PBX /SIP server, and thus they have to re-register more often?
All calls to .onRegistrationDone(session, duration) in the source files specify duration, not mExpiryTime.
There are constant definitions (EXPIRY_TIME = 3600, SHORT_EXPIRY_TIME = 10, MIN_EXPIRY_TIME = 60) and comparisons of duration to these..
Range of values is drastically different between the two (3600 vs 15 trillion).
How/where does mExpiryTime (expiration moment) make it into the argument of my method?
ms since boot and s (or ms) since epoch are still very different, what makes that adjustment?
and finally, asking a 1 hr expiration and receiving it in the OK reply from SIP server, i expect that to be reflected here too, so lExpire should be 1 hr in the future, not now!!
Any ideas?
my application performs in background step counting using the step detector sensor API's introduced in android 4.4.X.
It's essential to my app to know the exact time (at least accuracy of a second) each step event has accrued.
because I perform sensor batching , the time onSensorChanged(SensorEvent event) been called is not the same time when the step event took place - I must use the event.timestampfield to get the event time.
the documentation about this field is:
The time in nanosecond at which the event happened
The problem:
In some devices (such Moto X 2013) seems like this timestamp is time in nano seconds since boot, while in some devices (such Nexus 5) it's actually returns universal system time in nano seconds same as System.currentTimeMills() / 1000.
I understand, there's already an old open issue about that, but since sensor batching is introduced - it becomes important to use this field to know the event time, and it's not possible to rely anymore on the System.currentTimeMills()
My question:
What should I do to get always the event time in system milliseconds across all devices?
Instead of your "2-day" comparison, you could just check if event.timestamp is less than e.g. 1262304000000000000 - that way you'd only have a problem if the user's clock is set in the past, or their phone has been running for 40 years...
Except that a comment on this issue indicates that sometimes it's even milliseconds instead of nanoseconds. And other comments indicate that there's an offset applied, in which case it won't be either system time or uptime-based.
If you really have to be accurate, the only way I can see is to initially capture an event (or two, for comparison) with max_report_latency_ns set to 0 (i.e. non-batched) and compare the timestamp to the system time and/or elapsedRealtime. Then use that comparison to calculate an offset (and potentially decide whether you need to compensate for milliseconds vs nanoseconds) and use that offset for your batched events.
E.g. grab a couple of events, preferably a couple of seconds apart, recording the System.currentTimeMillis() each time and then do something like this:
long timestampDelta = event2.timestamp - event1.timestamp;
long sysTimeDelta = sysTimeMillis2 - sysTimeMillis1;
long divisor; // to get from timestamp to milliseconds
long offset; // to get from event milliseconds to system milliseconds
if (timestampDelta/sysTimeDelta > 1000) { // in reality ~1 vs ~1,000,000
// timestamps are in nanoseconds
divisor = 1000000;
} else {
// timestamps are in milliseconds
divisor = 1;
}
offset = sysTimeMillis1 - (event1.timestamp / divisor);
And then for your batched events
long eventTimeMillis = (event.timestamp / divisor) + offset;
One final caveat - even if you do all that, if the system time changes during your capture, it may affect your timestamps. Good luck!
I found a work-around solution that solving the problem. the solution assumes that the timestamp can be only one of the two: system timestamp, or boot time:
protected long getEventTimestampInMills(SensorEvent event) {
long timestamp = event.timestamp / 1000 / 1000;
/**
* work around the problem that in some devices event.timestamp is
* actually returns nano seconds since last boot.
*/
if (System.currentTimeMillis() - timestamp > Consts.ONE_DAY * 2) {
/**
* if we getting from the original event timestamp a value that does
* not make sense(it is very very not unlikely that will be batched
* events of two days..) then assume that the event time is actually
* nano seconds since boot
*/
timestamp = System.currentTimeMillis()
+ (event.timestamp - System.nanoTime()) / 1000000L;
}
return timestamp;
}
According to the link in your question:
This is, in fact, "working as intended". The timestamps are not
defined as being the Unix time; they're just "a time" that's only
valid for a given sensor. This means that timestamps can only be
compared if they come from the same sensor.
So, the timestamp-field could be completely unrelated to the current system time.
However; if at startup you were to take two sensor samples, without batching, you could calculate the difference between the System.currentTimeMillis() and the timestamp, as well as the quotient to the differences between the different times you should be able to convert between the different times:
//receive event1:
long t1Sys = System.currentTimeMillis();
long t1Evt = event.timestamp;
//receive event2:
long t2Sys = System.currentTimeMillis();
long t2Evt = event.timestamp;
//Unregister sensor
long startoffset = t1Sys - t1Evt; //not exact, but should definitely be less than a second, possibly use an averaged value.
long rateoffset = (t2Sys - t1Sys) / (t2Evt - t1Evt);
Now any timestamp from that sensor can be converted
long sensorTimeMillis = event.timestamp * rateoffset + startoffset;
Is there a way by which using the android chronometer class to set base of the chronometer in 15 minutes and from that period the times goes down until 0 seconds?
I have tried with setBase(60000) but this isn't work.
Check out this thread Android: chronometer as a persistent stopwatch. How to set starting time? What is Chronometer "Base"? as well as this thread Android - Get time of chronometer widget. Neither answers your question directly, but the nuggets there should lead you to an answer.
In general the chronometer works like this (if you would like to set the Base to a specific nr):
mChronometer.setBase(SystemClock.elapsedRealtime() - (nr_of_min * 60000 + nr_of_sec * 1000)))
what you are asking can be done through a countdown (http://developer.android.com/reference/android/os/CountDownTimer.html)
Or create your own countdown by using the chronometer like this (more work should be done cause i just wrote this and did not test it yet)
private OnChronometerTickListener countUp = new OnChronometerTickListener(){
#Override
public void onChronometerTick(Chronometer chronometer){
long elapsedTime = (SystemClock.elapsedRealtime() - mChronometerCountUp.getBase()) / 60000;
Log.v("counting up", elapsedTime);
// you will see the time counting up
count_down--;
if(count_down == 0){
mChronometerCountUp.stop();
}
// an int which will count down,
// this is not (very) accurate due to the fact that u r using the update part of the chronometer
// u just might implement the countdown i guess
// or 2 chronometers (one counting up and an other counting down using the elapsed time :p)
// just remember programming is creating ur solution to problems u face its like expression urself
};
};
http://developer.android.com/reference/android/widget/Chronometer.html
For set the base time you can use elapsedRealtime(), and you can output format with setFormat()
I'm trying to get the time using android and open gl for my racing game.
My code now is:
deltaTime = (System.currentTimeMillis() + startTime) / 1000000000000.0f;
startTime = System.currentTimeMillis();
tickTime += deltaTime;
DecimalFormat dec = new DecimalFormat("#.##");
Log.d("time", dec.format(tickTime/100));
but it's a bit too fast.
You may want to look at a bit of Android Breakout:
http://code.google.com/p/android-breakout/source/browse/src/com/faddensoft/breakout/GameState.java#1001
The computation is similar, but note it uses System.nanoTime(), which uses the monotonic clock. You don't want to use System.currentTimeMillis(), which uses the wall clock. If the device is connected to a network, the wall clock can be updated, which can cause big jumps forward or backward.
The code also includes a (disabled) frame-rate-smoothing experiment that didn't seem to matter much.
As I think you discovered, the key to this approach is to recognize that the time interval between frames is not constant, and you need to update the game state based on how much time has actually elapsed, not a fixed notion of display update frequency.
Since you're working in milliseconds, shouldn't you be dividing by 1000f instead of 1000000000000.0f?