com.google.plus.wigwamnow.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.google.plus.wigwamnow.MainActivity.java

Source

/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * 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.plus.wigwamnow;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.plus.PlusClient;
import com.google.android.gms.plus.PlusClient.OnAccessRevokedListener;
import com.google.plus.wigwamnow.models.Wigwam;
import com.google.plus.wigwamnow.social.FacebookProvider;
import com.google.plus.wigwamnow.social.GoogleProvider;
import com.google.plus.wigwamnow.social.PlusAuthActivity;
import com.google.plus.wigwamnow.social.PlusClientFragment;
import com.google.plus.wigwamnow.social.PlusClientFragment.OnSignInListener;
import com.google.plus.wigwamnow.social.PlusClientHostActivity;
import com.google.plus.wigwamnow.social.SocialProviderConstants;
import com.google.plus.wigwamnow.social.SocialProvider;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

import com.facebook.Session;
import com.facebook.SessionState;
import com.facebook.UiLifecycleHelper;
import com.facebook.widget.UserSettingsFragment;

/**
 * MainActivity class for the WigwamNow app. Hosts fragments for the login screen, settings screen,
 * and main wigwam selection screen.
 *
 * @author samstern
 */
public class MainActivity extends FragmentActivity
        implements OnSignInListener, PlusClientHostActivity, PlusAuthActivity {

    public static final String TAG = MainActivity.class.getSimpleName();

    /** Fragment index for the {@link SplashFragment} **/
    private static final int SPLASH = 0;

    /** Fragment index for the {@link SelectionFragment} **/
    private static final int SELECTION = 1;

    /** Fragment index for the Facebook {@link UserSettingsFragment} **/
    private static final int FB_SETTINGS = 2;

    /** Fragment index for the {@link PlusSettingsFragment} **/
    private static final int GPLUS_SETTINGS = 3;

    /** Count of the total number of {@link Fragment}s hosted **/
    private static final int FRAGMENT_COUNT = GPLUS_SETTINGS + 1;

    /** Request code for request identification the {@link PlusClientFragment} **/
    private static final int REQUEST_CODE_RESOLVE_ERR = 9000;

    /** Key prefix for storing hybrid auth status in {@link SharedPreferences} **/
    private static final String KEY_CODE_SENT = "CODE_SENT";

    /** Key for the last {@link SocialProviderConstants} stored in the {@link SharedPreferences} **/
    private static final String KEY_LAST_PROVIDER = "LAST_PROVIDER";

    /** Key for passing a {@link Wigwam} in a {@link Bundle} **/
    protected static final String EXTRA_WIGWAM = "wigwam";

    /** Key for passing a {@link SocialProviderConstants} in a {@link Bundle} **/
    protected static final String EXTRA_PROVIDER = "provider";

    /** Boolean determining if the {@link Activity} was resumed through {@link #onResume} **/
    private boolean mIsResumed = false;

    /** Array of {@link Fragment}s hosted by this activity **/
    private Fragment[] mFragments = new Fragment[FRAGMENT_COUNT];

    /** Helper to manage lifecycle of Facebook (@link Session} **/
    private UiLifecycleHelper mUiHelper;

    /** Invisible Fragment to manage {@link PlusClient} state **/
    private PlusClientFragment mPlusFragment;

    /** The last {@link SocialProviderConstants} stored in {@link SharedPreferences} **/
    private int mLastProvider;

    /**
     * Handler for change in Facebook Session Status
     */
    private Session.StatusCallback mFacebookStatusCallback = new Session.StatusCallback() {
        @Override
        public void call(Session session, SessionState state, Exception exception) {
            onSessionStateChange(session, state, exception);
        }
    };

    /** id of the Settings option in the options menu, when applicable **/
    private int mSettingsId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Track Facebook Session Status
        mUiHelper = new UiLifecycleHelper(this, mFacebookStatusCallback);
        mUiHelper.onCreate(savedInstanceState);

        // Link to PlusClientFragment
        mPlusFragment = PlusClientFragment.getPlusClientFragment(this,
                getResources().getStringArray(R.array.plus_scopes),
                getResources().getStringArray(R.array.visible_activities));

        // Reset the last known SocialProviderConstants
        mLastProvider = SocialProviderConstants.NONE;

        // Instantiate the four main fragments
        FragmentManager fm = getSupportFragmentManager();
        mFragments[SPLASH] = fm.findFragmentById(R.id.splashFragment);
        mFragments[SELECTION] = fm.findFragmentById(R.id.selectionFragment);
        mFragments[FB_SETTINGS] = fm.findFragmentById(R.id.fbSettingsFragment);
        mFragments[GPLUS_SETTINGS] = fm.findFragmentById(R.id.gplusSettingsFragment);

        // Hide all of the fragments, to start
        FragmentTransaction transaction = fm.beginTransaction();
        for (Fragment fragment : mFragments) {
            transaction.hide(fragment);
        }
        transaction.commit();
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        // If the user is signed in with any SocialProvider, display the Settings option in the
        // options menu, otherwise clear the menu.
        switch (currentProvider()) {
        case SocialProviderConstants.NONE:
            return false;
        default:
            if (menu.size() == 0) {
                mSettingsId = menu.add(R.string.settings).getItemId();
            }
            return true;
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // When the Settings option is selected, show the settings Fragment for the SocialProvider.
        if (item.getItemId() == mSettingsId) {
            if (currentProvider() == SocialProviderConstants.FACEBOOK) {
                showFragment(FB_SETTINGS, true);
            } else if (currentProvider() == SocialProviderConstants.GOOGLE) {
                showFragment(GPLUS_SETTINGS, true);
            }
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onResume() {
        super.onResume();
        mUiHelper.onResume();
        mIsResumed = true;
        // Restore the last known SocialProviderConstants
        SharedPreferences sharedPreferences = getPreferences(MODE_PRIVATE);
        mLastProvider = sharedPreferences.getInt(KEY_LAST_PROVIDER, SocialProviderConstants.NONE);
        // Parse deep link
        SelectionFragment selection = (SelectionFragment) mFragments[SELECTION];
        selection.parseDeepLink();
    }

    @Override
    public void onPause() {
        super.onPause();
        mUiHelper.onPause();
        mIsResumed = false;
        // Save the provider
        saveProvider(currentProvider());
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        mUiHelper.onActivityResult(requestCode, resultCode, data);
        // Delegate to the Google Plus Fragment
        mPlusFragment.handleOnActivityResult(requestCode, resultCode, data);
        // Initiate Google hybrid authorization flow when successfully signed in
        if (requestCode == GoogleProvider.REQUEST_CODE_TOKEN_AUTH && resultCode == RESULT_OK) {
            sendGoogleAuthToServer();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mUiHelper.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mUiHelper.onSaveInstanceState(outState);
    }

    @Override
    protected void onResumeFragments() {
        super.onResumeFragments();
        if (currentProvider() != SocialProviderConstants.NONE) {
            // User is logged in
            showFragment(SELECTION, true);
        } else if (mLastProvider != SocialProviderConstants.NONE) {
            // User is navigating back to this, was logged in when he/she left
            showFragment(SELECTION, true);
        } else {
            // User is not logged in, show the splash screen and request login
            showFragment(SPLASH, false);
        }
    }

    /**
     * Save the last social login {@link SocialProviderConstants} used.
     * 
     * @param provider the provider (see {@link SocialProviderConstants}) which the user chose.
     */
    private void saveProvider(int provider) {
        saveInt(KEY_LAST_PROVIDER, provider);
    }

    /**
     * Save an integer to the {@link SharedPreferences}
     * 
     * @param key the String key that will be used to retrieve the integer.
     * @param val the integer value to save.
     */
    private void saveInt(String key, int val) {
        SharedPreferences.Editor editor = getPreferences(MODE_PRIVATE).edit();
        editor.putInt(key, val);
        editor.commit();
    }

    /**
     * Show the {@link SplashFragment}, prompting the user to log in again.
     */
    protected void showLoginFragment() {
        showFragment(SPLASH, false);
    }

    /**
     * Show a given fragment and hide all others.
     *
     * @param fragmentIndex index of the fragment to show such as {@code SPLASH}.
     * @param addToBackStack boolean to control adding to back stack.
     */
    private void showFragment(int fragmentIndex, boolean addToBackStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction transaction = fm.beginTransaction();
        if (fragmentIndex >= mFragments.length || fragmentIndex < 0) {
            // Invalid fragment index, take no action
            Log.w(TAG, "Tried to display invalid fragment " + Integer.toString(fragmentIndex));
            return;
        }
        // Hide all other fragments
        for (Fragment frag : mFragments) {
            if (frag == mFragments[fragmentIndex]) {
                transaction.show(frag);
            } else {
                transaction.hide(frag);
            }
        }
        if (addToBackStack) {
            transaction.addToBackStack(null);
        }
        transaction.commit();
        // Update the options menu in the Action Bar (if on JellyBean or above)
        supportInvalidateOptionsMenu();
    }

    /**
     * Callback when the state of the Facebook Session changes.
     *
     * @param session the session which changed.
     * @param state the current state of the session.
     * @param exception any exception which occurred.
     */
    private void onSessionStateChange(Session session, SessionState state, Exception exception) {
        // Only make changes if the activity is visible
        if (mIsResumed) {
            FragmentManager manager = getSupportFragmentManager();
            // Get the number of entries in the back stack
            int backStackSize = manager.getBackStackEntryCount();
            // Clear the back stack
            for (int i = 0; i < backStackSize; i++) {
                manager.popBackStack();
            }
            if (state.isOpened()) {
                // Send the authorization information to the web server
                sendFbTokenToServer();
                // Show the authenticated fragment
                showFragment(SELECTION, false);
            } else if (state.isClosed()) {
                // Show the login fragment
                saveProvider(SocialProviderConstants.NONE);
                showFragment(SPLASH, false);
            }
        }
    }

    /**
     * Get the current social identity {@link SocialProviderConstants}.
     * 
     * @return the int from {@link SocialProviderConstants} that the user used to log in.
     * <code>SocialProviderConstants.NONE</code> if the user is not currently authenticated.
     */
    public int currentProvider() {
        // TODO(samstern): Refactor to return Social SocialProviderConstants
        if (mPlusFragment.getClient().isConnected()) {
            return SocialProviderConstants.GOOGLE;
        } else if (Session.getActiveSession() != null && Session.getActiveSession().isOpened()) {
            return SocialProviderConstants.FACEBOOK;
        } else {
            return SocialProviderConstants.NONE;
        }
    }

    @Override
    public void signIn() {
        mPlusFragment.signIn(REQUEST_CODE_RESOLVE_ERR);
    }

    @Override
    public void signOut() {
        mPlusFragment.signOut();
        showFragment(SPLASH, false);
    }

    @Override
    public void disconnect() {
        mPlusFragment.disconnect(new OnAccessRevokedListener() {
            @Override
            public void onAccessRevoked(ConnectionResult status) {
                // Additional disconnect logic here
            }
        });
        recordCodeSent(SocialProviderConstants.GOOGLE, 0);
        showFragment(SPLASH, false);
    }

    @Override
    public PlusClient getPlusClient() {
        return mPlusFragment.getClient();
    }

    /**
     * Retrieve authorization information from the {@link SocialProviderConstants} and send it 
     * to the server.
     */
    protected void sendGoogleAuthToServer() {
        // TODO(samstern): Call this method BEFORE the first connect, not after.
        // TODO(samstern): Refactor into single hybrid auth method
        if (!authSentToServer(SocialProviderConstants.GOOGLE)) {
            SocialProvider google = new GoogleProvider();
            google.hybridAuth(this);
        }
    }

    /**
     * Send the access_token from the current Facebook {@link Session} to the server.
     */
    private void sendFbTokenToServer() {
        SocialProvider facebook = new FacebookProvider();
        facebook.hybridAuth(this);
    }

    /**
     * Called when a {@link Wigwam} is selected in {@link SelectionFragment}
     *
     * @param wigwam the {@link Wigwam} which was selected from the list.
     */
    protected void wigwamSelected(Wigwam wigwam) {
        newIntent(wigwam);
    }

    /**
     * Create an intent for the {@link WigwamDetailActivity} for a {@link Wigwam}.
     * 
     * @param wigwam the selected {@link Wigwam}
     */
    private void newIntent(Wigwam wigwam) {
        Intent intent = new Intent(this, WigwamDetailActivity.class);
        intent.putExtra(EXTRA_WIGWAM, (Parcelable) wigwam);
        intent.putExtra(EXTRA_PROVIDER, currentProvider());

        startActivity(intent);
    }

    /**
     * Check {@link SharedPreferences} to see if authorization has already been passed to the server
     * for the current user.
     * 
     * @param provider the social provider {@link SocialProviderConstants} for the current user.
     * @return true if authorization was previously sent, false otherwise.
     */
    private boolean authSentToServer(int provider) {
        SharedPreferences sharedPreferences = getPreferences(MODE_PRIVATE);
        int codeSent = sharedPreferences.getInt(KEY_CODE_SENT + Integer.toString(provider), 0);

        return codeSent == 1;
    }

    /**
     * Record whether the authorization information has already been sent to the server or if
     * it should be sent again on next log in.
     * 
     * @param provider the {@link SocialProviderConstants} for which the code was/wasn't sent.
     * @param status 1 if the code was sent, 0 if it needs to be set next time.
     */
    public void recordCodeSent(int provider, int status) {
        saveInt(KEY_CODE_SENT + Integer.toString(provider), status);
    }

    @Override
    public void onSignedIn(PlusClient plusClient) {
        // Load profile information
        plusClient.loadPerson((SelectionFragment) mFragments[SELECTION], "me");
        if (!authSentToServer(SocialProviderConstants.GOOGLE)) {
            // Get and send the code to the server, but only if the user hasn't done this
            // since the last disconnect
            Log.d(TAG, "SENDING AUTHORIZATION");
            sendGoogleAuthToServer();
        }
        // Show the selection fragment
        showFragment(SELECTION, true);
    }

    @Override
    public void onSignInFailed() {
        saveProvider(SocialProviderConstants.NONE);
    }
}