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