2016年6月14日 星期二

Facebook Android SDK in 10 Minutes - Login Part

I appreciate Facebook for providing a fancy Android SDK, prevent us from touching the messy OAuth2 authorization flow. However, the official documentation page is not fancy at all. Fragmented code snippets and unclear description statements just make beginners even more confused. So here is some quick start for beginners.
(From official document page)"If the access token is available already assign it" ??!

Scenario

Due to the internal design of SDK, integration of Facebook Login may depend on you app structure. In this article, I'm going to show one of the scenarios.
  1. User would see a splash Activity(an entry Activity) right after launching the app.
  2. The splash Activity would check the login information.
  3. If the user had login, jump to the main Activity, otherwise, jump to the login/register Activity. (Of course, the main Activity would use Facebook APIs).
This login flow is slightly different from that in official documentation page, which seems to leverage Fragment. Also, I simply use the LoginButton widget from SDK.
(Note: Facebook SDK version here is v4.12.1. I skip the setup steps which, unlike the development documents, is written pretty well in the official tutorial.)

Implementation

In the splash Activity(here we call "SplashActivity"), we have the following code snippet. 

public class SplashActivity extends AppCompatActivity {

    private Intent mLoginIntent;
    private Intent mMainIntent;

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

        mMainIntent = new Intent(this, MainActivity.class);
        mLoginIntent = new Intent(this, LoginActivity.class);

        FacebookSdk.sdkInitialize(getApplicationContext(), new FacebookSdk.InitializeCallback() {
             @Override
             public void onInitialized() {
                 checkFbLogin(AccessToken.getCurrentAccessToken());
             }
        });
        AppEventsLogger.activateApp(getApplication());

    }

    private void checkFbLogin(AccessToken currentToken){
        if(currentToken != null){
            startActivity(mMainIntent);
        }else{
            startActivity(mLoginIntent);
        }
        finish();
    }
}

FacebookSdk.sdkInitialize() must be called as early as you can. SplashActivity as previous mentioned, need to check the login information. Lots of people had complained that AccessToken.getCurrentAccessToken() didn't behave like the document said, which should return null if use hasn't login. The key is that the real "token loader" is FacebookSdk.sdkInitialize(), which is asynchronous, so you need to wait a little bit for the SDK initialization. Nevertheless, one should leverage the "on complete callback" of FacebookSdk.sdkInitialize() instead of pausing for a while by Thread.sleep() or Handler. Because the proper pausing interval may vary from device to device.

Here is the LoginActivity.

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

        if(!FacebookSdk.isInitialized()){
            FacebookSdk.sdkInitialize(getApplicationContext());
            AppEventsLogger.activateApp(getApplication());
        }

        setContentView(R.layout.activity_login);

        // Login button provided by SDK
        LoginButton butFbLogin = (LoginButton)findViewById(R.id.but_fb_login);
        assert butFbLogin != null;

        mLoginCallbackManager = CallbackManager.Factory.create();

        butFbLogin.setReadPermissions("email");
        butFbLogin.registerCallback(mLoginCallbackManager, new FacebookCallback<LoginResult>() {
            @Override
            public void onSuccess(LoginResult loginResult) {
                if(loginResult.getAccessToken() != null){
                    Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                }else{
                    Log.e(TAG, "Returned FB access token null");
                }
            }

            @Override
            public void onCancel() {
                //TODO
            }

            @Override
            public void onError(FacebookException error) {
                error.printStackTrace();
                Toast.makeText(LoginActivity.this, "Error Login to Facebook", Toast.LENGTH_LONG).show();
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data){
        super.onActivityResult(requestCode, resultCode, data);
        mLoginCallbackManager.onActivityResult(requestCode, resultCode, data);
    }

The login page is rather straight forward. Few things need to be careful: Facebook.sdkInitialized() must be called before setContentView(). It seems that parts of XML of widgets provided by SDK is modified or generated dynamically. Also, if a user revoke permission for your app from her account, AccessToken.getCurrentAccessToken() would still return non-null result. So you may need extra care during API calls.

沒有留言:

張貼留言