OnDataPointListener does not get callback if GoogleApiClient is disconnected

913 views Asked by At

I am using Google Fit APIs for the first time. I have modified the BasicSensorsApi sample code to calculate steps count. It is working fine, but the problem is when I call GoogleApiClient.disconnect() in onStop() function and then again call GoogleApiClient.connect() in onStart() function, OnDataPointListener stops getting callbacks. I am not unregistering this listener any where.

When I don't call GoogleApiClient.disconnect() it is working fine and I get callbacks event after activity's onStop() function is called.

I am not sure whether I should disconnect GoogleApiClient in onStop() function or not. If yes then how can I solve above problem?

Here is the relavent code:

private void buildFitnessClient() {
    // Create the Google API Client
    mClient = new GoogleApiClient.Builder(this)
            .addApi(Fitness.SENSORS_API)
            .addScope(new Scope(Scopes.FITNESS_LOCATION_READ))
            .addScope(new Scope((Scopes.FITNESS_ACTIVITY_READ)))
            .addScope(new Scope((Scopes.FITNESS_BODY_READ)))
            .addConnectionCallbacks(
                    new GoogleApiClient.ConnectionCallbacks() {

                        @Override
                        public void onConnected(Bundle bundle) {
                            Log.i(TAG, "Connected!!!");
                            // Now you can make calls to the Fitness APIs.
                            // Put application specific code here.
                            findFitnessDataSources();
                        }

                        @Override
                        public void onConnectionSuspended(int i) {
                            // If your connection to the sensor gets lost at some point,
                            // you'll be able to determine the reason and react to it here.
                            if (i == GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST) {
                                Log.i(TAG, "Connection lost.  Cause: Network Lost.");
                            } else if (i == GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) {
                                Log.i(TAG, "Connection lost.  Reason: Service Disconnected");
                            }
                        }
                    }
            )
            .addOnConnectionFailedListener(
                    new GoogleApiClient.OnConnectionFailedListener() {
                        // Called whenever the API client fails to connect.
                        @Override
                        public void onConnectionFailed(ConnectionResult result) {
                            Log.i(TAG, "Connection failed. Cause: " + result.toString());
                            if (!result.hasResolution()) {
                                // Show the localized error dialog
                                GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), MainActivity.this, 0).show();
                                return;
                            }
                            // The failure has a resolution. Resolve it.
                            // Called typically when the app is not yet authorized, and an
                            // authorization dialog is displayed to the user.
                            if (!authInProgress) {
                                try {
                                    Log.i(TAG, "Attempting to resolve failed connection");
                                    authInProgress = true;
                                    result.startResolutionForResult(MainActivity.this, REQUEST_OAUTH);
                                } catch (IntentSender.SendIntentException e) {
                                    Log.e(TAG, "Exception while starting resolution activity", e);
                                }
                            }
                        }
                    }
            )
            .build();
}

@Override
protected void onStart() {
    super.onStart();
    // Connect to the Fitness API
    Log.i(TAG, "Connecting...");
    mClient.connect();
}

@Override
protected void onStop() {
    super.onStop();
    if (mClient.isConnected()) {
        //mClient.disconnect();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_OAUTH) {
        authInProgress = false;
        if (resultCode == RESULT_OK) {
            // Make sure the app is not already connected or attempting to connect
            if (!mClient.isConnecting() && !mClient.isConnected()) {
                mClient.connect();
            }
        }
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(AUTH_PENDING, authInProgress);
}

// [END auth_connection_flow_in_activity_lifecycle_methods]

/**
 * Find available data sources and attempt to register on a specific {@link DataType}.
 * If the application cares about a data type but doesn't care about the source of the data,
 * this can be skipped entirely, instead calling
 * {@link com.google.android.gms.fitness.SensorsApi
 * #register(GoogleApiClient, SensorRequest, DataSourceListener)},
 * where the {@link SensorRequest} contains the desired data type.
 */
private void findFitnessDataSources() {
    // [START find_data_sources]
    Fitness.SensorsApi.findDataSources(mClient, new DataSourcesRequest.Builder()
            // At least one datatype must be specified.
            .setDataTypes(DataType.TYPE_STEP_COUNT_CADENCE)
            .setDataTypes(DataType.TYPE_STEP_COUNT_CUMULATIVE)
            .setDataTypes(DataType.TYPE_STEP_COUNT_DELTA)
                    // Can specify whether data type is raw or derived.
            .setDataSourceTypes(DataSource.TYPE_DERIVED)
            .build())
            .setResultCallback(new ResultCallback<DataSourcesResult>() {
                @Override
                public void onResult(DataSourcesResult dataSourcesResult) {
                    Log.i(TAG, "Result: " + dataSourcesResult.getStatus().toString());
                    for (DataSource dataSource : dataSourcesResult.getDataSources()) {
                        Log.i(TAG, "Data source found: " + dataSource.toString());
                        Log.i(TAG, "Data Source type: " + dataSource.getDataType().getName());

                        //Let's register a listener to receive Activity data!
                        if (dataSource.getDataType().equals(DataType.TYPE_STEP_COUNT_CADENCE) && mListener == null) {
                            Log.i(TAG, "Data source for TYPE_STEP_COUNT_CADENCE found!  Registering.");
                            registerFitnessDataListener(dataSource, DataType.TYPE_STEP_COUNT_CADENCE);
                        } else if (dataSource.getDataType().equals(DataType.TYPE_STEP_COUNT_CUMULATIVE) && mListener == null) {
                            Log.i(TAG, "Data source for TYPE_STEP_COUNT_CUMULATIVE found!  Registering.");
                            registerFitnessDataListener(dataSource, DataType.TYPE_STEP_COUNT_CUMULATIVE);
                        } else if (dataSource.getDataType().equals(DataType.TYPE_STEP_COUNT_DELTA) && mListener == null) {
                            Log.i(TAG, "Data source for TYPE_STEP_COUNT_DELTA found!  Registering.");
                            registerFitnessDataListener(dataSource, DataType.TYPE_STEP_COUNT_DELTA);
                        }
                    }
                }
            });
    // [END find_data_sources]
}

/**
 * Register a listener with the Sensors API for the provided {@link DataSource} and
 * {@link DataType} combo.
 */
private void registerFitnessDataListener(DataSource dataSource, DataType dataType) {
    // [START register_data_listener]
    mListener = new OnDataPointListener() {
        @Override
        public void onDataPoint(DataPoint dataPoint) {
            for (Field field : dataPoint.getDataType().getFields()) {
                Value val = dataPoint.getValue(field);
                Log.i(TAG, "Detected DataPoint field: " + field.getName());
                Log.i(TAG, "Detected DataPoint value: " + val);
            }
        }
    };

    Fitness.SensorsApi.add(
            mClient,
            new SensorRequest.Builder()
                    .setDataSource(dataSource) // Optional but recommended for custom data sets.
                    .setDataType(dataType) // Can't be omitted.
                    .setSamplingRate(10, TimeUnit.SECONDS)
                    .build(),
            mListener)
            .setResultCallback(new ResultCallback<Status>() {
                @Override
                public void onResult(Status status) {
                    if (status.isSuccess()) {
                        Log.i(TAG, "Listener registered!");
                    } else {
                        Log.i(TAG, "Listener not registered.");
                    }
                }
            });
    // [END register_data_listener]
}
2

There are 2 answers

4
siva On BEST ANSWER

If you are tracking the step counts in the foreground then this is correct behaviour as you are disconnecting the google api client in onstop(). Once GoogleApiClient is disconnected all the listeners will be removed from the GoogleApiClient. But if you wish to track the step counts in the background, then you may have to move your implementation to a service and decide when exactly you want to disconnect from GoogleApiclient.

1
jkj yuio On

contrary to the documentation, you have to disconnect in onDestroy not in onStop. connect in onCreate and do nothing in onStop/onStart.

Why?

because an app waiting for onConnect to be called does not prevent onStop being called. What happens is that onStop is called before your app receives the event. then, of course, it disconnects and never gets it.