Open losheredos opened 4 years ago
Hey,
the docs' usage example is as follows:
import Fitness from '@ovalmoney/react-native-fitness';
const permissions = [
{ kind: Fitness.PermissionKind.Step, access: Fitness.PermissionAccess.Write },
];
Fitness.isAuthorized(permissions)
.then((authorized) => {
// Do something
})
.catch((error) => {
// Do something
});
Notice that the permissions access is Fitness.PermissionAccess.Write
while in your example it is Fitness.PermissionAccess.Read
.
This may be an issue as the device can't write new activity to Google Fit / Healthkit.
Try changing the permissions:
const permissions = [
{ kind: Fitness.PermissionKind.Step, access: Fitness.PermissionAccess.Write },
{ kind: Fitness.PermissionKind.Step, access: Fitness.PermissionAccess.Read}
];
If not, can you share more of your code?
Cheers,
Actually I have changed my code to your example after I shared, I think it did not change but I will check more and let you know.
By the way I thought that this APIs just get data from phone and let us get data through this package but as you mentioned, it only saves data after we let permission to write? Did I understand it right? Also in this case should we let app stay open to be able to write this data?
No, there is no need to keep the app open. And yes, this package uses the Google Fit or Apple Healthkit APIs behind the scenes based on the platform your React Native app is currently running on.
Thanks for your answers. After few days of trying actually IOS started to return some response from the API but android is still same. Here is some questions.
1) Does this package should actually work fine in simulators? If yes then we can just debug/test it normally? 2) I checked react-native-google-fit package and there its written that:
Do they mean some extra package, or these are some default packages normally available in android devices?
Hi @losheredos
Yes it works fine in simulator, as long as simulator has and you are using an account with some data on it.
Th simulator should contains Google Play Services in order to make this work property. I don't think the other packages you mentions are necessary
Still same for android, I mean what I can do I dont know. Here is the full code example which I use:
import Fitness from '@ovalmoney/react-native-fitness';
export async function generateWeeklyAnalysis() {
let datesArray = [
{day: 'Pzt', distance: 0},
{day: 'Sal', distance: 0},
{day: 'Çar', distance: 0},
{day: 'Per', distance: 0},
{day: 'Cum', distance: 0},
{day: 'Cmt', distance: 0},
{day: 'Paz', distance: 0}
];
let totalDistance = 0;
let today = new Date().getDay();
for (let i = 0; i < today; i++){
let initialDate = new Date(Date.now() - 86400000*(today-i));
let startDate = new Date(initialDate.getFullYear(), initialDate.getMonth(), initialDate.getDate(), 0, 0, 0);
let endDate = new Date(initialDate.getFullYear(), initialDate.getMonth(), initialDate.getDate(), 23, 59, 59);
let res = await getSteps(startDate, endDate, 'hour');
let distance = res.length === 0 ? getRandomInt(6) : res;
datesArray[i].distance = distance;
totalDistance += distance*1000*2;
}
return {
datesArray,
totalDistance
};
}
function getSteps(startDate, endDate, hour) {
return Fitness.getSteps({startDate, endDate, hour});
}
function getRandomInt(max) {
let integer = Math.floor(Math.random() * max) + 1;
let double = (integer*.25);
return (integer + double).toFixed(2);
}
I tried date with different formats but nothing changes. Am I wrong in something? By the way thanks for your quick answers to help, I appricate it mate.
@losheredos Just a side note: in your function getSteps
you're passing a parameter named hour
to Fitness.getSteps
, this should be named interval
.
And this is my implementation of getting the steps of today and the previous 6 days. I find that using momentjs makes working with dates a lot easier.
const getWeekDaysArray = function() {
const day = moment().startOf('day');
const arr = [moment(day)];
for (let i = 0; i <= 5; i++) {
arr.push(moment(day.subtract(1, 'days')));
}
return arr;
};
async function getSteps(startDate, endDate, interval = 'days') {
return await Fitness.getSteps({ startDate, endDate, interval });
}
async function getStepsForWeek() {
const daysOfWeek = getWeekDaysArray();
const startDate = daysOfWeek[daysOfWeek.length - 1].toISOString();
const endDate = moment(daysOfWeek[0]).endOf('day').toISOString();
const res = await getSteps(startDate, endDate);
const newSteps = daysOfWeek.map(date => {
const resultForDayOfWeek = res.find(resultDay => {
const resultDate = moment(resultDay.startDate).startOf('day');
return resultDate.valueOf() === date.startOf('day').valueOf();
});
return {
date,
quantity: resultForDayOfWeek ? resultForDayOfWeek.quantity : 0,
};
});
return newSteps;
}
@rlems I think you read it wrong maybe, because its written in documentation
Set interval to decide how detailed the returned data is, set it to hour or minute otherwise it defaults to days.
If I'm not wrong.
@losheredos You've got these 2 parts in your code:
let res = await getSteps(startDate, endDate, 'hour');
function getSteps(startDate, endDate, hour) {
return Fitness.getSteps({startDate, endDate, hour});
}
This object is wrong: {startDate, endDate, hour}
because of the hour param. Should be {startDate, endDate, interval: hour}
The docs give this example:
Fitness.getSteps({ startDate: string, endDate: string, interval: string })
interval
parameter is of type string ('hour' or 'days')
Ah yeah got it, right. But anyways I added this interval option later to check if it will make difference in results. It didn't make any difference. I will even check again and write here.
Edit: Yeah checked again and results are same.
Having the same issue. Getting empty array on emulator and real device. here is my implementation.
let currentDate = new Date();
let end = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate(), 23, 59, 59);
let start = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate(), 0, 0, 0);
let formattedStartDate = start.toString();
let formattedEndDate = end.toString();
let stepsResult = await Fitness.getSteps({
startDate: formattedStartDate,
endDate: formattedEndDate,
interval: 'days',
});
Am I missing something? Shouldn't it at least return 0 if no steps were recorded for the day?
Hi @marlene89, if no steps were recorded for the day, no data will be returned.
So there was no data then I decided to try react-native-google-fit . There I could at least get array with source data and steps arrays(which was empty as well). But noticed that in that package there is method to start recording. After I started that method data was recorded and I could get steps of the same day. So maybe in this package there should be a method to record (or it does automatically and there is issue about it?) for android side of it.
You may try the same steps as I did and check if you will be able to get something more @marlene89
@losheredos There is a method to subscribe to steps: Fitness.subscribeToSteps()
Check the documentation for more info.
@rlems yeah thanks for warning I missed it, but if this was the case for this issue probably this method could be in top section of methods..
@losheredos will def try your suggestion. I'll post results when I have them. thanks :)
Hey, thought this might be related.
So to be able to record steps\activity on Google Fit you must call the Fitness.subscribeToSteps()
or Fitness.subscribeToActivity()
.
if these methods return false (failed to subscribe) it probably means the app doesn't have the required permissions, because as of Android 10 I think, physical activity is defined as a sensitive permission and requires Android permissions to be granted. (see https://developers.google.com/fit/android/authorization#requesting_android_permissions )
To solve this, use react-native-permissions
to request for the ACTIVITY_RECOGNITION
permission.
It can be used like this:
import {check, request, PERMISSIONS, RESULTS} from 'react-native-permissions';
import Fitness from '@ovalmoney/react-native-fitness';
const requestActivityPermission = async () => {
try {
const result = await check(PERMISSIONS.ANDROID.ACTIVITY_RECOGNITION)
switch (result) {
case RESULTS.UNAVAILABLE:
console.log(
'This feature is not available (on this device / in this context)',
);
break;
case RESULTS.DENIED:
console.log(
'The permission has not been requested / is denied but requestable',
);
await request(PERMISSIONS.ANDROID.ACTIVITY_RECOGNITION)
break;
case RESULTS.GRANTED:
console.log('The permission is granted');
break;
case RESULTS.BLOCKED:
console.log('The permission is denied and not requestable anymore');
break;
}
} catch (err) {
console.warn(err);
}
};
// wherever you subscribe to steps or activity, add requestActivityPermission() if the method returns false
const handleSubscribe = async () => {
try{
const subscribedToSteps = await Fitness.subscribeToSteps()
if(subscribedToSteps){
console.log("subscribed to step counter")
} else {
await requestActivityPermission()
}
} catch (e) {
console.log(e)
}
}
I'll try and open a PR to add this functionality.
Hope this helps in the meantime
@ibraude , Hello, I used the Fitness.subscribeToSteps() method when the app started,and I agree to step authorization 。 Then I called the Fitness.isAuthorized(permissions) method ,it returns false, then I called Fitness.requestPermissions(permissions) method ,but still return false , please help me ,thanks
Hey, Did you set up an OAuth 2.0 Client ID with the Google API console? See how to do that here: https://developers.google.com/fit/android/get-api-key
Haha, it's my problem. The debug package I used for testing but it should be the release package. Now I use the Fitness.subscribeToActivity() method to determine whether it is authorized, and then use the react-native-permissions library to request the two permissions of ACTIVITY_RECOGNITION and ACCESS_FINE_LOCATION. Now I have the user’s Steps, Calories and Distance, thank you
@cham1985 can you share your code , because i am still getting the empty array.
Any progress on this @GuleriaAshish ? @cham1985 could you share your setup? For me it also doesn't work on Android. For some reason it shows only the first screen to choose your account for the permissions, but not the actual permissions questions. I gave more details in this thread https://github.com/OvalMoney/react-native-fitness/issues/48#issuecomment-688761194
Ugh, never mind right after posting this it started working :D Seems like there's quite a delay in data syncing
I'm having the same unexpected result: getSteps()
yields an empty array. I'm trying to fetch tracked steps on Android Emulator without having Google Fit app installed. I am leveraging subscribeToSteps()
method as documentation suggests. I am certain that I have some steps tracked in given period because I can see them on Google Fit app that I have installed on my physical phone and I can confirm that permissions are successfully granted since my emulated app appears in Google Fit settings under "Third-party apps with account access".
I could swear it did work when I last worked on the project couple a weeks ago.. Did Google change something on their end? @Francesco-Voto
This is my control flow (simplified):
async function myFitness() {
const permissions = [
{
kind: Fitness.PermissionKinds.Steps,
access: Fitness.PermissionAccesses.Read,
},
];
const period = {
startDate: '2020-12-29T22:00:00.000Z',
endDate: '2021-01-05T21:59:59.999Z',
interval: 'days'
};
let isAuthorized = await Fitness.isAuthorized(permissions);
if (!isAuthorized) {
isAuthorized = await Fitness.requestPermissions(permissions);
if (!isAuthorized) {
return;
}
}
isAuthorized = await Fitness.subscribeToSteps();
if (!isAuthorized) {
return;
}
const trackedSteps = await Fitness.getSteps(period);
console.log(trackedSteps); // logs empty array
}
Hey, thought this might be related. So to be able to record steps\activity on Google Fit you must call the
Fitness.subscribeToSteps()
orFitness.subscribeToActivity()
.if these methods return false (failed to subscribe) it probably means the app doesn't have the required permissions, because as of Android 10 I think, physical activity is defined as a sensitive permission and requires Android permissions to be granted. (see https://developers.google.com/fit/android/authorization#requesting_android_permissions )
To solve this, use
react-native-permissions
to request for theACTIVITY_RECOGNITION
permission.It can be used like this:
import {check, request, PERMISSIONS, RESULTS} from 'react-native-permissions'; import Fitness from '@ovalmoney/react-native-fitness'; const requestActivityPermission = async () => { try { const result = await check(PERMISSIONS.ANDROID.ACTIVITY_RECOGNITION) switch (result) { case RESULTS.UNAVAILABLE: console.log( 'This feature is not available (on this device / in this context)', ); break; case RESULTS.DENIED: console.log( 'The permission has not been requested / is denied but requestable', ); await request(PERMISSIONS.ANDROID.ACTIVITY_RECOGNITION) break; case RESULTS.GRANTED: console.log('The permission is granted'); break; case RESULTS.BLOCKED: console.log('The permission is denied and not requestable anymore'); break; } } catch (err) { console.warn(err); } }; // wherever you subscribe to steps or activity, add requestActivityPermission() if the method returns false const handleSubscribe = async () => { try{ const subscribedToSteps = await Fitness.subscribeToSteps() if(subscribedToSteps){ console.log("subscribed to step counter") } else { await requestActivityPermission() } } catch (e) { console.log(e) } }
I'll try and open a PR to add this functionality.
Hope this helps in the meantime
but, if i get true from these methods I should be able to get some data, right? But i still get empty array
here is my code
[...]
`export const StepCountPage = () => {
const permission = [
{ kind: 0, access: 0 },
{ kind: 2, access: 0 },
{ kind: 0, access: 1 },
{ kind: 1, access: 0 },
{ kind: 1, access: 1 },
{ kind: 4, access: 0 },
{ kind: 4, access: 1 },
{ kind: 2, access: 1 }
];
const period = {
startDate: '2017-07-17T00:00:17.971Z',
endDate: '2021-01-05T21:59:59.999Z',
Interval: 'day'
};
useEffect(() => {
console.log("permissions: ", permission)
Fitness.requestPermissions(permission)
.then((authorized) => {
console.log("status? ", authorized)
if (authorized) {
howManySteps();
} else {
Fitness.requestPermissions(permission).
then((authorized) => {
howManySteps();
console.log("status?", authorized)
})
.catch((error) => {
console.log("denied", error)
})
}
})
.catch((error) => {
// Do something
});
}, []);
const howManySteps = async () => {
const subSteps = await Fitness.subscribeToSteps()
console.log("subscribeToSteps:", subSteps)
const steps = await Fitness.getSteps(period)
console.log("steps: ", steps)
};
[...]
}`
here is my output:
LOG permissions: [{"access": 0, "kind": 0}, {"access": 0, "kind": 2}, {"access": 1, "kind": 0}, {"access": 0, "kind": 1}, {"access": 1, "kind": 1}, {"access": 0, "kind": 4}, {"access": 1, "kind": 4}, {"access": 1, "kind": 2}] LOG status? true LOG subscribeToSteps: true LOG steps: []
As @ibraude mentioned before I still get the empty array. It works on both android & IOS like authorizaiton etc. but when I get results of steps its just empty array. I thought it could be cuz of simulator but I test it in real device now, looks same..
Permissions:
Can anyone help about it?