Android Open Source - Navigation-drawer-a-la-Google Login And Auth Helper






From Project

Back to project page Navigation-drawer-a-la-Google.

License

The source code is released under:

Apache License

If you think the Android project Navigation-drawer-a-la-Google listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2014 Google Inc. All rights reserved.
 *//  w  w w .  java 2  s  .  co m
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.samples.apps.iosched.util;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;

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;
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.Scope;
import com.google.android.gms.plus.Plus;

import java.io.IOException;
import java.lang.ref.WeakReference;

/**
 * This helper handles the UI flow for signing in and authenticating an account. It handles
 * connecting to the Google+ API to fetch profile data (name, cover photo, etc) and
 * also getting the auth token for the necessary scopes. The life of this object is
 * tied to an Activity. Do not attempt to share it across Activities, as unhappiness will
 * result.
 */
public class LoginAndAuthHelper implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
    // Auth scopes we need
    public static final String AUTH_SCOPES[] = {
            Scopes.PLUS_LOGIN,
            Scopes.DRIVE_APPFOLDER,
            "https://www.googleapis.com/auth/plus.profile.emails.read"};

    static final String AUTH_TOKEN_TYPE;
    private static final String TAG = LoginAndAuthHelper.class.getSimpleName();

    private static final int REQUEST_AUTHENTICATE = 100;
    private static final int REQUEST_RECOVER_FROM_AUTH_ERROR = 101;
    private static final int REQUEST_RECOVER_FROM_PLAY_SERVICES_ERROR = 102;
    private static final int REQUEST_PLAY_SERVICES_ERROR_DIALOG = 103;

    // Controls whether or not we can show sign-in UI. Starts as true;
    // when sign-in *fails*, we will show the UI only once and set this flag to false.
    // After that, we don't attempt again in order not to annoy the user.
    private static boolean sCanShowSignInUi = true;
    private static boolean sCanShowAuthUi = true;

    // Are we in the started state? Started state is between onStart and onStop.
    boolean mStarted = false;

    // True if we are currently showing UIs to resolve a connection error.
    boolean mResolving = false;

    // The Activity this object is bound to (we use a weak ref to avoid context leaks)
    WeakReference<Activity> mActivityRef;

    // Callbacks interface we invoke to notify the user of this class of useful events
    WeakReference<Callbacks> mCallbacksRef;

    // Name of the account to log in as (e.g. "foo@example.com")
    String mAccountName;
    Context mAppContext;

    // API client to interact with Google services
    private GoogleApiClient mGoogleApiClient;
    private GetTokenTask mTokenTask;

    public LoginAndAuthHelper(Activity activity, Callbacks callbacks, String accountName) {
        mActivityRef = new WeakReference<Activity>(activity);
        mCallbacksRef = new WeakReference<Callbacks>(callbacks);
        mAppContext = activity.getApplicationContext();
        mAccountName = accountName;
        if (PrefUtils.hasUserRefusedSignIn(activity)) {
            // If we know the user refused sign-in, let's not annoy them.
            sCanShowSignInUi = sCanShowAuthUi = false;
        }
    }

    /**
     * Handles an Activity result. Call this from your Activity's onActivityResult().
     */
    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
        Activity activity = getActivity();
        if (activity == null) {
            return false;
        }

        if (requestCode == REQUEST_AUTHENTICATE ||
                requestCode == REQUEST_RECOVER_FROM_AUTH_ERROR ||
                requestCode == REQUEST_PLAY_SERVICES_ERROR_DIALOG) {

            Log.d(TAG, "onActivityResult, req=" + requestCode + ", result=" + resultCode);
            if (requestCode == REQUEST_RECOVER_FROM_PLAY_SERVICES_ERROR) {
                mResolving = false;
            }

            if (resultCode == Activity.RESULT_OK) {
                if (mGoogleApiClient != null) {
                    Log.d(TAG, "Since activity result was RESULT_OK, reconnecting client.");
                    mGoogleApiClient.connect();
                } else {
                    Log.d(TAG, "Activity result was RESULT_OK, but we have no client to reconnect.");
                }
            } else if (resultCode == Activity.RESULT_CANCELED) {
                Log.d(TAG, "User explicitly cancelled sign-in/auth flow.");
                // save this as a preference so we don't annoy the user again
                PrefUtils.markUserRefusedSignIn(mAppContext);
            } else {
                Log.w(TAG, "Failed to recover from a login/auth failure, resultCode=" + resultCode);
            }
            return true;
        }
        return false;
    }

    private Activity getActivity() {
        Activity activity = mActivityRef.get();
        return activity;
    }

    public String getAccountName() {
        return mAccountName;
    }

    public boolean isStarted() {
        return mStarted;
    }

    /**
     * Stop the helper. Call this from your Activity's onStop().
     */
    public void stop() {
        if (!mStarted) {
            Log.w(TAG, "Helper already stopped. Ignoring redundant call.");
            return;
        }

        Log.d(TAG, "Helper stopping.");
        if (mTokenTask != null) {
            Log.d(TAG, "Helper cancelling token task.");
            mTokenTask.cancel(false);
        }
        mStarted = false;
        if (mGoogleApiClient.isConnected()) {
            Log.d(TAG, "Helper disconnecting client.");
            mGoogleApiClient.disconnect();
        }
        mResolving = false;
    }

    /**
     * Starts the helper. Call this from your Activity's onStart().
     */
    public void start() {
        Activity activity = getActivity();
        if (activity == null) {
            return;
        }

        if (mStarted) {
            Log.w(TAG, "Helper already started. Ignoring redundant call.");
            return;
        }

        mStarted = true;
        if (mResolving) {
            // if resolving, don't reconnect the plus client
            Log.d(TAG, "Helper ignoring signal to start because we're resolving a failure.");
            return;
        }
        Log.d(TAG, "Helper starting. Connecting " + mAccountName);
        if (mGoogleApiClient == null) {
            Log.d(TAG, "Creating client.");

            GoogleApiClient.Builder builder = new GoogleApiClient.Builder(activity);
            for (String scope : AUTH_SCOPES) {
                builder.addScope(new Scope(scope));
            }
            mGoogleApiClient = builder.addApi(Plus.API)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .setAccountName(mAccountName)
                    .build();
        }
        Log.d(TAG, "Connecting client.");
        mGoogleApiClient.connect();
    }

    // Called when the Google+ client is connected.
    @Override
    public void onConnected(Bundle bundle) {
        Activity activity = getActivity();
        if (activity == null) {
            return;
        }

        Log.d(TAG, "Helper connected, account " + mAccountName);

        // try to authenticate, if we don't have a token yet
        if (!AccountUtils.hasToken(activity, mAccountName)) {
            Log.d(TAG, "We don't have auth token for " + mAccountName + " yet, so getting it.");
            mTokenTask = new GetTokenTask();
            mTokenTask.execute();
        } else {
            Log.d(TAG, "No need for auth token, we already have it.");
            reportAuthSuccess(false);
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.d(TAG, "onConnectionSuspended.");
    }

    private void reportAuthSuccess(boolean newlyAuthenticated) {
        Log.d(TAG, "Auth success for account " + mAccountName + ", newlyAuthenticated=" + newlyAuthenticated);
        Callbacks callbacks;
        if (null != (callbacks = mCallbacksRef.get())) {
            callbacks.onAuthSuccess(mAccountName, newlyAuthenticated);
        }
    }

    // Called when the connection to Google Play Services fails.
    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Activity activity = getActivity();
        if (activity == null) {
            return;
        }

        if (connectionResult.hasResolution()) {
            if (sCanShowSignInUi) {
                Log.d(TAG, "onConnectionFailed, with resolution. Attempting to resolve.");
                sCanShowSignInUi = false;
                try {
                    mResolving = true;
                    connectionResult.startResolutionForResult(activity,
                            REQUEST_RECOVER_FROM_PLAY_SERVICES_ERROR);
                } catch (IntentSender.SendIntentException e) {
                    Log.e(TAG, "SendIntentException occurred: " + e.getMessage());
                    e.printStackTrace();
                }
            } else {
                Log.d(TAG, "onConnectionFailed with resolution but sCanShowSignInUi==false.");
                reportAuthFailure();
            }
            return;
        }

        Log.d(TAG, "onConnectionFailed, no resolution.");
        final int errorCode = connectionResult.getErrorCode();
        if (GooglePlayServicesUtil.isUserRecoverableError(errorCode) && sCanShowSignInUi) {
            sCanShowSignInUi = false;
            GooglePlayServicesUtil.getErrorDialog(errorCode, activity,
                    REQUEST_PLAY_SERVICES_ERROR_DIALOG).show();
        } else {
            reportAuthFailure();
        }
    }

    private void reportAuthFailure() {
        Log.d(TAG, "Auth FAILURE for account " + mAccountName);
        Callbacks callbacks;
        if (null != (callbacks = mCallbacksRef.get())) {
            callbacks.onAuthFailure(mAccountName);
        }
    }

    private void showRecoveryDialog(int statusCode) {
        Activity activity = getActivity();
        if (activity == null) {
            return;
        }

        if (sCanShowAuthUi) {
            sCanShowAuthUi = false;
            Log.d(TAG, "Showing recovery dialog for status code " + statusCode);
            final Dialog d = GooglePlayServicesUtil.getErrorDialog(
                    statusCode, activity, REQUEST_RECOVER_FROM_PLAY_SERVICES_ERROR);
            d.show();
        } else {
            Log.d(TAG, "Not showing Play Services recovery dialog because sCanShowSignInUi==false.");
            reportAuthFailure();
        }
    }

    private void showAuthRecoveryFlow(Intent intent) {
        Activity activity = getActivity();
        if (activity == null) {
            return;
        }

        if (sCanShowAuthUi) {
            sCanShowAuthUi = false;
            Log.d(TAG, "Starting auth recovery Intent.");
            activity.startActivityForResult(intent, REQUEST_RECOVER_FROM_AUTH_ERROR);
        } else {
            Log.d(TAG, "Not showing auth recovery flow because sCanShowSignInUi==false.");
            reportAuthFailure();
        }
    }

    public interface Callbacks {
        void onPlusInfoLoaded(String accountName);

        void onAuthSuccess(String accountName, boolean newlyAuthenticated);

        void onAuthFailure(String accountName);
    }

    static {
        StringBuilder sb = new StringBuilder();
        sb.append("oauth2:");
        for (String scope : AUTH_SCOPES) {
            sb.append(scope);
            sb.append(" ");
        }
        AUTH_TOKEN_TYPE = sb.toString();
    }

    /**
     * Async task that obtains the auth token.
     */
    private class GetTokenTask extends AsyncTask<Void, Void, String> {
        public GetTokenTask() {
        }

        @Override
        protected String doInBackground(Void... params) {
            try {
                if (isCancelled()) {
                    Log.d(TAG, "doInBackground: task cancelled, so giving up on auth.");
                    return null;
                }

                Log.d(TAG, "Starting background auth for " + mAccountName);
                final String token = GoogleAuthUtil.getToken(mAppContext, mAccountName, AUTH_TOKEN_TYPE);

                // Save auth token.
                Log.d(TAG, "Saving token: " + (token == null ? "(null)" : "(length " +
                        token.length() + ")") + " for account " + mAccountName);
                AccountUtils.setAuthToken(mAppContext, mAccountName, token);
                return token;
            } catch (GooglePlayServicesAvailabilityException e) {
                postShowRecoveryDialog(e.getConnectionStatusCode());
            } catch (UserRecoverableAuthException e) {
                postShowAuthRecoveryFlow(e.getIntent());
            } catch (IOException e) {
                Log.e(TAG, "IOException encountered: " + e.getMessage());
            } catch (GoogleAuthException e) {
                Log.e(TAG, "GoogleAuthException encountered: " + e.getMessage());
            } catch (RuntimeException e) {
                Log.e(TAG, "RuntimeException encountered: " + e.getMessage());
            }
            return null;
        }

        @Override
        protected void onPostExecute(String token) {
            super.onPostExecute(token);

            if (isCancelled()) {
                Log.d(TAG, "Task cancelled, so not reporting auth success.");
            } else if (!mStarted) {
                Log.d(TAG, "Activity not started, so not reporting auth success.");
            } else {
                Log.d(TAG, "GetTokenTask reporting auth success.");
                reportAuthSuccess(true);
            }
        }

        private void postShowRecoveryDialog(final int statusCode) {
            Activity activity = getActivity();
            if (activity == null) {
                return;
            }

            if (isCancelled()) {
                Log.d(TAG, "Task cancelled, so not showing recovery dialog.");
                return;
            }

            Log.d(TAG, "Requesting display of recovery dialog for status code " + statusCode);
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (mStarted) {
                        showRecoveryDialog(statusCode);
                    } else {
                        Log.e(TAG, "Activity not started, so not showing recovery dialog.");
                    }
                }
            });
        }

        private void postShowAuthRecoveryFlow(final Intent intent) {
            Activity activity = getActivity();
            if (activity == null) {
                return;
            }

            if (isCancelled()) {
                Log.d(TAG, "Task cancelled, so not showing auth recovery flow.");
                return;
            }

            Log.d(TAG, "Requesting display of auth recovery flow.");
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (mStarted) {
                        showAuthRecoveryFlow(intent);
                    } else {
                        Log.e(TAG, "Activity not started, so not showing auth recovery flow.");
                    }
                }
            });
        }
    }
}




Java Source Code List

com.google.samples.apps.iosched.ui.widget.BezelImageView.java
com.google.samples.apps.iosched.ui.widget.ScrimInsetsScrollView.java
com.google.samples.apps.iosched.util.AccountUtils.java
com.google.samples.apps.iosched.util.LoginAndAuthHelper.java
com.google.samples.apps.iosched.util.PrefUtils.java
com.google.samples.apps.iosched.util.UIUtils.java
info.korzeniowski.activitydrawersample.BaseDrawerActivity.java
info.korzeniowski.activitydrawersample.FirstDrawerActivity.java
info.korzeniowski.activitydrawersample.SecondDrawerActivity.java
info.korzeniowski.activitydrawersample.SettingsActivity.java
info.korzeniowski.activitydrawersample.SimpleFragment.java
info.korzeniowski.activitydrawersample.ThirdDrawerActivity.java