Google Fit is a platform that allows developers to build applications that are focused on user fitness data. One of the tools Google has provided is Google Fit for Android, which is available as a package in Google Play Services.
While there are many different APIs available for Google Fit, as discussed in this Envato Tuts+ overview article, this tutorial focuses on using Google Fit to create low-power background subscriptions for sensor data. We explore how to leverage Google Play Services to store fitness data so that it can be accessed at a later time.
The Recording API differs from the Sensors API in that live sensor data is not delivered to the Recording API, but rather data is saved online in order to save a record of user activity. This tutorial includes a sample project, which you can download from GitHub.
1. Project Setup
Step 1: Set Up the Developer Console
To use Google Fit, you need to create an OAuth 2.0 client ID and register your application through the Google Developer Console. You can find a detailed explanation of how to set up the Google Developer Console in my tutorial about the Google Fit Sensors API.
Step 2: Create the Android Project
Once you have your application configured for authentication in the Google Developer Console, use the package name you registered to create a new Android application with a minimum SDK of 14 and an empty Activity
.
With the base Android app created, open the build.gradle file and include Google Play Services under the dependencies node and sync your app.
compile 'com.google.android.gms:play-services-fitness:8.4.0'
You should now be able to include the necessary Google Play Services classes into your application. Before we dive into the Java code for this tutorial, open activity_main.xml and modify it so that it includes two Button
items that will be used to demonstrate some of the functionality of the Recording API.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/btn_show_subscriptions" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Show Subscriptions"/> <Button android:id="@+id/btn_cancel_subscriptions" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Cancel Subscriptions"/> </LinearLayout>
When your app is run, the user interface will look like the below image.
To finish setting up your project, open MainActivity.java and implement the following callbacks and their required methods:
GoogleApiClient.ConnectionCallbacks
GoogleAPiClient.OnConnectionFailedListener
View.OnClickListener
You also need to add the member variables for your class, as shown below.
public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, View.OnClickListener { private Button mCancelSubscriptionsBtn; private Button mShowSubscriptionsBtn; private ResultCallback<Status> mSubscribeResultCallback; private ResultCallback<Status> mCancelSubscriptionResultCallback; private ResultCallback<ListSubscriptionsResult> mListSubscriptionsResultCallback; private GoogleApiClient mGoogleApiClient; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public void onConnected(@Nullable Bundle bundle) { Log.e("RecordingAPI", "onConnected"); } @Override public void onConnectionSuspended(int i) { Log.e("RecordingAPI", "onConnectionSuspended"); } @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { Log.e("RecordingAPI", "onConnectionFailed"); } @Override public void onClick(View v) { } }
2. Initialization
Before you can start using the callbacks and views that you defined above, you need to initialize them. We do this by creating two helper methods, one for initializing the views and one for initializing the callbacks that are called in onCreate()
.
The initViews()
method points the Button
objects to the defined items in activity.xml and sets an OnClickListener
for each button.
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); initCallbacks(); } private void initViews() { mCancelSubscriptionsBtn = (Button) findViewById(R.id.btn_cancel_subscriptions); mShowSubscriptionsBtn = (Button) findViewById(R.id.btn_show_subscriptions); mCancelSubscriptionsBtn.setOnClickListener(this); mShowSubscriptionsBtn.setOnClickListener(this); }
In initCallbacks()
, things get a little more interesting. Each callback is used for a specific asynchronous operation of the Recording API. As their names imply, mSubscribeResultCallback
is called when you first add a subscription for a sensor, mCancelSubscriptionResultCallback
is called when you cancel that subscription, and mListSubscriptionResultCallback
is used when you request a list of all subscriptions.
In the following code snippet, the two callbacks related to starting and stopping a subscription only log a success or failure. mListSubscriptionResultCallback
receives a list of subscriptions and log out information about each of them by getting the DataType
and then each Field
associated with the subscription.
private void initCallbacks() { mSubscribeResultCallback = new ResultCallback<Status>() { @Override public void onResult(@NonNull Status status) { if (status.isSuccess()) { if (status.getStatusCode() == FitnessStatusCodes.SUCCESS_ALREADY_SUBSCRIBED) { Log.e( "RecordingAPI", "Already subscribed to the Recording API"); } else { Log.e("RecordingAPI", "Subscribed to the Recording API"); } } } }; mCancelSubscriptionResultCallback = new ResultCallback<Status>() { @Override public void onResult(@NonNull Status status) { if (status.isSuccess()) { Log.e( "RecordingAPI", "Canceled subscriptions!"); } else { // Subscription not removed Log.e("RecordingAPI", "Failed to cancel subscriptions"); } } }; mListSubscriptionsResultCallback = new ResultCallback<ListSubscriptionsResult>() { @Override public void onResult(@NonNull ListSubscriptionsResult listSubscriptionsResult) { for (Subscription subscription : listSubscriptionsResult.getSubscriptions()) { DataType dataType = subscription.getDataType(); Log.e( "RecordingAPI", dataType.getName() ); for (Field field : dataType.getFields() ) { Log.e( "RecordingAPI", field.toString() ); } } } }; }
3. Connecting and Controlling Subscriptions
Now that you have the general framework for your sample app put together, it's time to wrap everything up by connecting to Google Play Services and requesting subscriptions.
In onCreate()
, you can initialize and connect to Google Play Services by declaring that you use Fitness.RECORDING_API
, adding a scope for reading fitness activity data, and setting it to automatically connect/disconnect through the application's life cycle.
mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(Fitness.RECORDING_API) .addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ)) .addConnectionCallbacks(this) .enableAutoManage(this, 0, this) .build();
At any time after the GoogleApiClient
has connected, you can request a subscription. For this tutorial, we subscribe in onConnected()
. The following snippet shows how to start a subscription for changes in the user's step count, though you can access any sensor that Google Fit supports.
If a subscription is already running because of an earlier action, then the callback receives a status code of FitnessStatusCodes.SUCCESS_ALREADY_SUBSCRIBED
.
@Override public void onConnected(@Nullable Bundle bundle) { Fitness.RecordingApi.subscribe(mGoogleApiClient, DataType.TYPE_STEP_COUNT_DELTA) .setResultCallback(mSubscribeResultCallback); }
It can sometimes be useful to request a list of subscriptions. Earlier, you created two buttons for these operations. In the onClick()
method, you check which button was tapped and perform the corresponding action.
@Override public void onClick(View v) { switch(v.getId()) { case R.id.btn_cancel_subscriptions: { cancelSubscriptions(); break; } case R.id.btn_show_subscriptions: { showSubscriptions(); break; } } }
If showSubscriptions()
is called, then the application requests a list of subscriptions associated with the Recording API.
private void showSubscriptions() { Fitness.RecordingApi.listSubscriptions(mGoogleApiClient) .setResultCallback(mListSubscriptionsResultCallback); }
This causes mListSubscriptionsResultCallback
to log out all the connected subscriptions. The result looks something like this:
.../com.tutsplus.googlefitrecordingapi E/RecordingAPI: com.google.step_count.delta .../com.tutsplus.googlefitrecordingapi E/RecordingAPI: steps(i)
Cancelling a subscription is straightforward as you can see below.
private void cancelSubscriptions() { Fitness.RecordingApi.unsubscribe(mGoogleApiClient, DataType.TYPE_STEP_COUNT_DELTA) .setResultCallback(mCancelSubscriptionResultCallback); }
Conclusion
You should now have a basic understanding of how to connect to Google Fit from an Android app and work with the Recording API. While this may not seem like much right now, it does pave the way for you to store information about the user's physical activities so that they can be accessed by other Google Fit APIs within your application.
Comments