I want to fetch resting heart rate from Google fit api.
I found two solutions to the problem:
Is there a way to read daily resting heart rate from the Google Fit API?
How to get Heart Rate values from Google Fit History? but they did not help me.
Source code:
fun fetchHeartPoints() {
val account = GoogleSignIn.getLastSignedInAccount(context)
if (account != null) {
try {
val requestData = getHeartPointsRequestData()
withContext(thread.io()) {
val response = Fitness
.getHistoryClient(context, account)
.readData(requestData)
.await()
val heartRateData = response.buckets
.asSequence()
.flatMap { it.dataSets }
.flatMap { it.dataPoints }
.map { it.toHeartRateData() }
.groupBy { it.startDate }
.toList()
Logger.d("Resting heart rate data: $heartRateData")
}
} catch (e: Exception) {
Logger.e("Exception : $e")
}
}
}
private fun getHeartPointsRequestData(): DataReadRequest {
val (start, end) = getStartAndEndDateForGoogleFit()
return DataReadRequest.Builder()
.aggregate(DataType.TYPE_HEART_RATE_BPM)
.aggregate( DataType.AGGREGATE_HEART_RATE_SUMMARY)
.enableServerQueries()
.setTimeRange(start, end, TimeUnit.MILLISECONDS)
.bucketByTime(1, TimeUnit.DAYS)
.build()
}
In response I get:
[(01 Apr 2022 18:48:06, [HeartRateData(value=min: 66.0 max: 94.0 average: 74.66672, startDate=01 Apr 2022 18:48:06, endDate=01 Apr 2022 23:57:42, unit=count/min, itemId=e15e74d971391afe318fafb366ca97d3)]
But this is heart rate data, not resting heart rate.
Do you have any ideas on this issue?
Related
I am not getting weekly data from google fit (Yes, There is data in google fit which I have track using watch), but Yes I am getting today's date data.
I am attaching code snippet.
Start date = "2022-09-13T00:00:00Z"
End date = "2022-09-20T23:59:59Z"
private fun readSleepSessions(startTime : Long , endTime : Long) {
val client = Fitness.getSessionsClient(requireContext(), getGoogleAccount())
val sessionReadRequest = SessionReadRequest.Builder()
.read(DataType.TYPE_SLEEP_SEGMENT)
.includeSleepSessions()
.readSessionsFromAllApps()
.enableServerQueries()
.setTimeInterval(1663027200000, 1663718399000, TimeUnit.MILLISECONDS)
.build()
client.readSession(sessionReadRequest)
.addOnSuccessListener {
Log.d(TAG, "readSleepSessions: $sessionReadRequest")
dumpSleepSessions(it)
}
.addOnFailureListener {
Log.e("MainScreen", "Unable to read sleep sessions", it)
}
}
private fun dumpSleepSessions(response: SessionReadResponse) {
if (response.sessions.isNotEmpty()){
for (session in response.sessions) {
dumpSleepSession(session, response.getDataSet(session))
Log.d(TAG, "dumpSleepSessions: ${response.sessions}")
}
}else{
Log.d(TAG, "dumpSleepSessionsResponse: ${response.status}")
}
}
private fun dumpSleepSession(session: Session, dataSets: List<DataSet>) {
dumpSleepSessionMetadata(session)
dumpSleepDataSets(dataSets)
}
private fun dumpSleepDataSets(dataSets: List<DataSet>) {
for (dataSet in dataSets) {
for (dataPoint in dataSet.dataPoints) {
val sleepStageOrdinal = dataPoint.getValue(Field.FIELD_SLEEP_SEGMENT_TYPE).asInt()
val sleepStage = sleepTargetName[sleepStageOrdinal]
val durationMillis =
dataPoint.getEndTime(TimeUnit.MILLISECONDS) - dataPoint.getStartTime(TimeUnit.MILLISECONDS)
val duration = TimeUnit.MILLISECONDS.toMinutes(durationMillis)
Log.d(TAG, "\t$sleepStage: $duration (minutes)")
}
}
In my android app I am using workerManger to upload some data to my server every 15 minutes, and then i decided to test it on 4 devices:
1.Huawei Honor 4x (android: 5.1.1, api level: 22)
2.Samsung Galaxy A8 2016 (android: 8.0.0 , api level: 26 )
3.Samsung A30 (android: 10 , api level: 29 )
4.Realme 5 pro (android: 10 , api level: 29 )
And sadly every device had a different behavior
1.The Huawei phone: uploaded the data to the server every hour (not every 15 minutes)
2.The Samsung a8 phone: works fine
3.The Samsung a30 phone: repeatedly stops uploading for some time and then continue
4.The Realme phone: works fine for the first 10 loops of so and then stops working
All of this with the app running and I have not rebooted the phones or even killed the app, so i am feeling kind of lost and do not know how to deal with this widely different behavior, I have even tried to use Stetho (which enable chrome dev tools for your app) to look at the workManger's database but i didn't find the database.
Here is my worker
const val NETWORK_URL = "URL"
const val REQUEST_METHOD = "REQUEST_METHOD"
const val REQUEST_BODY = "REQUEST_BODY"
private const val TAG = "NetworkWorker"
class NetworkWorker(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
private lateinit var mUser: FirebaseUser
override fun doWork(): Result {
WorkManager.getInstance(applicationContext).pruneWork()
if (Firebase.auth.currentUser == null) {
Log.e(TAG, "doWork: user is null")
return Result.failure()
} else {
mUser = Firebase.auth.currentUser!!
}
val url = inputData.getString(NETWORK_URL)!!
val method = inputData.getInt(REQUEST_METHOD, Request.Method.GET)
val body = if (inputData.getString(REQUEST_BODY) == null) {
null
} else {
JSONObject(inputData.getString(REQUEST_BODY)!!)
}
Log.d(TAG, "doWork: body is $body")
val queue = Volley.newRequestQueue(applicationContext)
Log.d(TAG, "doWork: url is $url")
mUser.getIdToken(true)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val idToken = task.result!!.token
Log.d(TAG, "doWork: token is $idToken")
val request = object : JsonObjectRequest(method, url, body,
Response.Listener { response ->
Log.d(TAG, "doWork: $response")
},
Response.ErrorListener { error ->
Log.e(TAG, "doWork: error is", error)
}) {
override fun getHeaders(): MutableMap<String, String> {
val params = HashMap<String, String>()
params["Content-Type"] = "application/json"
params["Authorization"] = "$idToken"
return params
}
}
queue.add(request)
} else {
Log.e(
TAG,
"doWork: error getting the token",
task.exception
)
}
}
return Result.success()
}
}
and here is how I enqueue it in my activity
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val uploadLocationWork = PeriodicWorkRequest
.Builder(NetworkWorker::class.java, 15, TimeUnit.MINUTES)
.setInputData(
workDataOf(
NETWORK_URL to "http://10.1.0.11:8000/api/location",
REQUEST_BODY to json,
REQUEST_METHOD to Request.Method.POST))
.setConstraints(constraints)
.build()
WorkManager
.getInstance(this)
.enqueueUniquePeriodicWork(
"testWorkManger",
ExistingPeriodicWorkPolicy.KEEP,
uploadLocationWork)
I want to consume twitter streaming api in android.
I've used kotlin coroutines and retrofit.
Somehow in the third request i get an HTTP 420 ERROR (Enhance your calm)
I cannot understand why this happens. I am using kotlin coroutines.
Here's my code:
fun getStreamData(str: String) {
Log.d("debug", "Fetching data..")
coroutineScope.launch {
withContext(Dispatchers.Main) {
//Display loading animation in UI
_status.value = DataApiStatus.LOADING
}
try {
val listResult = ApiService().api!!.getTweetList(str).await()
while (!listResult.source().exhausted()) {
val reader = JsonReader(InputStreamReader(listResult.byteStream()))
// https://stackoverflow.com/questions/11484353/gson-throws-malformedjsonexception
reader.setLenient(true);
val gson = GsonBuilder().create()
val j = gson.fromJson<JsonObject>(reader, JsonObject::class.java)
Log.d("debug", "JSON: " + j.toString())
if (j.get("text") != null && j.getAsJsonObject("user").get("profile_image_url_https") != null && j.getAsJsonObject("user").get("name") != null){
val t = gson.fromJson<Tweet>(j, Tweet::class.java)
withContext(Dispatchers.Main) {
_status.value = DataApiStatus.DONE
// https://stackoverflow.com/questions/47941537/notify-observer-when-item-is-added-to-list-of-livedata
tweetsList.add(t)
_tweetsList.value = tweetsList
}
}
}
}
catch (e : JsonSyntaxException) {
Log.e("error", "JsonSyntaxException ${e.message}");
}
catch (e: Exception) {
Log.e("error", "ERROR ${e.message}")
}
}
}
This function is responsible to search the stream accordingly to str string which is a parameter.
Also, when the search parameter changes i cancel the current job and relaunch a new one with the actual search parameter.
fun cancelJob(){
Log.d("debug", "Cancelling current Job!")
coroutineScope.coroutineContext.cancelChildren()
}
What am i doing wrong? In the third request i get an HTTP 420 ERROR.
Here's the full code:
https://github.com/maiamiguel/RHO-Challenge
The 420 Enhance Your Calm status code is an unofficial extension by Twitter. Twitter used this to tell HTTP clients that they were being rate limited. Rate limiting means putting restrictions on the total number of requests a client may do within a time period.
I'm developing a step counter with Google Fit.
I tried to implement as it is defined in Google Fit docs. But the problem is that when I query step count it always returns empty dataset but when I query with readDailyTotal function it returns a dataset.
I am not able to find what is the cause.
I get the required permission for required step count permission
val fitnessOptions = FitnessOptions.builder()
.addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE, FitnessOptions.ACCESS_READ)
.addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.build()
if (! GoogleSignIn.hasPermissions(GoogleSignIn.getLastSignedInAccount(activity),
fitnessOptions)) {
GoogleSignIn.requestPermissions(
activity, // your activity
REQUEST_CODE_GOOGLE_FIT_PERMISSIONS,
GoogleSignIn.getLastSignedInAccount(activity),
fitnessOptions
)
} else {
onSuccess.invoke()
}
I subscribe to the application for recording step counts.
Fitness.getRecordingClient(context, client!!)
.subscribe(DataType.TYPE_STEP_COUNT_DELTA)
.addOnSuccessListener {
onSuccess.invoke()
}
.addOnFailureListener { e ->
onFail.invoke(e)
}
I query 1 week period with history API but it always returns an empty dataset.
// Setting a start and end date using a range of 1 week before this moment.
val cal = Calendar.getInstance()
val now = Date()
cal.time = now
val endTime = cal.timeInMillis
cal.add(Calendar.WEEK_OF_YEAR, -1)
val startTime = cal.timeInMillis
val dateFormat = DateFormat.getDateInstance()
Log.i(TAG, "Range Start: " + dateFormat.format(startTime))
Log.i(TAG, "Range End: " + dateFormat.format(endTime))
val readRequest = DataReadRequest.Builder()
// The data request can specify multiple data types to return, effectively
// combining multiple data queries into one call.
// In this example, it's very unlikely that the request is for several hundred
// datapoints each consisting of a few steps and a timestamp. The more likely
// scenario is wanting to see how many steps were walked per day, for 7 days.
.aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA)
// Analogous to a "Group By" in SQL, defines how data should be aggregated.
// bucketByTime allows for a time span, whereas bucketBySession would allow
// bucketing by "sessions", which would need to be defined in code.
.bucketByTime(1, TimeUnit.DAYS)
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.enableServerQueries()
.build()
Fitness.getHistoryClient(context, client)
.readData(readRequest)
.addOnSuccessListener { dataReadResponse ->
dumpDataSets(dataReadResponse.dataSets)
onSuccess.invoke(dataReadResponse)
}
.addOnFailureListener { e ->
onFail.invoke(e)
}
.addOnCompleteListener { task ->
dumpDataSets(task.result!!.dataSets)
onComplete.invoke(task)
}
Datasets can be found in buckets:
dataReadResponse.buckets.forEach { bucket ->
bucket.dataSet.forEach { dataSet ->
dataSet.dataPoints.forEach { dataPoint ->
// USE DataPoint
}
}
}
This code get calo in day:
val cal = Calendar.getInstance()
cal.time = Date()
val endTime = cal.timeInMillis
cal.add(Calendar.DAY_OF_YEAR, -1)
val startTime = cal.timeInMillis
val readRequest = DataReadRequest.Builder()
.aggregate(DataType.TYPE_CALORIES_EXPENDED, DataType.AGGREGATE_CALORIES_EXPENDED)
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.bucketByTime(1, TimeUnit.DAYS)
.build()
Fitness.getHistoryClient(this, GoogleSignIn.getLastSignedInAccount(this))
.readData(readRequest)
.addOnSuccessListener { dataReadResponse ->
// For the sake of the sample, we'll print the data so we can see what we just
// added. In general, logging fitness information should be avoided for privacy
// reasons.
printData(dataReadResponse)
}
.addOnFailureListener { e -> Log.e(TAG, "onFailure()", e) }
.addOnCompleteListener(OnCompleteListener {
Log.d(TAG, "onComplete()")
})
Value return:Field Calories Value= 11332.985.
But in Google Fit App display value = 6.164 Cal.
Why value of CALORIES_EXPENDED of API not equal value of Google Fit App?