StasDoskalenko/react-native-google-fit

getActivitySamples return empty array

Closed this issue ยท 14 comments

Hi and thank you for the great package.
I have noticed that getActivitySamples() is returning and empty array also if I add manually some data to GoogleFit.
I am using the version 0.16.1 of this package and I am asking the following scopes:
scopes: [Scopes.FITNESS_ACTIVITY_READ, Scopes.FITNESS_LOCATION_READ]

I am checking if authorized, and it is:
GoogleFit.authorize(...)

but when I do a console.log of the res data it is always []

For me the app is crashing when it reaches the getActivitySamples function in android

componentDidMount() {
        const options = {
            scopes: [
                Scopes.FITNESS_ACTIVITY_READ,
                Scopes.FITNESS_ACTIVITY_READ_WRITE,
                Scopes.FITNESS_BODY_READ,
                Scopes.FITNESS_BODY_READ_WRITE,
            ],
        }
        GoogleFit.authorize(options)
            .then((res) => {
                console.log('authorized >>>' + JSON.stringify(res))
                const options1 = {
                    startDate: "2017-01-01T00:00:17.971Z", // required ISO8601Timestamp
                    endDate: new Date().toISOString() // required ISO8601Timestamp
                };
                let opt = {
                    startDate: "2017-01-01T00:00:17.971Z", // required
                    endDate: new Date().toISOString(), // required
                    bucketUnit: "DAY", // optional - default "DAY". Valid values: "NANOSECOND" | "MICROSECOND" | "MILLISECOND" | "SECOND" | "MINUTE" | "HOUR" | "DAY"
                    bucketInterval: 1, // optional - default 1. 
                };
                try {
                    GoogleFit.getActivitySamples(opt)
                        .then((res) => {
                            console.log(result)
                        }).catch((error) => {
                            console.log(error.message)
                        })
                } catch (error) {
                    console.log(error.message)
                }

            })
            .catch((error) => {
                console.log(error.message)
            })

    }

    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>
                    Welcome to React Native!
                </Text>
                <Text style={styles.instructions}>
                    To get started, edit index.js
                </Text>
                <Text style={styles.instructions}>
                    Double tap R on your keyboard to reload,{'\n'}
                    Shake or press menu button for dev menu
                </Text>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
    },
    instructions: {
        textAlign: 'center',
        color: '#333333',
        marginBottom: 5,
    },
});

Getting the same result here.

Hi @IronTony

I faced the issue and I resolved it by changing bucketUnit to MINUTE

let opt = {
   startDate: "2017-01-01T00:00:17.971Z", // required
   endDate: new Date().toISOString(), // required
   bucketUnit: "MINUTE", // optional - default "DAY". Valid values: "NANOSECOND" | "MICROSECOND" | "MILLISECOND" | "SECOND" | "MINUTE" | "HOUR" | "DAY"
   bucketInterval: 15, // optional - default 1. 
 };

I am getting empty arrray for getActivity , getDistance, getSleep and Heart

const distance = await GoogleFit.getDailyDistanceSamples(opt);
const floors = await GoogleFit.getActivitySamples(opt);
const heartrateaverage = await GoogleFit.getHeartRateSamples(opt);
const sleep = await GoogleFit.getSleepSamples(opt);

let opt = {
startDate: '2021-02-12}00:00:17.971Z', // required
endDate: new Date().toISOString(), // required
bucketUnit: 'DAY', // optional - default "DAY". Valid values: "NANOSECOND" | "MICROSECOND" | "MILLISECOND" | "SECOND" | "MINUTE" | "HOUR" | "DAY"
bucketInterval: 15, // optional - default 1.
};

Same here, and the weird thing is it only happens ever since we changed our Cloud Console Project. Everything works, sleep data, steps data, but activities always come back empty.

I had the same issue and noticed that there was an error in logs about token expiration. However, authorize() and checkIsAuthorized() never returned error. Once I used disconnect() and re-authorized() my app, everything worked.

I had the same issue, when using bucketUnit: "DAY" and bucketInterval: 1. I am almost certain the reason for this behavior is that the native function .bucketByActivitySegment(1, TimeUnit.DAYS) is used. This returns all ActivitySegments with a duration of more than a day. If you use bucketUnit: "MINUTES" and bucketInterval: 15 it returns all ActivitySegments with a duration longer than 15min.

Hope someone can confirm my findings

Hello all. I figured this one out.

getActivitySamples has this line:

.aggregate(DataType.TYPE_DISTANCE_DELTA, DataType.AGGREGATE_DISTANCE_DELTA) (https://github.com/StasDoskalenko/react-native-google-fit/blob/fbcf2c70042150bdea8[โ€ฆ]id/src/main/java/com/reactnative/googlefit/ActivityHistory.java)

DataType.TYPE_DISTANCE_DELTA requires ACCESS_FINE_LOCATION and in API level 29, ACTIVITY_RECOGNITION (https://developers.google.com/android/reference/com/google/android/gms/fitness/data/DataType#TYPE_DISTANCE_DELTA)

One would add these in AndroidManifest and request user permissions at runtime because these are considered dangerous (https://reactnative.dev/docs/permissionsandroid).

However, ACTIVITY_RECOGNITION is not yet supported by RN.

This PR to support it is not yet merged and released: https://github.com/facebook/react-native/pull/28244/files

Therefore, getActivitySamples does not function as expected for some configurations.

Solutions:

  • Patch RN with that PR
  • Downgrade to API level 28
  • Disable that line

I will comment again if I create PR to help with this.

I'm atleast alittle wrong about the above. Downgrading the API level to 28 does not work. Will report back if I can find a solution.

Ok solution:

  • use Scopes.FITNESS_LOCATION_READ
  • add <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

@peter-chau I just take a look at the activity code, the implementation includes distance type in the past without problem. But later Google changes the location info into sensitive/dangerous scopes. So it starts to require permission and cause problem.

In fact, I realized that we can request the permission silently from the native side / java code depending on the datatype even without notifying the users and developers.
My preference always inclines to Devs should understand how the code works behinds the scene, and user can make their own choices regardless their awareness, So I decided not to make auto request permission, instead, make hard requirement to add scope and permission by developer themselves though it's indeed inconvenient. But it can always be changed later.

Your solution is added to doc now to avoid further confusion for the future.
c665081

Hey, I've got the same issue with empty array. The solution for this is to ask for permission android.permission.ACTIVITY_RECOGNITION and android.permission.ACCESS_FINE_LOCATION. I hope this helps! ;)

Hi I tried all what is listed here but still empty on my end

For anyone using getActivitySamples() in 2022, I can confirm the method works for me. Here is my working configuration.

Thanks to @szymonrybczak for the android.permission.ACCESS_FINE_LOCATION and android.permission.ACTIVITY_RECOGNITION suggestions, I implemented them both in AndroidManifest.xml. I do not have the time at this very minute to run another build, but I would like to test again to confirm if ACTIVITY_RECOGNITION is truly required.

Thanks to @peter-chau I added the scope of Scopes.FITNESS_LOCATION_READ

Thanks to @YahiaJabeur I adjusted bucketInterval to 15 and bucketUnit to "SECOND" (which just seems more accurate for apps today).

Don't forget to rebuild the app. I relaunched the app and had to re-authorize permissions, and boom it worked!