Java tutorial
/* * Copyright (C) 2014 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.sensorsample; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Color; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; import android.view.View; import com.google.android.gms.common.ConnectionResult; 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.ResultCallback; import com.google.android.gms.common.api.Scope; import com.google.android.gms.common.api.Status; import com.google.android.gms.fit.samples.common.logger.Log; import com.google.android.gms.fit.samples.common.logger.LogView; import com.google.android.gms.fit.samples.common.logger.LogWrapper; import com.google.android.gms.fit.samples.common.logger.MessageOnlyLogFilter; import com.google.android.gms.fitness.Fitness; import com.google.android.gms.fitness.FitnessActivities; 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.Session; import com.google.android.gms.fitness.request.DataDeleteRequest; 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.wearable.MessageApi; import com.google.android.gms.wearable.MessageEvent; import com.google.android.gms.wearable.Wearable; import java.text.DateFormat; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import static java.text.DateFormat.getTimeInstance; /** * This sample demonstrates how to use the Sessions API of the Google Fit platform to insert * sessions into the History API, query against existing data, and remove sessions. It also * demonstrates how to authenticate a user with Google Play Services and how to properly * represent data in a Session, as well as how to use ActivitySegments. */ public class MyActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, MessageApi.MessageListener { public static final String TAG = "TestSleepSessions"; public static final String SLEEP_SESSION_NAME = "TEST SLEEP SESSION"; private static final String DATE_FORMAT = "yyyy.MM.dd HH:mm:ss"; private GoogleApiClient mClient = null; private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); // This method sets up our custom logger, which will print all log messages to the device // screen, as well as to adb logcat. initializeLogging(); // When permissions are revoked the app is restarted so onCreate is sufficient to check for // permissions core to the Activity's functionality. if (!checkPermissions()) { requestPermissions(); } } @Override 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. buildApiClient(); } /** * Build a {@link GoogleApiClient} that will authenticate the user and allow the application * to connect to Fitness APIs. The scopes included should match the scopes your app needs * (see documentation for details). Authentication will occasionally fail intentionally, * and in those cases, there will be a known resolution, which the OnConnectionFailedListener() * can address. Examples of this include the user never having signed in before, or having * multiple accounts on the device and needing to specify which account to use, etc. */ private void buildApiClient() { // Create the Google API Client if (mClient == null && checkPermissions()) { mClient = new GoogleApiClient.Builder(this).addApi(Wearable.API).addApi(Fitness.HISTORY_API) .addApi(Fitness.SESSIONS_API).addScope(new Scope(Scopes.FITNESS_LOCATION_READ_WRITE)) .addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE)).addConnectionCallbacks(this) .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(MyActivity.this.findViewById(R.id.main_activity_view), "Exception while connecting to Google Play services: " + result.getErrorMessage(), Snackbar.LENGTH_INDEFINITE).show(); } }).build(); } } /** * Create and execute a {@link SessionInsertRequest} to insert a session into the History API, * and then create and execute a {@link SessionReadRequest} to verify the insertion succeeded. * By using an AsyncTask to make our calls, we can schedule synchronous calls, so that we can * query for sessions after confirming that our insert was successful. Using asynchronous calls * and callbacks would not guarantee that the insertion had concluded before the read request * was made. An example of an asynchronous call using a callback can be found in the example * on deleting sessions below. */ private class InsertAndVerifySessionTask extends AsyncTask<String, Void, Void> { protected Void doInBackground(String... params) { //First, create a new session and an insertion request. // ?? SessionInsertRequest insertRequest = insertSleepSession(params[0]); // ???? // [START insert_session] // Then, invoke the Sessions API to insert the session and await the result, // which is possible here because of the AsyncTask. Always include a timeout when // calling await() to avoid hanging that can occur from the service being shutdown // because of low memory or other conditions. Log.i(TAG, "Inserting the session in the History API"); com.google.android.gms.common.api.Status insertStatus = Fitness.SessionsApi .insertSession(mClient, insertRequest).await(1, TimeUnit.MINUTES); // Before querying the session, check to see if the insertion succeeded. if (!insertStatus.isSuccess()) { Log.i(TAG, "There was a problem inserting the session: " + insertStatus.getStatusMessage()); return null; } // At this point, the session has been inserted and can be read. Log.i(TAG, "Session insert was successful!"); // [END insert_session] // ?? // Begin by creating the query. // SessionReadRequest readRequest = readSleepSession(); // // // [START read_session] // // Invoke the Sessions API to fetch the session with the query and wait for the result // // of the read request. Note: Fitness.SessionsApi.readSession() requires the // // ACCESS_FINE_LOCATION permission. // SessionReadResult sessionReadResult = // Fitness.SessionsApi.readSession(mClient, readRequest) // .await(1, TimeUnit.MINUTES); // // // Get a list of the sessions that match the criteria to check the result. // Log.i(TAG, "Session read was successful. Number of returned sessions is: " // + sessionReadResult.getSessions().size()); // for (Session session : sessionReadResult.getSessions()) { // // Process the session // dumpSession(session); // // // Process the data sets for this session // List<DataSet> dataSets = sessionReadResult.getDataSet(session); // for (DataSet dataSet : dataSets) { // dumpDataSet(dataSet); // } // } // [END read_session] return null; } } private SessionInsertRequest insertSleepSession(String params) { Log.i(TAG, "Creating a new session for an sleep"); // @todo ?????params???? // Setting start and end times for our run. Calendar cal = Calendar.getInstance(); Date now = new Date(); cal.setTime(now); // Set a range of the run, using a start time of 30 minutes before this moment, // with a 10-minute walk in the middle. long endTime = cal.getTimeInMillis(); cal.add(Calendar.MINUTE, -10); long endSleepTime = cal.getTimeInMillis(); cal.add(Calendar.MINUTE, -10); long startSleepTime = cal.getTimeInMillis(); cal.add(Calendar.MINUTE, -10); long startTime = cal.getTimeInMillis(); // [START build_insert_session_request_with_activity_segments] // Create a second DataSet of ActivitySegments to indicate the runner took a 10-minute walk // in the middle of the run. DataSource activitySegmentDataSource = new DataSource.Builder().setAppPackageName(this.getPackageName()) .setDataType(DataType.TYPE_ACTIVITY_SEGMENT).setName(SLEEP_SESSION_NAME + "-activity segments") .setType(DataSource.TYPE_RAW).build(); DataSet activitySegments = DataSet.create(activitySegmentDataSource); DataPoint firstSleepDp = activitySegments.createDataPoint().setTimeInterval(startTime, startSleepTime, TimeUnit.MILLISECONDS); firstSleepDp.getValue(Field.FIELD_ACTIVITY).setActivity(FitnessActivities.SLEEP_DEEP); activitySegments.add(firstSleepDp); DataPoint walkingDp = activitySegments.createDataPoint().setTimeInterval(startSleepTime, endSleepTime, TimeUnit.MILLISECONDS); walkingDp.getValue(Field.FIELD_ACTIVITY).setActivity(FitnessActivities.SLEEP_LIGHT); activitySegments.add(walkingDp); DataPoint secondSleepDp = activitySegments.createDataPoint().setTimeInterval(endSleepTime, endTime, TimeUnit.MILLISECONDS); secondSleepDp.getValue(Field.FIELD_ACTIVITY).setActivity(FitnessActivities.SLEEP_AWAKE); activitySegments.add(secondSleepDp); // [START build_insert_session_request] // Create a session with metadata about the activity. Session session = new Session.Builder().setName(SLEEP_SESSION_NAME).setDescription("sleep test") .setIdentifier("UniqueIdentifierHere").setActivity(FitnessActivities.SLEEP) .setStartTime(startTime, TimeUnit.MILLISECONDS).setEndTime(endTime, TimeUnit.MILLISECONDS).build(); // Build a session insert request SessionInsertRequest insertRequest = new SessionInsertRequest.Builder().setSession(session) .addDataSet(activitySegments).build(); // [END build_insert_session_request] // [END build_insert_session_request_with_activity_segments] return insertRequest; } private SessionReadRequest readSleepSession() { Log.i(TAG, "Reading History API results for session: " + SLEEP_SESSION_NAME); // [START build_read_session_request] // Set a start and end time for our query, using a start time 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(); // Build a session read request SessionReadRequest readRequest = new SessionReadRequest.Builder() .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS).read(DataType.TYPE_ACTIVITY_SEGMENT) .setSessionName(SLEEP_SESSION_NAME).build(); // [END build_read_session_request] return readRequest; } private void dumpDataSet(DataSet dataSet) { Log.i(TAG, "Data returned for Data type: " + dataSet.getDataType().getName()); for (DataPoint dp : dataSet.getDataPoints()) { DateFormat dateFormat = getTimeInstance(); Log.i(TAG, "Data point:"); Log.i(TAG, "\tType: " + dp.getDataType().getName()); Log.i(TAG, "\tStart: " + dateFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS))); Log.i(TAG, "\tEnd: " + dateFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS))); for (Field field : dp.getDataType().getFields()) { Log.i(TAG, "\tField: " + field.getName() + " Value: " + dp.getValue(field)); } } } private void dumpSession(Session session) { DateFormat dateFormat = getTimeInstance(); Log.i(TAG, "Data returned for Session: " + session.getName() + "\n\tDescription: " + session.getDescription() + "\n\tStart: " + dateFormat.format(session.getStartTime(TimeUnit.MILLISECONDS)) + "\n\tEnd: " + dateFormat.format(session.getEndTime(TimeUnit.MILLISECONDS))); } /** * Delete the {@link DataSet} we inserted with our {@link Session} from the History API. * In this example, we delete all step count data for the past 24 hours. Note that this * deletion uses the History API, and not the Sessions API, since sessions are truly just time * intervals over a set of data, and the data is what we are interested in removing. */ private void deleteSession() { Log.i(TAG, "Deleting today's session data for activity"); // 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 endTime = cal.getTimeInMillis(); cal.add(Calendar.DAY_OF_YEAR, -1); long startTime = cal.getTimeInMillis(); // Create a delete request object, providing a data type and a time interval DataDeleteRequest request = new DataDeleteRequest.Builder() .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS) .addDataType(DataType.TYPE_ACTIVITY_SEGMENT).deleteAllSessions() // Or specify a particular session here .build(); // Invoke the History API with the Google API client object and the delete request and // specify a callback that will check the result. Fitness.HistoryApi.deleteData(mClient, request).setResultCallback(new ResultCallback<Status>() { @Override public void onResult(Status status) { if (status.isSuccess()) { Log.i(TAG, "Successfully deleted today's sessions"); } else { // The deletion will fail if the requesting app tries to delete data // that it did not insert. Log.i(TAG, "Failed to delete today's sessions"); } } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_delete_session) { deleteSession(); return true; } return super.onOptionsItemSelected(item); } @Override protected void onStart() { super.onStart(); } @Override protected void onStop() { super.onStop(); if (null != mClient && mClient.isConnected()) { Wearable.MessageApi.removeListener(mClient, this); mClient.disconnect(); } } @Override public void onConnected(Bundle bundle) { Log.d(TAG, "onConnected"); Wearable.MessageApi.addListener(mClient, this); } @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 == ConnectionCallbacks.CAUSE_NETWORK_LOST) { Log.i(TAG, "Connection lost. Cause: Network Lost."); } else if (i == ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) { Log.i(TAG, "Connection lost. Reason: Service Disconnected"); } } @Override public void onMessageReceived(MessageEvent messageEvent) { Log.d(TAG, "onMessageReceived : " + messageEvent.getPath()); // wear???? String msg = messageEvent.getPath(); Log.i(TAG, msg); // ?????????Fit?? new InsertAndVerifySessionTask().execute(msg); } /** * Initialize a custom log class that outputs both to in-app targets and logcat. */ private void initializeLogging() { // Wraps Android's native log framework. LogWrapper logWrapper = new LogWrapper(); // Using Log, front-end to the logging chain, emulates android.util.log method signatures. Log.setLogNode(logWrapper); // Filter strips out everything except the message text. MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); logWrapper.setNext(msgFilter); // On screen logging via a customized TextView. LogView logView = (LogView) findViewById(R.id.sample_logview); // Fixing this lint errors adds logic without benefit. //noinspection AndroidLintDeprecation logView.setTextAppearance(this, R.style.Log); logView.setBackgroundColor(Color.WHITE); msgFilter.setNext(logView); Log.i(TAG, "Ready"); } /** * 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; } private void requestPermissions() { boolean shouldProvideRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, 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.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(MyActivity.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(MyActivity.this, new String[] { Manifest.permission.ACCESS_FINE_LOCATION }, REQUEST_PERMISSIONS_REQUEST_CODE); } } /** * Callback received when a permissions request has been completed. */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { Log.i(TAG, "onRequestPermissionResult"); if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) { if (grantResults.length <= 0) { // If user interaction was interrupted, the permission request is cancelled and you // receive empty arrays. Log.i(TAG, "User interaction was cancelled."); } else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission was granted. buildApiClient(); } else { // Permission denied. // In this Activity we've chosen to notify the user that they // have rejected a core permission for the app since it makes the Activity useless. // We're communicating this message in a Snackbar since this is a sample app, but // core permissions would typically be best requested during a welcome-screen flow. // Additionally, it is important to remember that a permission might have been // rejected without asking the user for permission (device policy or "Never ask // again" prompts). Therefore, a user interface affordance is typically implemented // when permissions are denied. Otherwise, your app could appear unresponsive to // touches or interactions which have required permissions. 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(); } } } }