Search code examples
androidfacebookfacebook-loginfacebook-sdk-3.0

Why does calling Session.openActiveSession() from the Android Facebook SDK start a new Activity?


I've implemented the Facebook SDK (currently 3.5, but I started with 3.0) into an Android app. According to the Facebook best practices, I need to provide an option to log out. The problem is that the first time I log in it works, but not after logging out and trying to log in again.

My app has a BaseActivity that handles most of the shared code (including logins) and is extended by two classes: FacebookLoginActivity for the login UI and ViewActivity for displaying information after login.

This is the code that I'm using, edited slightly to remove irrelevant methods:
[BaseActivity]

package uk.co.cgfindies.wittylater;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;

import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.facebook.Session;
import com.facebook.SessionState;
import com.testflightapp.lib.TestFlight;

public class BaseActivity extends SherlockFragmentActivity {

    public static final int ACTIVITY_FACEBOOK_LOGIN = 0;
    public static final List<String> BASIC_PERMISSIONS = Arrays.asList("basic_info");
    public static final List<String> READ_PERMISSIONS = Arrays.asList("basic_info", "read_stream");
    public static final List<String> PUBLISH_PERMISSIONS = Arrays.asList("publish_actions");
    public static final String GENERIC_TAG = "WITTYLATER";
    public static final String PROFILE_PICTURE_CACHE_UNIQUE_NAME = "fb_profile_pictures";
    public static final int PROFILE_PICTURE_CACHE_SIZE = 1024 * 1024 * 1;
    public static final int DEFAULT_LIKES_REFRESH_INTERVAL_IN_MINUTES = 15;
    private static final String PENDING_PUBLISH_KEY = "pendingPublishReauthorization";
    private static final String FACEBOOK_LOGIN_TAG = "FACEBOOK_LOGIN_TAG";
    public static final int NOTIFICATION_POST_UPDATED = 1;
    public static final String ARGS_BOOLEAN_SHOW_MENU = "ARGS_NO_MENU";
    public static final String ARGS_BOOLEAN_FACEBOOK_LOGIN = "ARGS_BOOLEAN_FACEBOOK_LOGIN";

    private boolean facebookLogin = false;  // This will be true if the system is currently trying to log the user in.
    private boolean showMenu = true;
    protected static boolean pendingPublishReauthorization;
    private static String username = "";
    private Session.StatusCallback statusCallback = new SessionStatusCallback();
    private static SessionState currentState;

    private Context context;

  private class SessionStatusCallback implements Session.StatusCallback {
      @Override
      public void call(Session session, SessionState state, Exception exception) {
          onSessionStateChange(session, state, exception);
      }
  }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        init(savedInstanceState);
    }

  ...

    private void init(Bundle savedInstanceState) {
        Log.i(BaseActivity.GENERIC_TAG, "init: " + this.getClass().getName());

    Session session = Session.getActiveSession();
    if (session == null) {
        if (savedInstanceState != null) {
            session = Session.restoreSession(this, null, statusCallback, savedInstanceState);
        }
        if (session == null) {
            session = new Session(this);
        }
        Session.setActiveSession(session);
        if (session.getState().equals(SessionState.CREATED_TOKEN_LOADED)) {
            session.openForRead(new Session.OpenRequest(this).setCallback(statusCallback));
        }
    }

    if (!session.isOpened() && !(this instanceof FacebookLoginActivity)) {
        Log.i(BaseActivity.GENERIC_TAG, "Starting FacebookLoginActivity");
            Intent i = new Intent(this, FacebookLoginActivity.class);
            this.startActivity(i);
            this.finish();
            return;
    }

        if (!SyncWithFacebookService.isServiceRunning()) {
            Intent intent = new Intent(this, SyncWithFacebookService.class);
            startService(intent);
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        Session.getActiveSession().addCallback(statusCallback);
    }

    @Override
    public void onStop() {
        super.onStop();
        Session.getActiveSession().removeCallback(statusCallback);
    }

    @Override
    public void onResume() {
        Log.i(BaseActivity.GENERIC_TAG, this.getClass().getName() + " onResume()");
        super.onResume();
        Session session = Session.getActiveSession();
        onSessionStateChange(session, session.getState(), null);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.i(BaseActivity.GENERIC_TAG, this.getClass().getName() + " onActivityResult request: " + requestCode);
        super.onActivityResult(requestCode, resultCode, data);
        Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        Log.i(BaseActivity.GENERIC_TAG, this.getClass().getName() + " onSaveInstanceState()");
        super.onSaveInstanceState(outState);
        Session session = Session.getActiveSession();
    Session.saveSession(session, outState);
    }

    protected void onSessionStateChange(Session session, SessionState state, Exception exception) {
        Log.i(BaseActivity.GENERIC_TAG, "onSessionStateChange called from " + this.getClass().getName());
        Log.i(BaseActivity.GENERIC_TAG, "current state is " + ((currentState == null) ? "null" : currentState.toString()));
        Log.i(BaseActivity.GENERIC_TAG, "state is " + state.toString());

        if (state.equals(currentState)) {
            Log.i(BaseActivity.GENERIC_TAG, "state hasn't changed, returning.");
            return;
        }

        currentState = state;

        if (exception != null) {
            Log.d(BaseActivity.GENERIC_TAG, exception.getMessage());
            TestFlight.log(exception.getMessage());
        }

        if (state.isOpened()) {
            Log.i(BaseActivity.GENERIC_TAG, "Starting FetchFacebookDataActivity");
            Intent i = new Intent(this, FetchFacebookDataActivity.class);
            i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(i);
            finish();
        }
    }

    public void startLogin(View v) {
        Log.i(BaseActivity.GENERIC_TAG, this.getClass().getName() + " startLogin()");
        Session session = Session.getActiveSession();

        if (session != null && !session.isClosed()) {
            Log.i(BaseActivity.GENERIC_TAG, "Closing Session, clearing tokens");
            session.closeAndClearTokenInformation();
        }

        if (!session.getState().isOpened() && !session.getState().isClosed()) {
            Log.i(BaseActivity.GENERIC_TAG, "Session state is " + session.getState().toString() + ", opening for read.");
            session.openForRead(new Session.OpenRequest(this).setCallback(statusCallback));
        } else {
            Log.i(BaseActivity.GENERIC_TAG, "Opening new active session");
            Session.openActiveSession(this, true, statusCallback);
        }

    }

    public void facebookLogout() {
        Log.i(BaseActivity.GENERIC_TAG, "Logging out.");
        Session session = Session.getActiveSession();
        if (!session.isClosed()) {
            Log.i(BaseActivity.GENERIC_TAG, "Clearing Facebook tokens.");
            session.closeAndClearTokenInformation();
        }

        Log.i(BaseActivity.GENERIC_TAG, "Starting FacebookLoginActivity");
        startNewActivity(FacebookLoginActivity.class, true, true);
        finish();
        return;
    }

    protected void startNewActivity(Class<?> cls, boolean clearTop, boolean finish) {
        Log.i(BaseActivity.GENERIC_TAG, "Current class is " + this.getClass().getName());
        Log.i(BaseActivity.GENERIC_TAG, "Target class is " + cls.getName());
        Log.i(BaseActivity.GENERIC_TAG, Boolean.toString(cls.isInstance(this)));
        Log.i(BaseActivity.GENERIC_TAG, Boolean.toString(cls.isInstance(this.getClass())));
        Log.i(BaseActivity.GENERIC_TAG, Boolean.toString(this.getClass().isInstance(cls)));

        if (cls.isInstance(this)) {
            Log.i(BaseActivity.GENERIC_TAG, "Already instance of " + cls.getName());
            return;     // Already in that activity.
        }

        Log.i(BaseActivity.GENERIC_TAG, "Starting class " + cls.getName());
        Intent i = new Intent(this, ViewActivity.class);
        i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(i);
        finish();
    }
}

[FacebookLoginActivity]

package uk.co.cgfindies.wittylater;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;

public class FacebookLoginActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.i(BaseActivity.GENERIC_TAG, this.getClass().getName() + " onCreate()");
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_facebook_login);

        FacebookLoginFragment flf = new FacebookLoginFragment();
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.facebook_login_container, flf);
        fragmentTransaction.commit();
    }
}

[FacebookLoginFragment]

package uk.co.cgfindies.wittylater;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.actionbarsherlock.app.SherlockFragment;

public class FacebookLoginFragment extends SherlockFragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.i(BaseActivity.GENERIC_TAG, this.getClass().getName() + " onCreateView()");
        super.onCreateView(inflater, container, savedInstanceState);
        View v = inflater.inflate(R.layout.fragment_facebook_login, container, false);
        return v;
    }
}

[ViewActivity] package uk.co.cgfindies.wittylater;

import uk.co.cgfindies.wittylater.ViewPostedFragment.ViewPostedFragment_onClickListeners;
import uk.co.cgfindies.wittylater.ViewUnpostedFragment.ViewUnpostedFragment_onClickListeners;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.View;

import com.actionbarsherlock.view.Menu;

public class ViewActivity extends BaseActivity implements ViewUnpostedFragment_onClickListeners, ViewPostedFragment_onClickListeners {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view);
        ...
    }
}

As far as I can tell, the seconds time it tries to log in it gets to call Session.openActiveSession(), which triggers BaseActivity.onSessionStateChange(), and then an intent is started and ViewActivity.onCreate() is called. This means that the activity result from the login doesn't have anywhere to go. I've been trying everything I can think of for about 20 hours now, and I can't work out what is going wrong.

Why is ViewActivity being created suddenly? It may be because it is set as the launcher activity, or it may be because it is listed as the app Class Name in the Facebook App Settings, but other than that I have no idea.


Solution

  • I finally figured this out, I was looking in completely the wrong place.

    In another class I was using the Facebook UiLifecycleHelper class but I forgot to implement some of the Android lifecycle methods such as onPause and onDestroy.
    This meant that the UiLifecycleHelper was still active and causing all sorts of issues. Once that was fixed, the login / logout works perfectly.