1 package ${packageName};
3 import android.content.Intent;
4 import android.content.IntentSender;
5 import android.os.Bundle;
6 <#if minApiLevel lt 14>import android.support.v7.app.ActionBarActivity;</#if>
7 <#if minApiLevel gte 14>import android.app.Activity;</#if>
8 import android.util.Log;
10 import com.google.android.gms.common.ConnectionResult;
11 import com.google.android.gms.common.GooglePlayServicesClient;
12 import com.google.android.gms.common.Scopes;
13 import com.google.android.gms.plus.PlusClient;
14 <#if applicationPackage??>import ${applicationPackage}.R;</#if>
17 * A base class to wrap communication with the Google Play Services PlusClient.
19 public abstract class PlusBaseActivity extends <#if minApiLevel lt 14>ActionBar</#if>Activity
20 implements GooglePlayServicesClient.ConnectionCallbacks,
21 GooglePlayServicesClient.OnConnectionFailedListener {
23 private static final String TAG = PlusBaseActivity.class.getSimpleName();
25 // A magic number we will use to know that our sign-in error resolution activity has completed
26 private static final int OUR_REQUEST_CODE = 49404;
28 // A flag to stop multiple dialogues appearing for the user
29 private boolean mAutoResolveOnFail;
31 // A flag to track when a connection is already in progress
32 public boolean mPlusClientIsConnecting = false;
34 // This is the helper object that connects to Google Play Services.
35 private PlusClient mPlusClient;
37 // The saved result from {@link #onConnectionFailed(ConnectionResult)}. If a connection
38 // attempt has been made, this is non-null.
39 // If this IS null, then the connect method is still running.
40 private ConnectionResult mConnectionResult;
43 * Called when the {@link PlusClient} revokes access to this app.
45 protected abstract void onPlusClientRevokeAccess();
48 * Called when the PlusClient is successfully connected.
50 protected abstract void onPlusClientSignIn();
53 * Called when the {@link PlusClient} is disconnected.
55 protected abstract void onPlusClientSignOut();
58 * Called when the {@link PlusClient} is blocking the UI. If you have a progress bar widget,
59 * this tells you when to show or hide it.
61 protected abstract void onPlusClientBlockingUI(boolean show);
64 * Called when there is a change in connection state. If you have "Sign in"/ "Connect",
65 * "Sign out"/ "Disconnect", or "Revoke access" buttons, this lets you know when their states
68 protected abstract void updateConnectButtonState();
71 protected void onCreate(Bundle savedInstanceState) {
72 super.onCreate(savedInstanceState);
74 // Initialize the PlusClient connection.
75 // Scopes indicate the information about the user your application will be able to access.
77 new PlusClient.Builder(this, this, this).setScopes(Scopes.PLUS_LOGIN,
78 Scopes.PLUS_ME).build();
82 * Try to sign in the user.
84 public void signIn() {
85 if (!mPlusClient.isConnected()) {
86 // Show the dialog as we are now signing in.
87 setProgressBarVisible(true);
88 // Make sure that we will start the resolution (e.g. fire the intent and pop up a
89 // dialog for the user) for any errors that come in.
90 mAutoResolveOnFail = true;
91 // We should always have a connection result ready to resolve,
92 // so we can start that process.
93 if (mConnectionResult != null) {
96 // If we don't have one though, we can start connect in
97 // order to retrieve one.
98 initiatePlusClientConnect();
102 updateConnectButtonState();
106 * Connect the {@link PlusClient} only if a connection isn't already in progress. This will
107 * call back to {@link #onConnected(android.os.Bundle)} or
108 * {@link #onConnectionFailed(com.google.android.gms.common.ConnectionResult)}.
110 private void initiatePlusClientConnect() {
111 if (!mPlusClient.isConnected() && !mPlusClient.isConnecting()) {
112 mPlusClient.connect();
117 * Disconnect the {@link PlusClient} only if it is connected (otherwise, it can throw an error.)
118 * This will call back to {@link #onDisconnected()}.
120 private void initiatePlusClientDisconnect() {
121 if (mPlusClient.isConnected()) {
122 mPlusClient.disconnect();
127 * Sign out the user (so they can switch to another account).
129 public void signOut() {
131 // We only want to sign out if we're connected.
132 if (mPlusClient.isConnected()) {
133 // Clear the default account in order to allow the user to potentially choose a
134 // different account from the account chooser.
135 mPlusClient.clearDefaultAccount();
137 // Disconnect from Google Play Services, then reconnect in order to restart the
138 // process from scratch.
139 initiatePlusClientDisconnect();
141 Log.v(TAG, "Sign out successful!");
144 updateConnectButtonState();
148 * Revoke Google+ authorization completely.
150 public void revokeAccess() {
152 if (mPlusClient.isConnected()) {
153 // Clear the default account as in the Sign Out.
154 mPlusClient.clearDefaultAccount();
156 // Revoke access to this entire application. This will call back to
157 // onAccessRevoked when it is complete, as it needs to reach the Google
158 // authentication servers to revoke all tokens.
159 mPlusClient.revokeAccessAndDisconnect(new PlusClient.OnAccessRevokedListener() {
160 public void onAccessRevoked(ConnectionResult result) {
161 updateConnectButtonState();
162 onPlusClientRevokeAccess();
170 protected void onStart() {
172 initiatePlusClientConnect();
176 protected void onStop() {
178 initiatePlusClientDisconnect();
181 public boolean isPlusClientConnecting() {
182 return mPlusClientIsConnecting;
185 private void setProgressBarVisible(boolean flag) {
186 mPlusClientIsConnecting = flag;
187 onPlusClientBlockingUI(flag);
191 * A helper method to flip the mResolveOnFail flag and start the resolution
192 * of the ConnectionResult from the failed connect() call.
194 private void startResolution() {
196 // Don't start another resolution now until we have a result from the activity we're
198 mAutoResolveOnFail = false;
199 // If we can resolve the error, then call start resolution and pass it an integer tag
200 // we can use to track.
201 // This means that when we get the onActivityResult callback we'll know it's from
202 // being started here.
203 mConnectionResult.startResolutionForResult(this, OUR_REQUEST_CODE);
204 } catch (IntentSender.SendIntentException e) {
205 // Any problems, just try to connect() again so we get a new ConnectionResult.
206 mConnectionResult = null;
207 initiatePlusClientConnect();
212 * An earlier connection failed, and we're now receiving the result of the resolution attempt
215 * @see #onConnectionFailed(ConnectionResult)
218 protected void onActivityResult(int requestCode, int responseCode, Intent intent) {
219 updateConnectButtonState();
220 if (requestCode == OUR_REQUEST_CODE && responseCode == RESULT_OK) {
221 // If we have a successful result, we will want to be able to resolve any further
222 // errors, so turn on resolution with our flag.
223 mAutoResolveOnFail = true;
224 // If we have a successful result, let's call connect() again. If there are any more
225 // errors to resolve we'll get our onConnectionFailed, but if not,
226 // we'll get onConnected.
227 initiatePlusClientConnect();
228 } else if (requestCode == OUR_REQUEST_CODE && responseCode != RESULT_OK) {
229 // If we've got an error we can't resolve, we're no longer in the midst of signing
230 // in, so we can stop the progress spinner.
231 setProgressBarVisible(false);
236 * Successfully connected (called by PlusClient)
239 public void onConnected(Bundle connectionHint) {
240 updateConnectButtonState();
241 setProgressBarVisible(false);
242 onPlusClientSignIn();
246 * Successfully disconnected (called by PlusClient)
249 public void onDisconnected() {
250 updateConnectButtonState();
251 onPlusClientSignOut();
255 * Connection failed for some reason (called by PlusClient)
256 * Try and resolve the result. Failure here is usually not an indication of a serious error,
257 * just that the user's input is needed.
259 * @see #onActivityResult(int, int, Intent)
262 public void onConnectionFailed(ConnectionResult result) {
263 updateConnectButtonState();
265 // Most of the time, the connection will fail with a user resolvable result. We can store
266 // that in our mConnectionResult property ready to be used when the user clicks the
268 if (result.hasResolution()) {
269 mConnectionResult = result;
270 if (mAutoResolveOnFail) {
271 // This is a local helper function that starts the resolution of the problem,
272 // which may be showing the user an account chooser or similar.
278 public PlusClient getPlusClient() {