I am currently developing an app that will get Fitness History Data from Google Fit. Getting the steps and weight are okay but getting the sleep data is a bit of a problem. I want to get the accurate start and end time but the only way to get that is to bucket it by activity segment. The problem is, when there's a lot of data you are trying to get (the app that I'm currently developing requires to get data from 365 days ago at the most), it will not even return a timeout error and my app will keep loading. It will not even start to read the data from Google Fit. So, I wanna ask if there's a way to get the sleep data by activity segment despite its large size? And please do share your code. And by the way, this is how I get my sleep data:
val sleepReadRequest = DataReadRequest.Builder()
.aggregate(DataType.TYPE_ACTIVITY_SEGMENT, DataType.AGGREGATE_ACTIVITY_SUMMARY)
.bucketByActivitySegment(1, TimeUnit.MINUTES)
.setTimeRange(offset, end, TimeUnit.MILLISECONDS)
.build()
LogUtil.d(TAG, "getting sleep data...")
Fitness.getHistoryClient(
context,
Objects.requireNonNull<GoogleSignInAccount>(GoogleSignIn.getLastSignedInAccount(context))
)
.readData(sleepReadRequest)
.addOnSuccessListener { dataReadResponse ->
LogUtil.d(TAG, "success sleep data")
val secondSet = handleDataReturned(dataReadResponse, false, DateUtil.convertTimeStampToDate(offset, DateUtil.DATE_FORMAT))
dailyData.addAll(secondSet)
val allDailyList = getDailyDataList(dailyData, userHeight)
callback.onGetDataSuccess(allDailyList)
}
.addOnFailureListener { e ->
LogUtil.d(TAG, "fail sleep data")
if (e is ApiException && e.statusCode == GoogleFitError.NOT_SIGNED.code) { // not signed app exception
revokePermission(context)
callback.onGetDataFailure(GoogleFitError.parse(e.statusCode))
} else {
callback.onGetDataFailure(AppError.parse(Throwable(e)))
}
}
.addOnCompleteListener { task ->
LogUtil.d(TAG, "complete sleep data")
callback.onGetDataComplete(task)
}
Rather than aggregating, can you just read the activity segments and iterate through them yourself?
val sleepReadRequest =
DataReadRequest.Builder()
.read(DataType.TYPE_ACTIVITY_SEGMENT)
.setTimeRange(offset, end, TimeUnit.MILLISECONDS)
.build()
You can then retrieve the returned data with DataReadResult#getDataSet(DataType).
If you find that it's timing out (a year of data at once is potentially rather a lot!) I'd suggest batching the request into smaller ones and caching data in the past which is unlikely to change.
Related
I have tried
val dataRequest = DataReadRequest.Builder()
.aggregate(DataType.TYPE_DISTANCE_DELTA)
.aggregate(DataType.TYPE_CALORIES_EXPENDED)
.aggregate(DataType.TYPE_HEART_RATE_BPM)
.read(DataType.TYPE_WORKOUT_EXERCISE)
.read(DataType.AGGREGATE_MOVE_MINUTES)
.read(DataType.TYPE_MOVE_MINUTES)
.setTimeRange(start2B , end2B , TimeUnit.MILLISECONDS)
.enableServerQueries()
.bucketByActivitySegment(1, TimeUnit.MINUTES)
.build()
Fitness.getHistoryClient(this, account)
.readData(dataRequest)
.addOnSuccessListener {
for (bucket in it.buckets) {
for (data in bucket.dataSets) {
for (point in data.dataPoints) {
when (point.dataType) {
DataType.AGGREGATE_DISTANCE_DELTA -> distanceTotal.add( point.getValue(Field.FIELD_DISTANCE).asFloat() )
DataType.TYPE_HEART_RATE_BPM -> heartRateTotal.add( point.getValue(Field.FIELD_BPM).asFloat() )
DataType.TYPE_CALORIES_EXPENDED -> caloriesTotal.add( point.getValue(Field.FIELD_CALORIES).asFloat() )
DataType.TYPE_WORKOUT_EXERCISE -> exerciseNames.add( "[${point.getValue(Field.FIELD_EXERCISE).asString()}] ")
DataType.TYPE_MOVE_MINUTES -> Log.i("GOOGLE-FIT", "Move Minutes $point ")
DataType.AGGREGATE_MOVE_MINUTES -> Log.i("GOOGLE-FIT", "Moving Mins Count $point ")
}
}
Log.i("GOOGLE-FIT", "Google Daily DATE Read INFO A: $distanceTotal B: $heartRateTotal C: $caloriesTotal D: $exerciseNames ")
}
}
I get calories and distance sometimes, but never exerciseName
What is the correct way to access exercise/workout recorded in Google Fit?
Does anyone know of a tutorial/example showing how to get workout data from the Google fit API?
Where can one get google git API support?
thanks in advance
You may try Sessions API this will let you obtain a list of sessions from the fitness store that match some criteria.
https://developers.google.com/fit/android/using-sessions#read-fitness-data
Finally, if you haven't seen this yet, please check out the sample apps to help you get started writing/understanding Fit features in Android.
https://developers.google.com/fit/android/samples
I am trying to implement long polling which is lifecycle aware(in Activity/Fragment). The polling will be scoped to the fragment which sends API request to the server every fixed interval of time. However, I am unable to implement it.
This is how I want the implementation to be like
Have a hard timeout on client-side without considering any extra delay incurred in receiving the response.
Wait for the response of previous API call before sending the next request. i.e., the request in polling queue should wait for response irrespective of its priority due to polling interval
Consider:
HARD_TIMEOUT = 10s
POLLING_INTERVAL = 2s
Request 1: Started at: 0sec Response delay:1.2sec Duration left: 0.8sec
Request 2: Started at: 2sec Response delay:0.4sec Duration left: 1.6sec
Request 3: Started at: 4sec Response delay:2.5sec Duration left: 0sec
Request 4: Started at: 6.5sec Response delay:0.5sec Duration left: 1.0sec
Request 5: Started at: 8sec Response delay:0.8sec Duration left: 1.2sec
For this use case, I want to use polling instead of socket. Any Idea/solutions would be appreciated. Thank you.
Ok, Figured out a solution for polling using channels. this should help someone searching for an example.
private val pollingChannel = Channel<Deferred<Result<OrderStatus>>>()
val POLLING_TIMEOUT_DURATION = 10000L
val POLLING_FREQUENCY = 2000L
A channel is required to hold your asynchronous request just in case more request comes in while your async task is being executed.
val pollingChannel = Channel<Deferred<Pair<Int,Int>>>()
QUEUE EXECUTOR: It will pick an async task and start executing them in FIFO order.
CoroutineScope(Dispatchers.IO).launch {
for (i in pollingChannel) {
val x = i.await()
println("${SimpleDateFormat("mm:ss.SSS").format(Calendar.getInstance().time)} Request ${x.first}: value ${x.second}")
}
}
POLLING FUNCTION: Adds your async task to the polling channel every fixed interval of time until timeout.
CoroutineScope(Dispatchers.IO).launch {
var reqIndex = 1
val timedOut = withTimeoutOrNull(POLLING_TIMEOUT_DURATION) {
while (receiverJob.isActive) {
pollingChannel.send(async {
getRandomNumber(reqIndex++)
})
delay(POLLING_FREQUENCY)
}
}
}
ASYNCHRONOUS OPERATION
to avoid answer being verbose I created a function with a random delay, please replace with the required API call
private suspend fun getRandomNumber(index: Int): Pair<Int,Int> {
val randomDuration = (1..6L).random() * 500
delay(randomDuration)
return Pair(index,(0..100).random())
}
SAMPLE OUTPUT
I'm trying to get the Free busy data of other people that are within my Google organization using the google-api-services-calendar:v3 for Android (using Kotlin). I'm getting the times just fine for events with a set duration. But all day events don't show up on the list. Documentation on this is almost nowhere to be found and the stuff that I find on developers.google.com contains code that was deprecated in 2013...
// ...
val busyTimesList = mutableListOf<AgendaPlotter.TimeSpan>()
SessionService.sharedInstance.getGoogleAccount(activity)
observeOn(Schedulers.io())
.subscribe {
mCredential!!.selectedAccount = it.account
val request = FreeBusyRequest()
val durationCal = Calendar.getInstance()
durationCal.time = startDay.time
Calendars.startOfDay(durationCal)
request.timeMin = DateTime(durationCal.time)
durationCal.add(Calendar.DATE, 1)
request.timeMax = DateTime(durationCal.time)
val requestItems = listOf(FreeBusyRequestItem().setId("email#from.colleague"))
request.items = requestItems
request.timeZone = TimeZone.getDefault().id
val busyTimes: FreeBusyResponse
try {
val query = mService!!.freebusy().query(request)
// Use partial GET to retrieve only needed fields.
query.fields = "calendars"
busyTimes = query.execute()
busyTimes.calendars.forEach {
it.toPair().second.busy.forEach { timeSpan ->
val busyTime = AgendaPlotter.TimeSpan()
busyTime.fromTime.timeInMillis = timeSpan.start.value
busyTime.toTime.timeInMillis = timeSpan.end.value
busyTimesList.add(busyTime)
}
}
emitter.onNext(busyTimesList)
} catch (e: IOException) {
e.printStackTrace()
// ...
}
}
// ...
So my question, how do I also obtain the whole day events?
After some searching I noticed that there is actually nothing wrong with the API. It's a setting for a whole day event to be busy of free.
By default this is set to free, which makes it not show as a busy time, which makes sense. This goes unnoticed by a lot of people and they will be "free" on that day.
In my Android App I have a presenter which handles user interactions, contains kind of request manager and if needed sends user input over request manager to request manager.
Request manager itself contains server API and handles server request using this RxJava.
I have a code, which sends a request to server everytime a user enters a message and show the response from server:
private Observable<List<Answer>> sendRequest(String request) {
MyRequest request = new MyRequest();
request.setInput(request);
return Observable.fromCallable(() -> serverApi.process(request))
.doOnNext(myResponse -> {
// store some data
})
.map(MyResponse::getAnswers)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
}
However now I need to have kind of queue. The user may send a new message before the server has responded. Each message from the queue should be processed sequentially. I.e. the second message will be sent after we've got a response to the first message and so on.
In case an error occurs no further requests should be handled.
I also need to display the answers within a RecyclerView.
I have no idea how to change the code above to achieve the handling described above
I see kind of problem. On one hand, this queue can be anytime updated by the user, on the other hand anytime server sent a response the message should be removed from the queue.
Maybe there is a rxjava operator or special way I just missed.
I saw a similar answer here, however, the "queue" there is constant.
Making N sequential api calls using RxJava and Retrofit
I'll be very thankful for any solution or link
I don't fnd any elegant native-RxJava solution. So I will custom a Subscriber to do your work.
For your 3 points:
For sequential execution, we create a single thread scheduler
Scheduler sequential = Schedulers.from(Executors.newFixedThreadPool(1));
For stop all requests when error occur, we should subscribe all request together instead of create a Flowable every time. So we define following functions (here I request is Integer and response String):
void sendRequest(Integer request)
Flowable<String> reciveResponse()
and define a field to make association of request and response flow:
FlowableProcessor<Integer> requestQueue = UnicastProcessor.create();
For re-run the not-sent request, we define the rerun function:
void rerun()
Then we can use it:
reciveResponse().subscribe(/**your subscriber**/)
Now let us implement them.
When send request, we simply push it into requestQueue
public void sendRequest(Integer request) {
requestQueue.onNext(request);
}
First, to do the request sequentialy, we should schedule work to sequential:
requestQueue
.observeOn(sequential)
.map(i -> mockLongTimeRequest(i)) // mock for your serverApi.process
.observeOn(AndroidSchedulers.mainThread());
Second, to stop request when error occur. It's a default behavior. If we do nothing, an error will broken the subscription and any futher items will not be emitted.
Third, to re-run the not-sent requests. First because that the native operator will cancel the stream, like MapSubscriber do (RxJava-2.1.0-FlowableMap#63):
try {
v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.");
} catch (Throwable ex) {
fail(ex);// fail will call cancel
return;
}
We should wrap the error. Here I use my Try class to wrap the possible exception, you can use any other implementation that can wrap the exception instead of throw it:
.map(i -> Try.to(() -> mockLongTimeRequest(i)))
And then it's the custom OnErrorStopSubscriber implements Subscriber<Try<T>>, Subscription.
It request and emits items normally. When error occur(in fact is a failed Try emitted) it stopped there and won't request or emit even downstream request it. After call rerun method, it will back to the running statu and emit normally. The class is about 80 lines. You can see the code on my github.
Now we can test our code:
public static void main(String[] args) throws InterruptedException {
Q47264933 q = new Q47264933();
IntStream.range(1, 10).forEach(i -> q.sendRequest(i));// emit 1 to 10
q.reciveResponse().subscribe(e -> System.out.println("\tdo for: " + e));
Thread.sleep(10000);
q.rerun(); // re-run after 10s
Thread.sleep(10000);// wait for it complete because the worker thread is deamon
}
private String mockLongTimeRequest(int i) {
Thread.sleep((long) (1000 * Math.random()));
if (i == 5) {
throw new RuntimeException(); // error occur when request 5
}
return Integer.toString(i);
}
and output:
1 start at:129
1 done at:948
2 start at:950
do for: 1
2 done at:1383
3 start at:1383
do for: 2
3 done at:1778
4 start at:1778
do for: 3
4 done at:2397
5 start at:2397
do for: 4
error happen: java.lang.RuntimeException
6 start at:10129
6 done at:10253
7 start at:10253
do for: 6
7 done at:10415
8 start at:10415
do for: 7
8 done at:10874
9 start at:10874
do for: 8
9 done at:11544
do for: 9
You can see it runs sequentialy. And stopped when error occur. After call rerun method, it continue handle the left not-sent request.
For complete code, see my github.
For this kind of behaviour I'm using Flowable backpressure implementation.
Create outer stream that is parent for your api request stream, flatMap the api request with maxConcurrency = 1 and implement some sort of buffer strategy, so your Flowable doesn't throw exception.
Flowable.create(emitter -> {/* user input stream*/}, BackpressureStrategy.BUFFER)
.onBackpressureBuffer(127, // buffer size
() -> {/* overflow action*/},
BackpressureOverflowStrategy.DROP_LATEST) // action when buffer exceeds 127
.flatMap(request -> sendRequest(request), 1) // very important parameter
.subscribe(results -> {
// work with results
}, error -> {
// work with errors
});
It will buffer user input up to given threshold, and then drop it(if you don't do this it will throw exception, but it is highly unlikely that user will exceed such buffer), it will execute sequentially 1 by 1 like a queue. Don't try to implement this behaviour yourself if there are operators for thing kind of behaviour in libary itself.
Oh I forgot to mention, your sendRequest() method must return Flowable or you can convert it to Flowable.
Hope this helps!
My solutions would be as follows (I did something similar in Swift before):
You will need a wrapper interface (let's call it "Event") for both requests and responses.
You will need a state object (let's make it class "State") that will contain request queue and the latest server response, and a method that will accept "Event" as parameter and return 'this'.
Your main processing chain will look like Observable state = Observable.merge(serverResponsesMappedToEventObservable, requestsMappedToEventObservable).scan(new State(), (state, event) -> { state.apply(event) })
Both parameters of the .merge() method will probably be Subjects.
Queue processing will happen in the only method of "State" object (pick and send request from the queue on any event, add to queue on request event, update latest response on response event).
i suggest to create asynchronous observable methods , here a sample :
public Observable<Integer> sendRequest(int x){
return Observable.defer(() -> {
System.out.println("Sending Request : you get Here X ");
return storeYourData(x);
});
}
public Observable<Integer> storeYourData(int x){
return Observable.defer(() -> {
System.out.println("X Stored : "+x);
return readAnswers(x);
}).doOnError(this::handlingStoreErrors);
}
public Observable<Integer> readAnswers(int h){
return Observable.just(h);
}
public void handlingStoreErrors(Throwable throwable){
//Handle Your Exception.
}
the first observable will send request when he get response will proceed the second one and you can chain , you can customize each method to handle errors or success, this sample like queue.
here the result for execution :
for (int i = 0; i < 1000; i++) {
rx.sendRequest(i).subscribe(integer -> System.out.println(integer));
}
Sending Request : you get Here X
X Stored : 0
0
Sending Request : you get Here X
X Stored : 1
1
Sending Request : you get Here X
X Stored : 2
2
Sending Request : you get Here X
X Stored : 3
3
.
.
.
Sending Request : you get Here X
X Stored : 996
996
Sending Request : you get Here X
X Stored : 997
997
Sending Request : you get Here X
X Stored : 998
998
Sending Request : you get Here X
X Stored : 999
999
I'm doing a long write to a BLE for making an OTA update, but I need to wait for the write response of the BLE device for sending more data but I don't know how to catch the device write response, I'm using a Samsung galaxy tab s2 with android 7, and Kotlin for my code
override fun otaDataWrite(data:ByteArray) {
manager.connection?.flatMap { rxBleConnection: RxBleConnection? -> rxBleConnection?.createNewLongWriteBuilder()
?.setCharacteristicUuid(OTACharacteristics.OTA_DATA.uuid)
?.setBytes(data)
?.setMaxBatchSize(totalPackages)
?.build()
}?.subscribe({ t: ByteArray? ->
Log.i("arrive", "data ${converter.bytesToHex(t)}")
manageOtaWrite()
}, { t: Throwable? -> t?.printStackTrace() })
every time that I write the characteristic the subscriptions respond me immediately with the written data, I need capture the response of the characteristic, for sending more data
You are writing about response from the characteristic — I assume that the characteristic you refer is the one with UUID=OTA_DATA. The Long Write consist of small writes internally (so called batches).
What you probably want to achieve is something like:
fun otaDataWrite(data: ByteArray) {
manager.connection!!.setupNotification(OTA_DATA) // first we need to get the notification on to get the response
.flatMap { responseNotificationObservable -> // when the notification is ready we create the long write
connection.createNewLongWriteBuilder()
.setCharacteristicUuid(OTA_DATA)
.setBytes(data)
// .setMaxBatchSize() // -> if omitted will default to the MTU (20 bytes if MTU was not changed). Should be used only if a single write should be less than MTU in size
.setWriteOperationAckStrategy { writeCompletedObservable -> // we need to postpone writing of the next batch of data till we get the response notification
Observable.zip( // so we zip the response notification
responseNotificationObservable,
writeCompletedObservable, // with the acknowledgement of the written batch
{ _, writeCompletedBoolean -> writeCompletedBoolean } // when both are available the next batch will be written
)
}
.build()
}
.take(1) // with this line the notification that was set above will be discarded after the long write will finish
.subscribe(
{ byteArray ->
Log.i("arrive", "data ${converter.bytesToHex(byteArray)}")
manageOtaWrite()
},
{ it.printStackTrace() }
)
}
Well, after a lot of testing, I finally develop a standalone class for the OTA update with the android BLE API, and I used it together with all my RxBle methods, I don't know if I have a hardware problem or something else, but I solve the problem, thanks a lot.