Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getSteps return empty array #60

Open
losheredos opened this issue Jun 13, 2020 · 22 comments
Open

getSteps return empty array #60

losheredos opened this issue Jun 13, 2020 · 22 comments

Comments

@losheredos
Copy link

losheredos commented Jun 13, 2020

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:

const permissions = [
          {
              kind: Fitness.PermissionKind.Step,
              access: Fitness.PermissionAccess.Read
          },
];

Can anyone help about it?

@ibraude
Copy link
Contributor

ibraude commented Jun 13, 2020

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,

@losheredos
Copy link
Author

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?

@ibraude
Copy link
Contributor

ibraude commented Jun 15, 2020

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.

@losheredos
Copy link
Author

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:
  1. In order for the library to work correctly, you'll need following SDK setups:
    Android Support Repository
    Android Support Library
    Google Play services
    Google Repository
    Google Play APK Expansion Library

Do they mean some extra package, or these are some default packages normally available in android devices?

@Francesco-Voto
Copy link
Contributor

Hi @losheredos

  1. Yes it works fine in simulator, as long as simulator has and you are using an account with some data on it.

  2. 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

@losheredos
Copy link
Author

losheredos commented Jun 23, 2020

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.

@rlems
Copy link
Contributor

rlems commented Jun 25, 2020

@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;
}

@losheredos
Copy link
Author

@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.

@rlems
Copy link
Contributor

rlems commented Jun 25, 2020

@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')

@losheredos
Copy link
Author

losheredos commented Jun 25, 2020

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.

@marlene89
Copy link

marlene89 commented Jun 30, 2020

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?

@Francesco-Voto
Copy link
Contributor

Hi @marlene89,
if no steps were recorded for the day, no data will be returned.

@losheredos
Copy link
Author

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

@rlems
Copy link
Contributor

rlems commented Jul 3, 2020

@losheredos There is a method to subscribe to steps: Fitness.subscribeToSteps()

Check the documentation for more info.

@losheredos
Copy link
Author

@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..

@marlene89
Copy link

@losheredos will def try your suggestion. I'll post results when I have them. thanks :)

@ibraude
Copy link
Contributor

ibraude commented Aug 26, 2020

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

@StarksJohn
Copy link

@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

@ibraude
Copy link
Contributor

ibraude commented Aug 27, 2020

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

@StarksJohn
Copy link

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

@GuleriaAshish
Copy link

GuleriaAshish commented Sep 2, 2020

@cham1985 can you share your code , because i am still getting the empty array.

@PaLaMuNDeR
Copy link

PaLaMuNDeR commented Sep 11, 2020

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
#48 (comment)

@ipsips
Copy link

ipsips commented Jan 5, 2021

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
}

@juliomdsneto
Copy link

juliomdsneto commented Sep 27, 2021

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

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: []

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants