1 package com.example.android.wearable.watchface; 2 3 import com.google.android.gms.common.ConnectionResult; 4 import com.google.android.gms.common.GooglePlayServicesUtil; 5 import com.google.android.gms.common.Scopes; 6 import com.google.android.gms.common.api.GoogleApiClient; 7 import com.google.android.gms.common.api.PendingResult; 8 import com.google.android.gms.common.api.ResultCallback; 9 import com.google.android.gms.common.api.Scope; 10 import com.google.android.gms.common.api.Status; 11 import com.google.android.gms.fitness.Fitness; 12 13 import android.app.Activity; 14 import android.content.Context; 15 import android.content.Intent; 16 import android.content.IntentSender; 17 import android.content.SharedPreferences; 18 import android.os.Bundle; 19 import android.util.Log; 20 import android.view.View; 21 import android.widget.Switch; 22 import android.widget.Toast; 23 24 import java.util.concurrent.TimeUnit; 25 26 /** 27 * Allows users of the Fit WatchFace to tie their Google Fit account to the WatchFace. 28 */ 29 public class FitDistanceWatchFaceConfigActivity extends Activity implements 30 GoogleApiClient.ConnectionCallbacks, 31 GoogleApiClient.OnConnectionFailedListener { 32 33 private static final String TAG = "FitDistanceConfig"; 34 35 // Request code for launching the Intent to resolve authorization. 36 private static final int REQUEST_OAUTH = 1; 37 38 // Shared Preference used to record if the user has enabled Google Fit previously. 39 private static final String PREFS_FIT_ENABLED_BY_USER = 40 "com.example.android.wearable.watchface.preferences.FIT_ENABLED_BY_USER"; 41 42 /* Tracks whether an authorization activity is stacking over the current activity, i.e., when 43 * a known auth error is being resolved, such as showing the account chooser or presenting a 44 * consent dialog. This avoids common duplications as might happen on screen rotations, etc. 45 */ 46 private static final String EXTRA_AUTH_STATE_PENDING = 47 "com.example.android.wearable.watchface.extra.AUTH_STATE_PENDING"; 48 49 private static final long FIT_DISABLE_TIMEOUT_SECS = TimeUnit.SECONDS.toMillis(5);; 50 51 private boolean mResolvingAuthorization; 52 53 private boolean mFitEnabled; 54 55 private GoogleApiClient mGoogleApiClient; 56 57 private Switch mFitAuthSwitch; 58 59 @Override onCreate(Bundle savedInstanceState)60 protected void onCreate(Bundle savedInstanceState) { 61 super.onCreate(savedInstanceState); 62 setContentView(R.layout.activity_fit_watch_face_config); 63 64 mFitAuthSwitch = (Switch) findViewById(R.id.fit_auth_switch); 65 66 if (savedInstanceState != null) { 67 mResolvingAuthorization = 68 savedInstanceState.getBoolean(EXTRA_AUTH_STATE_PENDING, false); 69 } else { 70 mResolvingAuthorization = false; 71 } 72 73 // Checks if user previously enabled/approved Google Fit. 74 SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE); 75 mFitEnabled = 76 sharedPreferences.getBoolean(PREFS_FIT_ENABLED_BY_USER, false); 77 78 mGoogleApiClient = new GoogleApiClient.Builder(this) 79 .addApi(Fitness.HISTORY_API) 80 .addApi(Fitness.RECORDING_API) 81 .addApi(Fitness.CONFIG_API) 82 .addScope(new Scope(Scopes.FITNESS_LOCATION_READ_WRITE)) 83 .addConnectionCallbacks(this) 84 .addOnConnectionFailedListener(this) 85 .build(); 86 } 87 88 @Override onStart()89 protected void onStart() { 90 super.onStart(); 91 92 if ((mFitEnabled) && (mGoogleApiClient != null)) { 93 94 mFitAuthSwitch.setChecked(true); 95 mFitAuthSwitch.setEnabled(true); 96 97 mGoogleApiClient.connect(); 98 99 } else { 100 101 mFitAuthSwitch.setChecked(false); 102 mFitAuthSwitch.setEnabled(true); 103 } 104 } 105 106 @Override onStop()107 protected void onStop() { 108 super.onStop(); 109 110 if ((mGoogleApiClient != null) && (mGoogleApiClient.isConnected())) { 111 mGoogleApiClient.disconnect(); 112 } 113 } 114 115 @Override onSaveInstanceState(Bundle bundle)116 protected void onSaveInstanceState(Bundle bundle) { 117 super.onSaveInstanceState(bundle); 118 bundle.putBoolean(EXTRA_AUTH_STATE_PENDING, mResolvingAuthorization); 119 } 120 121 @Override onRestoreInstanceState(Bundle savedInstanceState)122 protected void onRestoreInstanceState(Bundle savedInstanceState) { 123 super.onRestoreInstanceState(savedInstanceState); 124 125 if (savedInstanceState != null) { 126 mResolvingAuthorization = 127 savedInstanceState.getBoolean(EXTRA_AUTH_STATE_PENDING, false); 128 } 129 } 130 131 @Override onActivityResult(int requestCode, int resultCode, Intent data)132 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 133 Log.d(TAG, "onActivityResult()"); 134 135 if (requestCode == REQUEST_OAUTH) { 136 mResolvingAuthorization = false; 137 138 if (resultCode == RESULT_OK) { 139 setUserFitPreferences(true); 140 141 if (!mGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) { 142 mGoogleApiClient.connect(); 143 } 144 } else { 145 // User cancelled authorization, reset the switch. 146 setUserFitPreferences(false); 147 } 148 } 149 } 150 151 @Override onConnected(Bundle connectionHint)152 public void onConnected(Bundle connectionHint) { 153 Log.d(TAG, "onConnected: " + connectionHint); 154 } 155 156 @Override onConnectionSuspended(int cause)157 public void onConnectionSuspended(int cause) { 158 159 if (cause == GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST) { 160 Log.i(TAG, "Connection lost. Cause: Network Lost."); 161 } else if (cause == GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) { 162 Log.i(TAG, "Connection lost. Reason: Service Disconnected"); 163 } else { 164 Log.i(TAG, "onConnectionSuspended: " + cause); 165 } 166 167 mFitAuthSwitch.setChecked(false); 168 mFitAuthSwitch.setEnabled(true); 169 } 170 171 @Override onConnectionFailed(ConnectionResult result)172 public void onConnectionFailed(ConnectionResult result) { 173 Log.d(TAG, "Connection to Google Fit failed. Cause: " + result.toString()); 174 175 if (!result.hasResolution()) { 176 // User cancelled authorization, reset the switch. 177 mFitAuthSwitch.setChecked(false); 178 mFitAuthSwitch.setEnabled(true); 179 // Show the localized error dialog 180 GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show(); 181 return; 182 } 183 184 // Resolve failure if not already trying/authorizing. 185 if (!mResolvingAuthorization) { 186 try { 187 Log.i(TAG, "Attempting to resolve failed GoogleApiClient connection"); 188 mResolvingAuthorization = true; 189 result.startResolutionForResult(this, REQUEST_OAUTH); 190 } catch (IntentSender.SendIntentException e) { 191 Log.e(TAG, "Exception while starting resolution activity", e); 192 } 193 } 194 } 195 onSwitchClicked(View view)196 public void onSwitchClicked(View view) { 197 198 boolean userWantsToEnableFit = mFitAuthSwitch.isChecked(); 199 200 if (userWantsToEnableFit) { 201 202 Log.d(TAG, "User wants to enable Fit."); 203 if ((mGoogleApiClient != null) && (!mGoogleApiClient.isConnected())) { 204 mGoogleApiClient.connect(); 205 } 206 207 } else { 208 Log.d(TAG, "User wants to disable Fit."); 209 210 // Disable switch until disconnect request is finished. 211 mFitAuthSwitch.setEnabled(false); 212 213 PendingResult<Status> pendingResult = Fitness.ConfigApi.disableFit(mGoogleApiClient); 214 215 pendingResult.setResultCallback(new ResultCallback<Status>() { 216 @Override 217 public void onResult(Status status) { 218 219 if (status.isSuccess()) { 220 Toast.makeText( 221 FitDistanceWatchFaceConfigActivity.this, 222 "Disconnected from Google Fit.", 223 Toast.LENGTH_LONG).show(); 224 225 setUserFitPreferences(false); 226 227 mGoogleApiClient.disconnect(); 228 229 230 } else { 231 Toast.makeText( 232 FitDistanceWatchFaceConfigActivity.this, 233 "Unable to disconnect from Google Fit. See logcat for details.", 234 Toast.LENGTH_LONG).show(); 235 236 // Re-set the switch since auth failed. 237 setUserFitPreferences(true); 238 } 239 } 240 }, FIT_DISABLE_TIMEOUT_SECS, TimeUnit.SECONDS); 241 } 242 } 243 setUserFitPreferences(boolean userFitPreferences)244 private void setUserFitPreferences(boolean userFitPreferences) { 245 246 mFitEnabled = userFitPreferences; 247 SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE); 248 SharedPreferences.Editor editor = sharedPreferences.edit(); 249 editor.putBoolean(PREFS_FIT_ENABLED_BY_USER, userFitPreferences); 250 editor.commit(); 251 252 mFitAuthSwitch.setChecked(userFitPreferences); 253 mFitAuthSwitch.setEnabled(true); 254 } 255 } 256