For some reason Profile.getCurrentProfile()
shows up null at times right after logging into FaceBook, using FB API v4.0.
This is causing problems for me
I have a simpler suggestion than the other answers here.
There is no need to register a login callback with:
registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() { ... }
Instead, whenever you need user data (like on button click) - start tracking profile changes with:
new ProfileTracker() {
@Override
protected void onCurrentProfileChanged(Profile oldProfile, Profile profile) {
stopTracking();
Log.d(TAG, profile.getFirstName());
Log.d(TAG, profile.getLastName());
Log.d(TAG, String.format("https://graph.facebook.com/%s/picture?type=large", profile.getId()));
}
}.startTracking();
And then start the Facebook login process by:
LoginManager.getInstance().logInWithReadPermissions(getContext(), Arrays.asList("public_profile"));
For the above code to work you of course need to prepare the usual stuff in your Activity:
private CallbackManager mCallbackManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mCallbackManager = CallbackManager.Factory.create();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (FacebookSdk.isFacebookRequestCode(requestCode) &&
mCallbackManager.onActivityResult(requestCode, resultCode, data)) {
// do nothing
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
Also do not forget to modify the AndroidManifest.xml file.
Facebook loads the profile information asynchronously, so even after you get your result from the login callback, Profile.getCurrentProfile() will return null.
However, every time the user logs in through Facebook (the first time and every subsequent time) his profile will change and will fire the profiletracker. This is where you must call the profile.
Here is how you should structure your code. You must listen for the ProfileTracker to update in order to update your user properties - avoid calling getCurrentProfile outside the tracker during the login process:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FacebookSdk.sdkInitialize(getApplicationContext());
callbackManager = CallbackManager.Factory.create();
profileTracker = new ProfileTracker() {
@Override
protected void onCurrentProfileChanged(Profile profile, Profile profile1) {
//Listen for changes to the profile or for a new profile: update your
//user data, and launch the main activity afterwards. If my user has just logged in,
//I make sure to update his information before launching the main Activity.
Log.i("FB Profile Changed", profile1.getId());
updateUserAndLaunch(LoginActivity.this);
}
};
profileTracker.startTracking();
accessTokenTracker = new AccessTokenTracker() {
@Override
protected void onCurrentAccessTokenChanged(
AccessToken oldAccessToken,
AccessToken currentAccessToken) {
//Check that the new token is valid. This tracker is fired
//when the user logs in the first time and afterwards any time he interacts with
//the Facebook API and there is a change in his permissions.
if (!accessTokenIsValid(currentAccessToken)){
Log.i("FB Token Updated", String.valueOf(currentAccessToken.getPermissions()));
requestLogin();
}
}
};
// User already has a valid access token? Then take the user to the main activity
if (accessTokenIsValid(AccessToken.getCurrentAccessToken())){
launchApp();
}else{
//Show the Facebook login page
requestLogin();
}
}
The key here is that you should not call Profile.getcurrentprofile from the login callback (either LoginManager,registercallback or loginButton.registercallback) - the values are not reliable. Set up the tracker and rely on it firing at the opportune time in order to get updated profile info.
Like Hardy said, you have to create an instance of ProfileTracker
which will start tracking profile updates, (i.e ProfileTracker.onCurrentProfileChanged()
will be called when the user's profile finishes being fetched).
Following is the complete code that you'd need to login to FB and get the user's profile:
LoginButton loginButton = (LoginButton) findViewById(R.id.btn_facebook);
loginButton.setReadPermissions("public_profile");
mCallbackManager = CallbackManager.Factory.create();
loginButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
private ProfileTracker mProfileTracker;
@Override
public void onSuccess(LoginResult loginResult) {
if(Profile.getCurrentProfile() == null) {
mProfileTracker = new ProfileTracker() {
@Override
protected void onCurrentProfileChanged(Profile oldProfile, Profile currentProfile) {
Log.v("facebook - profile", currentProfile.getFirstName());
mProfileTracker.stopTracking();
}
};
// no need to call startTracking() on mProfileTracker
// because it is called by its constructor, internally.
}
else {
Profile profile = Profile.getCurrentProfile();
Log.v("facebook - profile", profile.getFirstName());
}
}
@Override
public void onCancel() {
Log.v("facebook - onCancel", "cancelled");
}
@Override
public void onError(FacebookException e) {
Log.v("facebook - onError", e.getMessage());
}
});
You must override your Activity's or Fragment's onActivityResult()
as below:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// if you don't add following block,
// your registered `FacebookCallback` won't be called
if (mCallbackManager.onActivityResult(requestCode, resultCode, data)) {
return;
}
}
Code updated with Alex Zezekalo's suggestion to only call mProfileTracker.startTracking();
if Profile.getCurrentProfile()
returns null.
Following to Sufian's answer, you can also save the new profile to Profile class:
@Override
public void onSuccess(LoginResult loginResult) {
ProfileTracker profileTracker = new ProfileTracker() {
@Override
protected void onCurrentProfileChanged(Profile oldProfile, Profile currentProfile) {
this.stopTracking();
Profile.setCurrentProfile(currentProfile);
}
};
profileTracker.startTracking();
}