Unable to get the step count using google fit android - android

I am working on step count implementation and for that i am using the google fit api of android.
Below is my code.
public class GoogleFitDemoActivity extends AppCompatActivity {
public static final String TAG = "BasicSensorsApi";
private GoogleApiClient mClient = null;
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
private OnDataPointListener mListener;
private TextView stepsCountTextView;
ProgressDialog progressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.google_fit_main);
progressDialog = new ProgressDialog(this);
if (!checkPermissions()) {
requestPermissions();
Debug.displayToast(this, "Requesting for permisstion ");
}
stepsCountTextView = (TextView) findViewById(R.id.sample_logview);
}
/**
* Return the current state of the permissions needed.
*/
private boolean checkPermissions() {
int permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
/**
* Unregister the listener with the Sensors API.
*/
private void unregisterFitnessDataListener() {
if (mListener == null) {
return;
}
Fitness.SensorsApi.remove(
mClient,
mListener)
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
Debug.print(TAG, "Listener was removed!");
} else {
Debug.print(TAG, "Listener was not removed.");
}
}
});
}
private void buildFitnessClient() {
if (progressDialog == null) {
return;
}
progressDialog.setMessage("Wait.....");
progressDialog.show();
if (mClient == null && checkPermissions()) {
mClient = new GoogleApiClient.Builder(this)
.addApi(Fitness.SENSORS_API)
.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE))
.addConnectionCallbacks(
new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
runOnUiThread(new Runnable() {
#Override
public void run() {
progressDialog.setMessage("Client Connected waiting for fit register");
}
});
Debug.print(TAG, "Connected!!!");
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) {
Debug.print(TAG, "Connection lost. Cause: Network Lost.");
} else if (i
== GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) {
Debug.print(TAG,
"Connection lost. Reason: Service Disconnected");
}
}
}
)
.enableAutoManage(this, 0, new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult result) {
Debug.print(TAG, "Google Play services connection failed. Cause: " +
result.toString());
Snackbar.make(
GoogleFitDemoActivity.this.findViewById(R.id.main_activity_view),
"Exception while connecting to Google Play services: " +
result.getErrorMessage(),
Snackbar.LENGTH_INDEFINITE).show();
}
})
.build();
}
}
private void findFitnessDataSources() {
Fitness.SensorsApi.findDataSources(mClient, new DataSourcesRequest.Builder()
.setDataTypes(DataType.TYPE_STEP_COUNT_CUMULATIVE)
.setDataSourceTypes(DataSource.TYPE_DERIVED)
.build())
.setResultCallback(new ResultCallback<DataSourcesResult>() {
#Override
public void onResult(DataSourcesResult dataSourcesResult) {
progressDialog.dismiss();
Debug.print(TAG, "Result: " + dataSourcesResult.getStatus().toString());
for (DataSource dataSource : dataSourcesResult.getDataSources()) {
Debug.print(TAG, "Data source found: " + dataSource.toString());
Debug.print(TAG, DataType.TYPE_STEP_COUNT_CUMULATIVE + " Data Source type: " + dataSource.getDataType().getName());
//Let's register a listener to receive Activity data!
if (dataSource.getDataType().equals(DataType.TYPE_STEP_COUNT_CUMULATIVE)
&& mListener == null) {
Debug.print(TAG, "Data source for LOCATION_SAMPLE found! Registering.");
registerFitnessDataListener(dataSource,
DataType.TYPE_STEP_COUNT_CUMULATIVE);
}
}
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
}
#Override
protected void onResume() {
super.onResume();
buildFitnessClient();
}
private void registerFitnessDataListener(DataSource dataSource, DataType dataType) {
mListener = new OnDataPointListener() {
#Override
public void onDataPoint(DataPoint dataPoint) {
for (Field field : dataPoint.getDataType().getFields()) {
final Value val = dataPoint.getValue(field);
runOnUiThread(new Runnable() {
#Override
public void run() {
stepsCountTextView.setText("Steps count is " + val);
}
});
Debug.print(TAG, "Detected DataPoint field: " + field.getName());
Debug.print(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(1, TimeUnit.SECONDS)
.build(),
mListener)
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
Debug.print(TAG, "Listener registered!");
} else {
Debug.print(TAG, "Listener not registered.");
}
}
});
}
private void requestPermissions() {
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION);
if (shouldProvideRationale) {
Debug.print(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
findViewById(R.id.main_activity_view),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(GoogleFitDemoActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Debug.print(TAG, "Requesting permission");
ActivityCompat.requestPermissions(GoogleFitDemoActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
Debug.print(TAG, "onRequestPermissionResult");
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.length <= 0) {
Debug.print(TAG, "User interaction was cancelled.");
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
buildFitnessClient();
} else {
Snackbar.make(
findViewById(R.id.main_activity_view),
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.settings, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Build intent that displays the App settings screen.
Intent intent = new Intent();
intent.setAction(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
})
.show();
}
}
}
}
Below are issue in this code which i am facing.
(Let suppose I have started walking and steps count is 10 and I kill my all and again start app once my onConnected() is called I register for getting data point and I get the dataPoint value 10 while it should give 0 to start from first
1 The step value does not reset to 0 and continues to increment.
2 Every-time I run the code the steps value doesn't reset to 0, so it is taking in some past values.
3 Not getting the steps count update as frequently it is taking time to update.(when i walk for 5 min then it gives first step count)
4 Even after killing app and again relaunching app get the previous steps count,steps count not start form 0 start from old.(As I am not storing any values in preference or database. How I am getting the previous step count like 10)
Please see the result log
Detected DataPoint value: 1 >>>>>>>>>> at this it should be start from 0
Detected DataPoint field: steps
Detected DataPoint value: 1
Detected DataPoint field: steps
Detected DataPoint value: 6
Detected DataPoint field: steps
Detected DataPoint value: 6
Detected DataPoint field: steps
Detected DataPoint value: 7
Detected DataPoint field: steps
Detected DataPoint value: 7
Detected DataPoint field: steps
Detected DataPoint value: 2
Please help where i am making the mistake.

There are 2 ways to get step value from Google Fit through their API, I think:
1) History API: we only need subscribe with GG Fit what kind of value we want to get and GG Fit will save info by itself (somehow it can detect your step by utilize sensor, machine learning,... (not by GPS - you can find out it easily on Google)). Then, if we want to get value what GG Fit saved, just revoke History API with a time range.
2) Sensor API: we will use Sensor API with some fantastic algorithm to communicate with our own sensor in device to get directly step value (not saving by GG Fit on their server and getting it later). However, this method will use temporary memory of sensor in your device to save step value; and this value keeps increasing, only if your device is rebooted, then it's reset :( => So, you can do some small trick to get the real value.
(For example, save first value, then keep subtracting for the later. Suppose at first we got 1990, then saved it, then got 1995, then subtract it 1995 - 1190 = 5 steps) However, I don't know what if the value saved in sensor's memory becomes too big, and overflows. :(
** My experience: shouldn't use type DELTA. It could bring up a wrong value due to the time range; For example, from 17:30:00 to 17:31:00 it has value step 15, but if we revoke time range from 17:30:00 to 17:30:10 it has no value and time range from 17:30:10 to 17:31:00, it has value step 10. And sometime, it miss value when your screen device is sleeping, GG Fit will invoke their API underground but not recording time. More over, sometime I got negative values ##. God knows what they're are doing!
Finally, I think GG Fit app use some trick somehow to display the step value in real time (maybe sensor API, and most of time, I use Sensor API with step accumulate (not delta) which gives the best result similar to GG Fit app). Then, when I reactive app, awake device's screen (if you develop in Android, it means your activity calls onResume() method), I revoke History API with time range from the beginning to the current time I rewake my app and get the latest value.
https://developers.google.com/fit/android --> this link to show you what kind of API GG Fit support for Android.
https://developer.android.com/reference/android/hardware/Sensor.html#TYPE_STEP_COUNTER --> this link is the description for TYPE_STEP from sensor, it's said it will keep increasing until the device reboot.

Related

getBeaconState() returns null, no matter what type of attachment I add to my beacon

Setting my attachment details through Beacon dashboard. I keep getting: getBeaconState("FILTER") null. My goal is to just detect when a beacon is nearby for now. Any one form help me would be great!
I have already set up my virtual beacon as per eddystone format and it works perfectly (in beacon dashboard I see: The beacon is active and works correctly!) And have obviously set up my project on Google console and added same API keys to Advertiser and this(scanner) app.
public class MainActivity extends AppCompatActivity {
public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
public static final String TAG = MainActivity.class.getSimpleName();
List<BeaconState.TypeFilter> BEACON_TYPE_FILTERS = Arrays.asList(BeaconState.TypeFilter
.with("alpine-shade-255114", "int"));
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
hasLocationPermission();
addBeaconDetectorFence();
}
private void hasLocationPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION);
return;
}
}
#RequiresApi(api = Build.VERSION_CODES.M)
private void addBeaconDetectorFence() {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION);
return;
}
Awareness.getSnapshotClient(this).getBeaconState(BEACON_TYPE_FILTERS)
.addOnSuccessListener(beaconResult -> {
Log.e(TAG, "Got beacon state");
if(beaconResult!=null){
BeaconState beaconState = beaconResult.getBeaconState();
Toast.makeText(MainActivity.this, "Got beacon state! " + beaconState,
Toast.LENGTH_SHORT).show();}
})
.addOnFailureListener(e -> {
Log.e(TAG, "Could not get beacon state");
Toast.makeText(MainActivity.this, "Could not get beacon state!",
Toast.LENGTH_SHORT).show();
});
}
}
So, when I debug, it enters addOnSuccessListener but beaconState is always null.
Make sure that getBeaconState() is only supported on device API level above 18.
If calling from a device running an earlier version(below API 18), getStatus() will return status like API_NOT_AVAILABLE.
Moreover, the getBeaconState() method requires an android.permission.ACCESS_FINE_LOCATION permission. Add this permission in AndroidManifest.xml file.
Awareness.SnapshotApi.getBeaconState(mGoogleApiClient, BEACON_TYPE_FILTERS)
.setResultCallback(new ResultCallback<BeaconStateResult>() {
#Override
public void onResult(#NonNull BeaconStateResult beaconStateResult) {
if (!beaconStateResult.getStatus().isSuccess()) {
Log.e(TAG, "Could not get beacon state.");
return;
}
BeaconState beaconState = beaconStateResult.getBeaconState();
// Get info from the BeaconState.
}
});
In your code, you miss onResult() method. I hope it'll help you...!

Google Fit API not receiving data from Bluetooth wrist band (SWR12)

I am coding an Android application that uses the Google Fit API to connect to a bluetooth wrist band with the goal of collecting the heart rate information from the sensor. Here are the functions that I am using:
In the app main activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
if (!checkPermissions()) {
requestPermissions();
}
if (!checkPermissionsBody()) {
requestPermissionsBody();
Permission Request functions:
// Now we need a function to check permissions
private boolean checkPermissions() {
int permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
// Now we need a function to check permissions body sensors
private boolean checkPermissionsBody() {
int permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.BODY_SENSORS);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
// If permissions are not given, we need to request permissions
private void requestPermissions() {
Log.d(TAG,"getting permissions");
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION);
Log.d(TAG,String.valueOf(Manifest.permission.ACCESS_FINE_LOCATION));
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Log.i(TAG, "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
// If permissions are not given, we need to request permissions
private void requestPermissionsBody() {
Log.d(TAG,"getting permissions");
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.BODY_SENSORS);
Log.d(TAG,String.valueOf(Manifest.permission.BODY_SENSORS));
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.BODY_SENSORS},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Log.i(TAG, "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.BODY_SENSORS},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
onResume function:
protected void onResume() {
super.onResume();
// This ensures that if the user denies the permissions then uses Settings to re-enable
// them, the app will start working.
buildFitnessClient();
// now find the bluetooth devices
buildBLE();
// Connect to the Client
mClient.connect();
// Search for the data sources
findFitnessDataSources();
Building the API Client:
private void buildFitnessClient() {
if (mClient == null && checkPermissions()&& checkPermissionsBody()) {
mClient = new GoogleApiClient.Builder(this)
.addScope(new Scope(Scopes.FITNESS_BODY_READ))
.addApi(Fitness.SENSORS_API)
.addApi(Fitness.BLE_API)
.addConnectionCallbacks(
new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
Log.i(TAG, "Connected!!!");
// Now you can make calls to the Fitness APIs.
}
#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");
}
}
}
)
.enableAutoManage(this, 0, new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.i(TAG, "Google Play services connection failed. Cause: " +
result.toString());
Snackbar.make(
MainActivity.this.findViewById(R.id.activity_main),
"Exception while connecting to Google Play services: " +
result.getErrorMessage(),
Snackbar.LENGTH_INDEFINITE).show();
}
})
.build();
}
}
}
Now find the fitness source:
private void findFitnessDataSources() {
// [START find_data_sources]
// Note: Fitness.SensorsApi.findDataSources() requires the ACCESS_FINE_LOCATION permission.
Fitness.SensorsApi.findDataSources(mClient, new DataSourcesRequest.Builder()
// At least one datatype must be specified.
.setDataTypes(DataType.TYPE_HEART_RATE_BPM)
// Can specify whether data type is raw or derived.
//.setDataSourceTypes(DataSource.TYPE_RAW)
.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_HEART_RATE_BPM)
&& mListener == null) {
Log.i(TAG, "Data source for Heart Rate found! Registering.");
registerFitnessDataListener(dataSource,
DataType.TYPE_HEART_RATE_BPM);
}
}
}
});
Build BLE function - this is where you find the bluetooth device:
private void buildBLE(){
BleScanCallback callback = new BleScanCallback() {
#Override
public void onDeviceFound(BleDevice device) {
Log.d(TAG,"Found bluetooth Device");
// A device that provides the requested data types is available
PendingResult<Status> pendingResult =
Fitness.BleApi.claimBleDevice(mClient, device);
Log.d(TAG,"Claimed bluetooth Device");
}
#Override
public void onScanStopped() {
// The scan timed out or was interrupted
Log.d(TAG,"Scan was interruped");
}
};
StartBleScanRequest request = new StartBleScanRequest.Builder()
.setDataTypes(DataType.TYPE_HEART_RATE_BPM)
.setBleScanCallback(callback)
.build();
if (mClient != null){
PendingResult<Status> pendingResult =
Fitness.BleApi.startBleScan(mClient, request);
Log.d(TAG,"Find Sources");
Log.d(TAG,"Pending result: "+pendingResult.toString());
} else {
Log.d(TAG,"API client is null");
}
}
Finally Register the listener:
private void registerFitnessDataListener(DataSource dataSource, DataType dataType) {
// [START register_data_listener]
Log.i(TAG,"Listener Started");
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]
}
Most of these functions are taken directly from the google fit guide here: https://developers.google.com/fit/
The trouble is that when I use these functions the bluetooth device is discovered and claimed in a repeated loop. So the debugging output looks something like this:
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Scan cancelled");
Despite the device being claimed many times, there is data coming. This is even despite the fact that the listener is registered. I know because I see the debug line:
Log.i(TAG,"Listener Started");
So my question is, is there anything out of place in this code? Has anyone used this before?
For anyone interested in using Google Fit with a BLE device, the answer to this question is that you may only register one device at a time. When you reset you bluetooth adapter you have to re-claim the BLE device in the app. Therefore the proper procedure is to unclaim a BLE device and then reclaim it. Claiming a device multiple times leads to strange errors.

How to obtain Google Fit data from Smart watch Wearable device Fossil Q Founder? Which complies BLE requirements? In Android

I am trying to pull the data from Intel's Fossil Android based Smartwatch (BLE device) via Google Fit Android SDK. The BLE Scan seem to happen, pairing occurs but inside the result Callback it doesn't go to onDeviceFound (I can proceed from there if it reaches). It eventually times out within few seconds from the start of the scan.
Any help would be appreciated.
Thanks for the docs link. I did go through all of that thoroughly but it didn't help. This is my Class file and I can call startBleScan from my MainActivity, the BleScan seems to be working, but after that it doesn't proceed to go on to onDeviceFound method.
public class BlueToothDevicesManager {
private static final String TAG = "BlueToothDevicesManager";
private static final int REQUEST_BLUETOOTH = 1001;
private Main2Activity mMonitor;
private GoogleApiClient mClient;
public BlueToothDevicesManager(Main2Activity monitor, GoogleApiClient client) {
mMonitor = monitor;
mClient = client;
}
public void startBleScan() {
if (mClient.isConnected()) {
Log.i(TAG, "Google account is connected");
}
BleScanCallback callback = new BleScanCallback() {
#Override
public void onDeviceFound(BleDevice device) {
Log.i(TAG, "BLE Device Found: " + device.getName());
claimDevice(device);
}
#Override
public void onScanStopped() {
Log.i(TAG, "BLE scan stopped");
}
};
PendingResult result = Fitness.BleApi.startBleScan(mClient, new StartBleScanRequest.Builder()
.setDataTypes(DataType.TYPE_POWER_SAMPLE, DataType.TYPE_STEP_COUNT_CADENCE, DataType.TYPE_STEP_COUNT_DELTA, DataType.TYPE_SPEED, DataType.TYPE_ACTIVITY_SAMPLE, DataType.TYPE_DISTANCE_DELTA, DataType.TYPE_ACTIVITY_SEGMENT, DataType.TYPE_LOCATION_SAMPLE)
.setBleScanCallback(callback)
.build());
result.setResultCallback(new ResultCallback() {
#Override
public void onResult(#NonNull Result result) {
Status status = result.getStatus();
if (!status.isSuccess()) {
String a = status.getStatusCode() + "";
Log.i(TAG, a);
switch (status.getStatusCode()) {
case FitnessStatusCodes.DISABLED_BLUETOOTH:
try {
status.startResolutionForResult(mMonitor, REQUEST_BLUETOOTH);
} catch (SendIntentException e) {
Log.i(TAG, "SendIntentException: " + e.getMessage());
}
break;
}
Log.i(TAG, "BLE scan unsuccessful");
} else {
Log.i(TAG, "ble scan status message: " + status.getStatusMessage());
Log.i(TAG, "Ble scan successful: " + status.getResolution());
}
}
});
}
public void claimDevice(BleDevice device) {
//Stop ble scan
//Claim device
PendingResult<Status> pendingResult = Fitness.BleApi.claimBleDevice(mClient, device);
pendingResult.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(#NonNull Status st) {
if (st.isSuccess()) {
Log.i(TAG, "Claimed device successfully");
} else {
Log.e(TAG, "Did not successfully claim device");
}
}
});
}
}

Not getting live step counter updates from Google Fit

I want to check whether someone is walking using the Google Fit API by checking whether their step count delta is > 0. It seems simpler than using the History API to get daily step counts and figuring out whether it increased from the previous query.
I followed the code here but instead of location tracking, I do delta step counting. However, the listener doesn't seem to retrieve the latest delta step count.
Here is my code:
public void buildFitnessClient()
{
if ( mClient == null && checkPermissions() )
{
mClient = new GoogleApiClient.Builder(this)
.addApi(Fitness.SENSORS_API)
.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ))
.addConnectionCallbacks(
new GoogleApiClient.ConnectionCallbacks()
{
#Override
public void onConnected(Bundle bundle)
{
Log.d(TAG, "Connected!!!");
// Now you can make calls to the Fitness APIs.
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.d(TAG, "Connection lost. Cause: Network Lost.");
else if ( i
== GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED )
Log.d(TAG, "Connection lost. Reason: Service Disconnected");
}
}
)
.enableAutoManage(this, 0, new GoogleApiClient.OnConnectionFailedListener()
{
#Override
public void onConnectionFailed(ConnectionResult result)
{
Log.d(TAG, "Google Play services connection failed. Cause: " +
result.toString());
}
})
.build();
}
}
private void findFitnessDataSources()
{
// Note: Fitness.SensorsApi.findDataSources() requires the ACCESS_FINE_LOCATION permission.
Fitness.SensorsApi.findDataSources(mClient, new DataSourcesRequest.Builder()
// At least one datatype must be specified.
.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.d(TAG, "Result: " + dataSourcesResult.getStatus().toString());
for ( DataSource dataSource : dataSourcesResult.getDataSources() )
{
Log.d(TAG, "Data source found: " + dataSource.toString());
Log.d(TAG, "Data Source type: " + dataSource.getDataType().getName());
//Let's register a listener to receive Activity data!
if ( dataSource.getDataType().equals(DataType.TYPE_STEP_COUNT_DELTA)
&& mListener == null )
{
Log.d(TAG, "Data source for STEP_COUNT_DELTA found! Registering.");
registerFitnessDataListener(dataSource,
DataType.TYPE_STEP_COUNT_DELTA);
}
}
}
});
}
private void registerFitnessDataListener(DataSource dataSource, DataType typeStep)
{
mListener = new OnDataPointListener()
{
#Override
public void onDataPoint(DataPoint dataPoint)
{
Log.d(TAG, "Detected onDataPoint");
for ( Field field : dataPoint.getDataType().getFields() )
{
Value val = dataPoint.getValue(field);
Log.d(TAG, "Detected DataPoint field: " + field.getName());
Log.d(TAG, "Detected DataPoint value stepCount: " + val);
TransientValues.setStepCountDelta(val.asInt());
}
}
};
Fitness.SensorsApi.add(
mClient,
new SensorRequest.Builder()
.setDataSource(dataSource) // Optional but recommended for custom data sets.
.setDataType(typeStep) // Can't be omitted.
.setSamplingRate(1L, TimeUnit.SECONDS)
.build(),
mListener)
.setResultCallback(new ResultCallback<Status>()
{
#Override
public void onResult(Status status)
{
if ( status.isSuccess() ) Log.d(TAG, "Listener registered!");
else Log.d(TAG, "Listener not registered.");
}
});
}
The onDataPoint method is not called in mListener = onDataPointListener(){...} -> registerFitnessDataListener(...) and I'm not sure why.
Try by implementing this to your class
implements OnDataPointListener
I had the same problem and this fixed it. Let me know if it helped.

OnDataPointListener does not get callback if GoogleApiClient is disconnected

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]
}
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.
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.

Categories

Resources