Skip to content

Instantly share code, notes, and snippets.

@alexjlockwood
Last active December 10, 2015 13:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alexjlockwood/4440089 to your computer and use it in GitHub Desktop.
Save alexjlockwood/4440089 to your computer and use it in GitHub Desktop.
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Build;
import android.preference.PreferenceManager;
import com.google.android.gms.auth.GoogleAuthUtil;
public class AccountUtils {
private static final String KEY_ACCOUNT_NAME = "account_name";
public static Account getGoogleAccountByName(Context ctx, String accountName) {
if (accountName != null) {
AccountManager am = AccountManager.get(ctx);
Account[] accounts = am.getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
for (Account account : accounts) {
if (accountName.equals(account.name)) {
return account;
}
}
}
return null;
}
public static String getAccountName(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return prefs.getString(KEY_ACCOUNT_NAME, null);
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public static void setAccountName(Context ctx, String accountName) {
Editor editor = PreferenceManager.getDefaultSharedPreferences(ctx).edit();
editor.putString(KEY_ACCOUNT_NAME, accountName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
editor.apply();
} else {
editor.commit();
}
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public static void removeAccount(Context ctx) {
Editor editor = PreferenceManager.getDefaultSharedPreferences(ctx).edit();
editor.remove(KEY_ACCOUNT_NAME);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
editor.apply();
} else {
editor.commit();
}
}
}
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.common.AccountPicker;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
public class AuthActivity extends Activity {
static final int REQUEST_CODE_RECOVER_PLAY_SERVICES = 1001;
static final int REQUEST_CODE_PICK_ACCOUNT = 1002;
static final int REQUEST_CODE_RECOVER_AUTH = 1003;
private AuthTask mAuthTask;
private Request mLastRequest;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onResume() {
super.onResume();
if (checkPlayServices() && checkUserAccount()) {
// Then we're good to go!
}
}
private boolean checkPlayServices() {
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (status != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(status)) {
showErrorDialog(status);
} else {
Toast.makeText(this, "This device is not supported.", Toast.LENGTH_LONG).show();
finish();
}
return false;
}
return true;
}
void showErrorDialog(int code) {
GooglePlayServicesUtil.getErrorDialog(code, this, REQUEST_CODE_RECOVER_PLAY_SERVICES).show();
}
private boolean checkUserAccount() {
String accountName = AccountUtils.getAccountName(this);
if (accountName == null) {
// Then the user was not found in the SharedPreferences. Either the
// application deliberately removed the account, or the application's
// data has been forcefully erased.
showAccountPicker();
return false;
}
Account account = AccountUtils.getGoogleAccountByName(this, accountName);
if (account == null) {
// Then the account has since been removed.
AccountUtils.removeAccount(this);
showAccountPicker();
return false;
}
return true;
}
private void showAccountPicker() {
Intent pickAccountIntent = AccountPicker.newChooseAccountIntent(null, null,
new String[] { GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE }, true, null, null, null, null);
startActivityForResult(pickAccountIntent, REQUEST_CODE_PICK_ACCOUNT);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_RECOVER_PLAY_SERVICES:
if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "Google Play Services must be installed and up-to-date.",
Toast.LENGTH_SHORT).show();
finish();
}
return;
case REQUEST_CODE_PICK_ACCOUNT:
if (resultCode == RESULT_OK) {
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
AccountUtils.setAccountName(this, accountName);
} else if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "This application requires a Google account.", Toast.LENGTH_SHORT)
.show();
finish();
}
return;
case REQUEST_CODE_RECOVER_AUTH:
if (resultCode == RESULT_OK) {
if (mLastRequest != null) {
// We have recovered, so retry the old request.
mAuthTask = new AuthTask(this, mLastRequest);
mAuthTask.execute();
}
} else if (resultCode == RESULT_CANCELED) {
// TODO: Choose how your application should react in the case
// that the user has decided not to attempt auth recovery.
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
protected void onDestroy() {
if (mAuthTask != null) {
mAuthTask.cancel(true);
}
}
}
package com.alexjlockwood.gcmdemo;
import java.io.IOException;
import android.os.AsyncTask;
import android.util.Log;
import com.alexjlockwood.gcmdemo.http.Request;
import com.alexjlockwood.gcmdemo.http.Response;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.GooglePlayServicesAvailabilityException;
import com.google.android.gms.auth.UserRecoverableAuthException;
public class AuthTask extends AsyncTask<Void, Void, Response> {
private static final String TAG = "AuthTask";
private AuthActivity mActivity;
private Request mRequest;
public AuthTask(AuthActivity activity, Request request) {
mActivity = activity;
mRequest = request;
}
@Override
protected Response doInBackground(Void... params) {
try {
String token = GoogleAuthUtil.getToken(mActivity, mRequest.getAccountName(),
mRequest.getScope());
return NetworkUtils.doPost(mActivity, mRequest, token);
} catch (GooglePlayServicesAvailabilityException e) {
// GooglePlayServices.apk is either old, disabled, or not present.
showErrorDialog(e.getConnectionStatusCode());
} catch (UserRecoverableAuthException e) {
// The application hasn't been authorized by the user for access to the
// scope. Launch an Activity for a result.
mActivity.startActivityForResult(e.getIntent(), AuthActivity.REQUEST_CODE_RECOVER_AUTH);
// TODO: ensure that this is only done once per authenticated request!
} catch (GoogleAuthException e) {
// Failure. The call is not expected to ever succeed so it should not be
// retried.
Log.e(TAG, "Unrecoverable error: " + e.getMessage());
} catch (IOException e) {
Log.e(TAG, "IOException: " + e.getMessage());
// Network or server error, the call is expected to succeed if you try
// again later.
// TODO: Implement a simple loop here that will retry the request
// using exponential backoff.
}
return null;
}
@Override
protected void onPostExecute(Response response) {
// TODO: Choose how your Activity should respond to response
// successes and response failures.
}
private void showErrorDialog(final int errorCode) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
mActivity.showErrorDialog(errorCode);
}
});
}
}
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import android.content.Context;
import com.google.android.gms.auth.GoogleAuthUtil;
public final class NetworkUtils {
public static Response doPost(Context ctx, Request request, String token) throws IOException {
URL url = request.getUrl();
Map<String, List<String>> headers = request.getHeaders();
Map<String, String> bodyParams = request.getBodyParams();
byte[] body = constructBody(bodyParams);
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) url.openConnection();
addHeaders(conn, token, headers);
conn.addRequestProperty("Authorization", "OAuth " + token);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setFixedLengthStreamingMode(body.length);
conn.setRequestMethod("POST");
// post the request
OutputStream out = conn.getOutputStream();
out.write(body);
out.close();
// handle the response
BufferedInputStream in = new BufferedInputStream(conn.getInputStream());
int status = conn.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
return new Response(status, conn.getHeaderFields(), readStream(in));
} else if (status == HttpURLConnection.HTTP_UNAUTHORIZED) {
// The token has expired. Invalidate and get a new one.
GoogleAuthUtil.invalidateToken(ctx, token);
return doPost(ctx, request, token);
} else {
BufferedInputStream err = new BufferedInputStream(conn.getErrorStream());
return new Response(status, conn.getHeaderFields(), readStream(err));
}
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
public static void addHeaders(HttpURLConnection conn, String token,
Map<String, List<String>> headers) {
if (headers != null) {
for (String header : headers.keySet()) {
for (String value : headers.get(header)) {
conn.addRequestProperty(header, value);
}
}
}
}
public static byte[] constructBody(Map<String, String> bodyParams) {
// Constructs the POST body using the parameters
StringBuilder bodyBuilder = new StringBuilder();
Iterator<Entry<String, String>> iterator = bodyParams.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, String> param = iterator.next();
bodyBuilder.append(param.getKey()).append('=').append(param.getValue());
if (iterator.hasNext()) {
bodyBuilder.append('&');
}
}
return bodyBuilder.toString().getBytes();
}
public static byte[] readStream(InputStream in) throws IOException {
byte[] buf = new byte[1024];
int count = 0;
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
while ((count = in.read(buf)) != -1) {
out.write(buf, 0, count);
}
in.close();
return out.toByteArray();
}
}
import java.net.URL;
import java.util.List;
import java.util.Map;
public class Request {
private URL mUrl;
private Map<String, List<String>> mHeaders;
private String mAccountName;
private Map<String, String> mBodyParams; // non-null if POST
private String mScope;
// private ResponseHandler handler;
public URL getUrl() {
return mUrl;
}
public Map<String, List<String>> getHeaders() {
return mHeaders;
}
public String getAccountName() {
return mAccountName;
}
public Map<String, String> getBodyParams() {
return mBodyParams;
}
public String getScope() {
return mScope;
}
private Request(URL url, Map<String, List<String>> headers, Map<String, String> bodyParams,
String accountName, String scope) {
mUrl = url;
mHeaders = headers;
mAccountName = accountName;
mBodyParams = bodyParams;
mScope = scope;
}
public static Request makeGet(URL url, Map<String, List<String>> headers, String accountName,
String scope) {
return new Request(url, headers, null, accountName, scope);
}
public static Request makePost(URL url, Map<String, List<String>> headers, String accountName,
Map<String, String> body, String scope) {
return new Request(url, headers, body, accountName, scope);
}
}
import java.util.List;
import java.util.Map;
public class Response {
private int mStatusCode;
private Map<String, List<String>> mHeaders;
private byte[] mBody;
public int getStatusCode() {
return mStatusCode;
}
public Map<String, List<String>> getHeaders() {
return mHeaders;
}
public byte[] getBody() {
return mBody;
}
public Response(int statusCode, Map<String, List<String>> headers, byte[] body) {
mStatusCode = statusCode;
mHeaders = headers;
mBody = body;
}
public Response(int statusCode, Map<String, List<String>> headers, String body) {
mStatusCode = statusCode;
mHeaders = headers;
mBody = body.getBytes();
}
}
@bsvTag
Copy link

bsvTag commented May 31, 2013

Why do you send the same invalid token after indicating its expiration in https://gist.github.com/alexjlockwood/4440089#file-networkutils-java-L49 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment