package com.example.android.wearable.watchface; 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.PendingResult; 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.fitness.Fitness; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Switch; import android.widget.Toast; import java.util.concurrent.TimeUnit; /** * Allows users of the Fit WatchFace to tie their Google Fit account to the WatchFace. */ public class FitDistanceWatchFaceConfigActivity extends Activity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { private static final String TAG = "FitDistanceConfig"; // Request code for launching the Intent to resolve authorization. private static final int REQUEST_OAUTH = 1; // Shared Preference used to record if the user has enabled Google Fit previously. private static final String PREFS_FIT_ENABLED_BY_USER = "com.example.android.wearable.watchface.preferences.FIT_ENABLED_BY_USER"; /* Tracks whether an authorization activity is stacking over the current activity, i.e., when * a known auth error is being resolved, such as showing the account chooser or presenting a * consent dialog. This avoids common duplications as might happen on screen rotations, etc. */ private static final String EXTRA_AUTH_STATE_PENDING = "com.example.android.wearable.watchface.extra.AUTH_STATE_PENDING"; private static final long FIT_DISABLE_TIMEOUT_SECS = TimeUnit.SECONDS.toMillis(5);; private boolean mResolvingAuthorization; private boolean mFitEnabled; private GoogleApiClient mGoogleApiClient; private Switch mFitAuthSwitch; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fit_watch_face_config); mFitAuthSwitch = (Switch) findViewById(R.id.fit_auth_switch); if (savedInstanceState != null) { mResolvingAuthorization = savedInstanceState.getBoolean(EXTRA_AUTH_STATE_PENDING, false); } else { mResolvingAuthorization = false; } // Checks if user previously enabled/approved Google Fit. SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE); mFitEnabled = sharedPreferences.getBoolean(PREFS_FIT_ENABLED_BY_USER, false); mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(Fitness.HISTORY_API) .addApi(Fitness.RECORDING_API) .addApi(Fitness.CONFIG_API) .addScope(new Scope(Scopes.FITNESS_LOCATION_READ_WRITE)) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); } @Override protected void onStart() { super.onStart(); if ((mFitEnabled) && (mGoogleApiClient != null)) { mFitAuthSwitch.setChecked(true); mFitAuthSwitch.setEnabled(true); mGoogleApiClient.connect(); } else { mFitAuthSwitch.setChecked(false); mFitAuthSwitch.setEnabled(true); } } @Override protected void onStop() { super.onStop(); if ((mGoogleApiClient != null) && (mGoogleApiClient.isConnected())) { mGoogleApiClient.disconnect(); } } @Override protected void onSaveInstanceState(Bundle bundle) { super.onSaveInstanceState(bundle); bundle.putBoolean(EXTRA_AUTH_STATE_PENDING, mResolvingAuthorization); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (savedInstanceState != null) { mResolvingAuthorization = savedInstanceState.getBoolean(EXTRA_AUTH_STATE_PENDING, false); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "onActivityResult()"); if (requestCode == REQUEST_OAUTH) { mResolvingAuthorization = false; if (resultCode == RESULT_OK) { setUserFitPreferences(true); if (!mGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) { mGoogleApiClient.connect(); } } else { // User cancelled authorization, reset the switch. setUserFitPreferences(false); } } } @Override public void onConnected(Bundle connectionHint) { Log.d(TAG, "onConnected: " + connectionHint); } @Override public void onConnectionSuspended(int cause) { if (cause == GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST) { Log.i(TAG, "Connection lost. Cause: Network Lost."); } else if (cause == GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) { Log.i(TAG, "Connection lost. Reason: Service Disconnected"); } else { Log.i(TAG, "onConnectionSuspended: " + cause); } mFitAuthSwitch.setChecked(false); mFitAuthSwitch.setEnabled(true); } @Override public void onConnectionFailed(ConnectionResult result) { Log.d(TAG, "Connection to Google Fit failed. Cause: " + result.toString()); if (!result.hasResolution()) { // User cancelled authorization, reset the switch. mFitAuthSwitch.setChecked(false); mFitAuthSwitch.setEnabled(true); // Show the localized error dialog GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show(); return; } // Resolve failure if not already trying/authorizing. if (!mResolvingAuthorization) { try { Log.i(TAG, "Attempting to resolve failed GoogleApiClient connection"); mResolvingAuthorization = true; result.startResolutionForResult(this, REQUEST_OAUTH); } catch (IntentSender.SendIntentException e) { Log.e(TAG, "Exception while starting resolution activity", e); } } } public void onSwitchClicked(View view) { boolean userWantsToEnableFit = mFitAuthSwitch.isChecked(); if (userWantsToEnableFit) { Log.d(TAG, "User wants to enable Fit."); if ((mGoogleApiClient != null) && (!mGoogleApiClient.isConnected())) { mGoogleApiClient.connect(); } } else { Log.d(TAG, "User wants to disable Fit."); // Disable switch until disconnect request is finished. mFitAuthSwitch.setEnabled(false); PendingResult pendingResult = Fitness.ConfigApi.disableFit(mGoogleApiClient); pendingResult.setResultCallback(new ResultCallback() { @Override public void onResult(Status status) { if (status.isSuccess()) { Toast.makeText( FitDistanceWatchFaceConfigActivity.this, "Disconnected from Google Fit.", Toast.LENGTH_LONG).show(); setUserFitPreferences(false); mGoogleApiClient.disconnect(); } else { Toast.makeText( FitDistanceWatchFaceConfigActivity.this, "Unable to disconnect from Google Fit. See logcat for details.", Toast.LENGTH_LONG).show(); // Re-set the switch since auth failed. setUserFitPreferences(true); } } }, FIT_DISABLE_TIMEOUT_SECS, TimeUnit.SECONDS); } } private void setUserFitPreferences(boolean userFitPreferences) { mFitEnabled = userFitPreferences; SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putBoolean(PREFS_FIT_ENABLED_BY_USER, userFitPreferences); editor.commit(); mFitAuthSwitch.setChecked(userFitPreferences); mFitAuthSwitch.setEnabled(true); } }