java - 如何使用 Google Fit API 按需获取 SmartBand2 的实时心率?

标签 java android google-api sony google-fit-sdk

如何实时获取心率,而无需等待手环自行完成(每 10 分钟一次)。我使用 Google Fit API。 SmartBand2 应用程序可以按需获取心率,但我怀疑它是否使用了 Google Fit API。 我用来设置监听器的代码是这样的:

     if (savedInstanceState != null) {
        authInProgress = savedInstanceState.getBoolean(AUTH_PENDING);
    }
    mApiClient = new GoogleApiClient.Builder(this)
            .addApi(Fitness.SENSORS_API)
            .addApi(Fitness.BLE_API)
            .addApi(Fitness.HISTORY_API)
            .addApi(Fitness.RECORDING_API)
            .addScope(new Scope(Scopes.FITNESS_BODY_READ))
            .addScope(new Scope(Scopes.FITNESS_LOCATION_READ))
            .addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE))
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .enableAutoManage(this, 0, this)//Al añadir la propiedad enableAutoManage, Google Play Services gestiona correctamente la conexión y desconexión.
            .build();
    initCallbacks();


     @Override
public void onConnected(@Nullable final Bundle bundle) {

    checkSelfPermissionBody();

}

private void registerFitnessDataListener(DataSource dataSource, DataType dataType) {

    SensorRequest request = new SensorRequest.Builder()
            .setDataSource( dataSource )
            .setDataType( dataType )
            .setSamplingRate( 3, TimeUnit.SECONDS )
            .build();

    Fitness.SensorsApi.add( mApiClient, request, this )
            .setResultCallback(new ResultCallback<Status>() {
                @Override
                public void onResult(Status status) {
                    if (status.isSuccess()) {
                        Log.d(TAG, "GoogleFit SensorApi successfully added" );
                    }
                }
            });

    dataSource.getDevice();
}

@Override
public void onConnectionSuspended(final int i) {

}

@Override
public void onConnectionFailed(@NonNull final ConnectionResult connectionResult) {

    if( !authInProgress ) {
        try {
            authInProgress = true;
            connectionResult.startResolutionForResult( MainActivity.this, REQUEST_OAUTH );
        } catch(IntentSender.SendIntentException e ) {

        }
    } else {
        Log.d( TAG,"GoogleFit authInProgress" );
    }

}

@Override
public void onDataPoint(final DataPoint dataPoint) {
    for( final Field field : dataPoint.getDataType().getFields() ) {
        final Value value = dataPoint.getValue( field );
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "Field: " + field.getName() + " Value: " + value, Toast.LENGTH_SHORT).show();
                Log.d(TAG,"Field: " + field.getName() + " Value: " + value);
            }
        });
    }
}

@Override
protected void onStart() {
    super.onStart();
    mApiClient.connect();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.d(TAG,"ON ACTIVITY RESULT");
    if( requestCode == REQUEST_OAUTH ) {
        authInProgress = false;
        if( resultCode == RESULT_OK ) {
            if( !mApiClient.isConnecting() && !mApiClient.isConnected() ) {
                mApiClient.connect();
                Log.d(TAG,"GoogleFit RESULT_CONECTADO" );
            }
        } else if( resultCode == RESULT_CANCELED ) {
            Log.d(TAG,"GoogleFit RESULT_CANCELED" );
        }
    } else {
        Log.d(TAG,"GoogleFitr equestCode NOT request_oauth");
    }
}


@Override
protected void onStop() {
    super.onStop();
    Log.d(TAG,"ON STOP");

    Fitness.SensorsApi.remove( mApiClient, this )
            .setResultCallback(new ResultCallback<Status>() {
                @Override
                public void onResult(Status status) {
                    if (status.isSuccess()) {
                        mApiClient.disconnect();
                    }
                }
            });
}

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


private void bleScanDevice()
{

    // 1. Define a callback object
    BleScanCallback callback = new BleScanCallback() {
        @Override
        public void onDeviceFound(final BleDevice device) {
            // A device that provides the requested data types is available
            // -> Claim this BLE device (See next example)
            Log.d(TAG,device.toString());
            Log.d(TAG,"PRUEBA");
            claimDevice(device);
        }
        @Override
        public void onScanStopped() {
            // The scan timed out or was interrupted
        }
    };

    // 2. Create a scan request object:
    // - Specify the data types you're interested in
    // - Provide the callback object
    StartBleScanRequest request = new StartBleScanRequest.Builder()
            .setDataTypes(DataType.TYPE_HEART_RATE_BPM)
            .setBleScanCallback(callback)
            .build();


    // 3. Invoke the Bluetooth Low Energy API with:
    // - The Google API client
    // - The scan request
    PendingResult<Status> pendingResult =
            Fitness.BleApi.startBleScan(mApiClient, request);

    // 4. Check the result (see other examples)


}

private void claimDevice(BleDevice bleDevice)
{
    // After the platform invokes your callback
    // with a compatible BLE device (bleDevice):

    // 1. Invoke the Bluetooth Low Energy API with:
    // - The Google API client
    // - The BleDevice object provided in the callback
    PendingResult<Status> pendingResult =
            Fitness.BleApi.claimBleDevice(mApiClient, bleDevice);

    // 2. Check the result (see other examples)
}

private void releaseDevice(BleDevice bleDevice)
{
    // When you no longer need the BLE device

    // 1. Invoke the Bluetooth Low Energy API with:
    // - The Google API client
    // - The BLE device (from the initial scan)
    PendingResult<Status> pendingResult =
            Fitness.BleApi.unclaimBleDevice(mApiClient, bleDevice);

    // 2. Check the result (see other examples)
}

我还尝试通过记录健身数据来访问我的心率,并通过健身历史记录回收数据,但我只能获取最小值、最大值和平均值。 代码:

private void readDataFitnessHistory()
{
    // Setting a start and end date using a range of 1 week before this moment.
    Calendar cal = Calendar.getInstance();
    Date now = new Date();
    cal.setTime(now);
    long endTime = cal.getTimeInMillis();

    cal.add(Calendar.WEEK_OF_YEAR, -1);
    long startTime = cal.getTimeInMillis();

    java.text.DateFormat dateFormat = getDateInstance();
    Log.i(TAG, "Range Start: " + dateFormat.format(startTime));
    Log.i(TAG, "Range End: " + dateFormat.format(endTime));

    DataReadRequest readRequest = new DataReadRequest.Builder()
            // The data request can specify multiple data types to return, effectively
            // combining multiple data queries into one call.
            // In this example, it's very unlikely that the request is for several hundred
            // datapoints each consisting of a few steps and a timestamp.  The more likely
            // scenario is wanting to see how many steps were walked per day, for 7 days.
            .aggregate(DataType.TYPE_HEART_RATE_BPM, DataType.AGGREGATE_HEART_RATE_SUMMARY)
            // Analogous to a "Group By" in SQL, defines how data should be aggregated.
            // bucketByTime allows for a time span, whereas bucketBySession would allow
            // bucketing by "sessions", which would need to be defined in code.
            .bucketByTime(1, TimeUnit.DAYS)
            .setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
            .build();

    // Invoke the History API to fetch the data with the query and await the result of
    // the read request.
    DataReadResult dataReadResult =
            Fitness.HistoryApi.readData(mApiClient, readRequest).await(1, TimeUnit.MINUTES);
    DataSet dataSet = dataReadResult.getDataSet(DataType.TYPE_HEART_RATE_BPM);
    dumpDataSet(dataSet);
    displayBpmDataForToday();


} 


 private void showDataSet(DataSet dataSet) {
    Log.d(TAG +" History", "Data returned for Data type: " + dataSet.getDataType().getName());
    DateFormat dateFormat = DateFormat.getDateInstance();
    DateFormat timeFormat = DateFormat.getTimeInstance();
    List<DataPoint> dataPoints =dataSet.getDataPoints();

    for (DataPoint dp : dataPoints) {
        Log.d(TAG +" History", "Data point:");
        Log.d(TAG +" History", "\tType: " + dp.getDataType().getName());
        Log.d(TAG +" History", "\tStart: " + dateFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)) + " " + timeFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)));
        Log.d(TAG +" History", "\tEnd: " + dateFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS)) + " " + timeFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)));
        for(Field field : dp.getDataType().getFields()) {
            Log.d(TAG + " History", "\tField: " + field.getName() +
                    " Value: " + dp.getValue(field));
        }
    }
} 

private void displayBpmDataForToday() {
    DailyTotalResult result = Fitness.HistoryApi.readDailyTotal( mApiClient, DataType.TYPE_HEART_RATE_BPM ).await(1, TimeUnit.MINUTES);
    showDataSet(result.getTotal());
} 

 private void suscribeDataFitness(){
    Fitness.RecordingApi.subscribe(mApiClient, DataType.TYPE_HEART_RATE_BPM)
            .setResultCallback(mSubscribeResultCallback);
}

private void listActiveFitnessSubscription()
{
    Fitness.RecordingApi.listSubscriptions(mApiClient, DataType.TYPE_HEART_RATE_BPM)
            // Create the callback to retrieve the list of subscriptions asynchronously.
            .setResultCallback(mListSubscriptionsResultCallback);
}

private void unsuscribeDataFitness(){
    Fitness.RecordingApi.unsubscribe(mApiClient, DataType.TYPE_HEART_RATE_BPM)
            .setResultCallback(mCancelSubscriptionResultCallback);
}

private void initCallbacks() {
    mSubscribeResultCallback = new ResultCallback<Status>() {
        @Override
        public void onResult(Status status) {
            if (status.isSuccess()) {
                if (status.getStatusCode()
                        == FitnessStatusCodes.SUCCESS_ALREADY_SUBSCRIBED) {
                    Log.i(TAG, "Existing subscription for activity detected.");
                } else {
                    Log.i(TAG, "Successfully subscribed!");
                }
            } else {
                Log.i(TAG, "There was a problem subscribing.");
            }
        }
    };

    mCancelSubscriptionResultCallback = new ResultCallback<Status>() {
        @Override
        public void onResult(Status status) {
            if (status.isSuccess()) {
                Log.i(TAG, "Successfully unsubscribed for data type: TYPE_HEART_RATE_BPM");
            } else {
                // Subscription not removed
                Log.i(TAG, "Failed to unsubscribe for data type: TYPE_HEART_RATE_BPM");
            }
        }
    };

    mListSubscriptionsResultCallback = new ResultCallback<ListSubscriptionsResult>() {
        @Override
        public void onResult(@NonNull ListSubscriptionsResult listSubscriptionsResult) {
            for (Subscription subscription : listSubscriptionsResult.getSubscriptions()) {
                DataType dataType = subscription.getDataType();
                Log.e(TAG + "RecordingAPI", dataType.getName() );
                for (Field field : dataType.getFields() ) {
                    Log.e( TAG + " RecordingAPI", field.toString() );
                }
            }
        }
    };
} 

答案:

History:    Type: com.google.heart_rate.summary
History:    Start: 22 sept. 2017 10:40:06
D/DBGPRUEBA History:    End: 22 sept. 2017 10:40:06
D/DBGPRUEBA History:    Field: average Value: 71.13179
D/DBGPRUEBA History:    Field: max Value: 86.0
D/DBGPRUEBA History:    Field: min Value: 55.0

最佳答案

要从心率传感器获取实时数据,您应该使用传感器 API,如本 documentation 中所述。并订阅一个监听器,该监听器将为您提供实时数据点。您还可以在 SensorRequest.Builder() 中设置采样率,您可以每秒获取一次,而不是每 10 分钟获取一次。

关于java - 如何使用 Google Fit API 按需获取 SmartBand2 的实时心率?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46368398/

相关文章:

python - 如何设置 Google API 凭据以通过 Jupyter Notebook 访问 AutoML API?

java - 如何检测和处理多线程环境中访问的java列表中的冲突?

java - NoClassDefFound错误: Failed resolution of: Lorg/apache/commons/logging/LogFactory

java - 不允许重复的数组(Java)

java - Android 绘图线 2 点

php - 通过 PHP 使用 Google 身份验证 API

java - 执行由 Jasmin 生成的文件 .classes 时出错

Android - layout-large 文件夹被忽略

android - 在自定义适配器 ListView 中突出显示搜索到的文本

java - 如何查看Android KeyStore的证书吊销列表(CRL)?