package com.algobase.accounts; import android.os.Bundle; import android.os.Handler; import android.app.Activity; import com.google.android.gms.plus.Plus; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.common.Scopes; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.Status; import com.google.android.gms.common.api.Scope; import com.google.android.gms.fitness.Fitness; import com.google.android.gms.fitness.FitnessActivities; import com.google.android.gms.fitness.data.Session; import com.google.android.gms.fitness.data.Bucket; import com.google.android.gms.fitness.data.DataPoint; import com.google.android.gms.fitness.data.DataSet; import com.google.android.gms.fitness.data.DataSource; import com.google.android.gms.fitness.data.DataType; import com.google.android.gms.fitness.data.Field; import com.google.android.gms.fitness.data.Value; import com.google.android.gms.fitness.request.DataDeleteRequest; import com.google.android.gms.fitness.request.DataReadRequest; import com.google.android.gms.fitness.request.SessionInsertRequest; import com.google.android.gms.fitness.request.SessionReadRequest; import com.google.android.gms.fitness.result.SessionReadResult; import com.google.android.gms.fitness.result.DataReadResult; import com.google.android.gms.location.ActivityRecognition; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import com.algobase.share.system.*; import com.algobase.share.dialog.*; public class GoogleFitClient1 { public static final int OAUTH_REQUEST = 9100; public static final String TAG = "BasicHistoryApi"; static final String DATE_FORMAT = "yyyy.MM.dd HH:mm:ss"; GoogleApiClient client; boolean authInProgress = false; String accountName = ""; Activity activity; public void write_log(String txt) {} public void toast(String txt) {} public void ack(String title, String txt) {} public boolean duplicate_handler(String title, long start_t, long end_t) { return false; } Runnable connectRunnable; boolean silent = false; public GoogleFitClient1(Activity act) { activity = act; GoogleApiClient.Builder builder = new GoogleApiClient.Builder(activity); builder.addApi(Plus.API); builder.addApi(ActivityRecognition.API); builder.addApi(Fitness.HISTORY_API); builder.addApi(Fitness.SESSIONS_API); builder.addApi(Fitness.CONFIG_API); //builder.addApi(Fitness.RECORDING_API); //builder.addApi(Fitness.SENSORS_API); builder.useDefaultAccount(); builder.addScope(new Scope(Scopes.FITNESS_LOCATION_READ_WRITE)); builder.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE)); builder.addScope(new Scope(Scopes.PROFILE)); builder.addConnectionCallbacks( new ConnectionCallbacks() { @Override public void onConnected(Bundle bundle) { accountName = Plus.AccountApi.getAccountName(client); if (!silent) { if (accountName == null) toast("Google Fit connected."); else toast(accountName); } if (connectRunnable == null) return; connectRunnable.run(); connectRunnable = null; } @Override public void onConnectionSuspended(int i) { if (i == ConnectionCallbacks.CAUSE_NETWORK_LOST) toast("Google Fit: Connection lost."); else if (i == ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) toast("Google Fit: disconnected."); } }); builder.addOnConnectionFailedListener( new GoogleApiClient.OnConnectionFailedListener() { @Override public void onConnectionFailed(ConnectionResult result) { // called whenever the API client fails to connect. write_log("GoogleApiClient: " + result.toString()); if (silent) return; if (!result.hasResolution()) { GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), activity, 0).show(); return; } if (authInProgress) return; authInProgress = true; write_log("GoogleApiClient: start resolution."); try { result.startResolutionForResult(activity, OAUTH_REQUEST); } catch (Exception e) { write_log(e.toString()); } } }); client = builder.build(); } public void connectAfterResolution() { authInProgress = false; if (!client.isConnected() && !client.isConnecting()) client.connect(); } public void connect(Runnable runnable) { if (client.isConnected()) runnable.run(); else { connectRunnable = runnable; silent = false; client.connect(); } } public void connect() { connectRunnable = null; silent = false; client.connect(); } public void connectSilent() { silent = true; connectRunnable = null; client.connect(); } public void disconnect() { if (client.isConnected()) client.disconnect(); } public boolean isConnecting() { return client.isConnecting(); } public boolean isConnected() { return client.isConnected(); } public String getAccountName() { return accountName; } public void revokeAuth() { //toast("revokeAuth"); PendingResult pendingResult = null; try { pendingResult = Fitness.ConfigApi.disableFit(client); } catch (Exception ex) { toast(ex.toString()); } if (pendingResult == null) return; pendingResult.setResultCallback(new ResultCallback() { public void onResult(Status status) { if (status.isSuccess()) { toast("Google Fit disabled."); client.disconnect(); accountName = ""; } else ack("Google Fit not disabled", status.toString()); } }); } void insertStepData(long start_t, long end_t, int step_count) { write_log("Creating a new step data insert request"); // set start and end time for our data, using a start time of // 1 hour before this moment. //Calendar cal = Calendar.getInstance(); //Date now = new Date(); //cal.setTime(now); //long end_t = cal.getTimeInMillis(); //cal.add(Calendar.HOUR_OF_DAY, -1); //long start_t = cal.getTimeInMillis(); // Create a data source DataSource dataSource = new DataSource.Builder() .setAppPackageName(activity) .setDataType(DataType.TYPE_STEP_COUNT_DELTA) .setName(TAG + " - step count") .setType(DataSource.TYPE_RAW) //.setDevice(device) .build(); //DataType.TYPE_SPEED m/s //DataType.TYPE_HEART_RATE_BPM //DataType.TYPE_DISTANCE_DELTA //DataType.TYPE_CALORIES_EXPENDED //DataType.TYPE_LOCATION_SAMPLE //DataType.TYPE_LOCATION_TRACK // create a data set final DataSet dataSet = DataSet.create(dataSource); // for each data point, specify a start time, end time, // and the data value -- in this case, the number of new steps. DataPoint dataPoint = dataSet.createDataPoint(); //dataPoint.setTimeStamp(t, TimeUnit.MILLISECONDS); dataPoint.setTimeInterval(start_t, end_t, TimeUnit.MILLISECONDS); dataPoint.getValue(Field.FIELD_STEPS).setInt(step_count); dataSet.add(dataPoint); new MyThread() { public void run() { PendingResult result = Fitness.HistoryApi.insertData(client, dataSet); //Status insertStatus = result.await(); Status insertStatus = result.await(1, TimeUnit.MINUTES); if (!insertStatus.isSuccess()) write_log("There was a problem inserting the dataset."); } }.start(); } void queryFitnessData(long start_t, long end_t) { SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); write_log("Range Start: " + dateFormat.format(start_t)); write_log("Range End: " + dateFormat.format(end_t)); DataReadRequest.Builder builder = 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. builder.aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA); // 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. builder.bucketByTime(1, TimeUnit.DAYS); builder.setTimeRange(start_t, end_t, TimeUnit.MILLISECONDS); final DataReadRequest readRequest = builder.build(); // invoke the History API to fetch the data with the query // and await the result of the read request. new MyThread() { public void run() { PendingResult result = Fitness.HistoryApi.readData(client, readRequest); //DataReadResult dataReadResult = result.await(); DataReadResult dataReadResult = result.await(1, TimeUnit.MINUTES); printData(dataReadResult); } }.start(); } void printData(DataReadResult dataReadResult) { // If the DataReadRequest object specified aggregated data, // dataReadResult will be returned as buckets containing DataSets, // instead of just DataSets. if (dataReadResult.getBuckets().size() > 0) { write_log("Number of returned buckets of DataSets is: " + dataReadResult.getBuckets().size()); for (Bucket bucket : dataReadResult.getBuckets()) { List dataSets = bucket.getDataSets(); for (DataSet dataSet : dataSets) dumpDataSet(dataSet); } return; } if (dataReadResult.getDataSets().size() > 0) { write_log("Number of returned DataSets is: " + dataReadResult.getDataSets().size()); for (DataSet dataSet : dataReadResult.getDataSets()) dumpDataSet(dataSet); } } void dumpDataSet(DataSet dataSet) { write_log("Data returned for Data type: " + dataSet.getDataType().getName()); SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); for (DataPoint dp : dataSet.getDataPoints()) { write_log("Data point:"); write_log("\tType: " + dp.getDataType().getName()); write_log("\tStart: " + dateFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS))); write_log("\tEnd: " + dateFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS))); for(Field field : dp.getDataType().getFields()) write_log("\tField: " + field.getName() + " Value: " + dp.getValue(field)); } } void deleteData() { // delete today's step count data // set a start and end time for our data, using a start time // of 1 day before this moment. Calendar cal = Calendar.getInstance(); Date now = new Date(); cal.setTime(now); long end_t = cal.getTimeInMillis(); cal.add(Calendar.DAY_OF_YEAR, -1); long start_t = cal.getTimeInMillis(); // create a delete request object, providing a data type and // a time interval DataDeleteRequest.Builder builder = new DataDeleteRequest.Builder(); builder.setTimeInterval(start_t, end_t, TimeUnit.MILLISECONDS); builder.addDataType(DataType.TYPE_STEP_COUNT_DELTA); final DataDeleteRequest request = builder.build(); PendingResult pendingResult = Fitness.HistoryApi.deleteData(client, request); pendingResult.setResultCallback(new ResultCallback() { @Override public void onResult(Status status) { if (status.isSuccess()) write_log("Successfully deleted today's step count data"); else write_log("Failed to delete today's step count data"); } }); } // sessions public void insertBikingSession(String name, final long start_t, final long end_t, final float dist, final float speed) { //int hrate = 0; //int calories = 0; //String s = String.format("%d sec %.2f km", (end_t-start_t)/1000,dist/1000); //ack("Insert Session", s); final String sessionName = "sTracks - " + name; final String sessionDescription = "Biking Track"; final String sessionID = name; DataSource distDataSource = new DataSource.Builder() .setAppPackageName(activity.getPackageName()) .setDataType(DataType.TYPE_DISTANCE_DELTA) .setName(sessionName) .setType(DataSource.TYPE_RAW) //.setDevice(device) .build(); DataSource speedDataSource = new DataSource.Builder() .setAppPackageName(activity.getPackageName()) .setDataType(DataType.TYPE_SPEED) .setName(sessionName) .setType(DataSource.TYPE_RAW) //.setDevice(device) .build(); /* DataSource hrateDataSource = new DataSource.Builder() .setAppPackageName(activity.getPackageName()) .setDataType(DataType.TYPE_HEART_RATE_BPM) .setName(sessionName) .setType(DataSource.TYPE_RAW) //.setDevice(device) .build(); */ /* DataSource calorDataSource = new DataSource.Builder() .setAppPackageName(activity.getPackageName()) .setDataType(DataType.TYPE_CALORIES_EXPENDED) .setName(sessionName) .setType(DataSource.TYPE_RAW) //.setDevice(device) .build(); */ /* DataType.TYPE_SPEED m/s DataType.TYPE_HEART_RATE_BPM DataType.TYPE_DISTANCE_DELTA DataType.TYPE_CALORIES_EXPENDED DataType.TYPE_LOCATION_SAMPLE DataType.TYPE_LOCATION_TRACK */ final DataSet distDataSet = DataSet.create(distDataSource); DataPoint distDataPoint = distDataSet.createDataPoint(); distDataPoint.setTimeInterval(start_t, end_t, TimeUnit.MILLISECONDS); distDataPoint.getValue(Field.FIELD_DISTANCE).setFloat(dist); distDataSet.add(distDataPoint); final DataSet speedDataSet = DataSet.create(speedDataSource); DataPoint speedDataPoint = speedDataSet.createDataPoint(); speedDataPoint.setTimeInterval(start_t, end_t, TimeUnit.MILLISECONDS); speedDataPoint.getValue(Field.FIELD_SPEED).setFloat(speed); speedDataSet.add(speedDataPoint); /* final DataSet hrateDataSet = DataSet.create(hrateDataSource); DataPoint hrateDataPoint = hrateDataSet.createDataPoint(); hrateDataPoint.setTimeInterval(start_t, end_t, TimeUnit.MILLISECONDS); hrateDataPoint.getValue(Field.FIELD_BPM).setFloat(hrate); hrateDataSet.add(hrateDataPoint); */ /* final DataSet calorDataSet = DataSet.create(calorDataSource); DataPoint calorDataPoint = calorDataSet.createDataPoint(); calorDataPoint.setTimeInterval(start_t, end_t, TimeUnit.MILLISECONDS); calorDataPoint.getValue(Field.FIELD_CALORIES).setInt(calories); calorDataSet.add(calorDataPoint); */ // create session Session.Builder builder1 = new Session.Builder(); builder1.setName(sessionName); builder1.setDescription(sessionDescription); builder1.setIdentifier(sessionID); builder1.setActivity(FitnessActivities.BIKING); builder1.setStartTime(start_t, TimeUnit.MILLISECONDS); builder1.setEndTime(end_t, TimeUnit.MILLISECONDS); Session session = builder1.build(); // create a session insert request SessionInsertRequest.Builder builder2 = new SessionInsertRequest.Builder(); builder2.setSession(session); builder2.addDataSet(distDataSet); builder2.addDataSet(speedDataSet); //builder2.addDataSet(hrateDataSet); //builder2.addDataSet(calorDataSet); final SessionInsertRequest insertRequest = builder2.build(); new MyThread() { public void run() { int n = readFitnessSession(sessionID); if (n > 0) { if (!duplicate_handler(sessionID,start_t,end_t)) ack(sessionID, "Insert: Duplicate Activity."); return; } PendingResult result = Fitness.SessionsApi.insertSession(client,insertRequest); //Status insertStatus = result.await(); //Status insertStatus = result.await(15, TimeUnit.SECONDS); Status insertStatus = result.await(1, TimeUnit.MINUTES); if (insertStatus.isSuccess()) toast(sessionID + " inserted."); else toast("Google Fit: " + insertStatus.getStatusMessage()); if (readFitnessSession(sessionID) == 0) toast("Error: Cannot find session."); } }.start(); } int readFitnessSession(String id) { write_log("Searching: id = " + id); // set a start and end time for the query // start time: 1 month before current time Calendar cal = Calendar.getInstance(); Date now = new Date(); cal.setTime(now); long endTime = cal.getTimeInMillis(); long startTime = endTime - 3600L*24*30*1000; // Build a session read request SessionReadRequest request = new SessionReadRequest.Builder() .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS) .read(DataType.TYPE_SPEED) //.setSessionName(SAMPLE_SESSION_NAME) .setSessionId(id) .build(); PendingResult pendingResult = Fitness.SessionsApi.readSession(client,request); SessionReadResult result = pendingResult.await(1,TimeUnit.MINUTES); //toast("number of sessions: " + result.getSessions().size()); return result.getSessions().size(); } public void deleteSession(final String sessionID, long start_t, long end_t) { //toast("delete session: id = " + sessionID); /* Calendar cal = Calendar.getInstance(); Date now = new Date(); cal.setTime(now); long endTime = cal.getTimeInMillis(); long startTime = endTime - 3600L*24*365*1000; */ Session session = new Session.Builder() .setIdentifier(sessionID) .setActivity(FitnessActivities.BIKING) .setStartTime(start_t, TimeUnit.MILLISECONDS) .setEndTime(end_t, TimeUnit.MILLISECONDS) .build(); final DataDeleteRequest request = new DataDeleteRequest.Builder() /* .setTimeInterval(startTime,endTime, TimeUnit.MILLISECONDS) .addSession(session) */ .setTimeInterval(start_t,end_t, TimeUnit.MILLISECONDS) .deleteAllSessions() .deleteAllData() .build(); new MyThread() { public void run() { int n = readFitnessSession(sessionID); if (n == 0) { ack(sessionID, "Delete: Activity not found."); return; } PendingResult pendingResult = Fitness.HistoryApi.deleteData(client,request); pendingResult.setResultCallback(new ResultCallback() { public void onResult(Status status) { if (status.isSuccess()) toast(sessionID + " deleted."); else toast("Failed to delete session."); } }); } }.start(); } }