Blame | Last modification | View Log | RSS feed
/*** Copyright 2010-present Facebook.** 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.facebook.widget;import android.app.Activity;import android.app.AlertDialog;import android.content.Context;import android.content.ContextWrapper;import android.content.DialogInterface;import android.content.Intent;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Typeface;import android.os.AsyncTask;import android.os.Bundle;import android.support.v4.app.Fragment;import android.util.AttributeSet;import android.util.Log;import android.util.TypedValue;import android.view.Gravity;import android.view.View;import android.widget.Button;import com.facebook.*;import com.facebook.android.R;import com.facebook.internal.AnalyticsEvents;import com.facebook.internal.SessionAuthorizationType;import com.facebook.internal.SessionTracker;import com.facebook.internal.Utility;import com.facebook.internal.Utility.FetchedAppSettings;import com.facebook.model.GraphUser;import java.util.Arrays;import java.util.Collections;import java.util.List;/*** A Log In/Log Out button that maintains session state and logs* in/out for the app.* <p/>* This control will create and use the active session upon construction* if it has the available data (if the app ID is specified in the manifest).* It will also open the active session if it does not require user interaction* (i.e. if the session is in the {@link com.facebook.SessionState#CREATED_TOKEN_LOADED} state.* Developers can override the use of the active session by calling* the {@link #setSession(com.facebook.Session)} method.*/public class LoginButton extends Button {public static enum ToolTipMode {/*** Default display mode. A server query will determine if the tool tip should be displayed* and, if so, what the string shown to the user should be.*/DEFAULT,/*** Display the tool tip with a local string--regardless of what the server returns*/DISPLAY_ALWAYS,/*** Never display the tool tip--regardless of what the server says*/NEVER_DISPLAY}private static final String TAG = LoginButton.class.getName();private String applicationId = null;private SessionTracker sessionTracker;private GraphUser user = null;private Session userInfoSession = null; // the Session used to fetch the current user infoprivate boolean confirmLogout;private boolean fetchUserInfo;private String loginText;private String logoutText;private UserInfoChangedCallback userInfoChangedCallback;private Fragment parentFragment;private LoginButtonProperties properties = new LoginButtonProperties();private String loginLogoutEventName = AnalyticsEvents.EVENT_LOGIN_VIEW_USAGE;private OnClickListener listenerCallback;private boolean nuxChecked;private ToolTipPopup.Style nuxStyle = ToolTipPopup.Style.BLUE;private ToolTipMode nuxMode = ToolTipMode.DEFAULT;private long nuxDisplayTime = ToolTipPopup.DEFAULT_POPUP_DISPLAY_TIME;private ToolTipPopup nuxPopup;static class LoginButtonProperties {private SessionDefaultAudience defaultAudience = SessionDefaultAudience.FRIENDS;private List<String> permissions = Collections.<String>emptyList();private SessionAuthorizationType authorizationType = null;private OnErrorListener onErrorListener;private SessionLoginBehavior loginBehavior = SessionLoginBehavior.SSO_WITH_FALLBACK;private Session.StatusCallback sessionStatusCallback;public void setOnErrorListener(OnErrorListener onErrorListener) {this.onErrorListener = onErrorListener;}public OnErrorListener getOnErrorListener() {return onErrorListener;}public void setDefaultAudience(SessionDefaultAudience defaultAudience) {this.defaultAudience = defaultAudience;}public SessionDefaultAudience getDefaultAudience() {return defaultAudience;}public void setReadPermissions(List<String> permissions, Session session) {if (SessionAuthorizationType.PUBLISH.equals(authorizationType)) {throw new UnsupportedOperationException("Cannot call setReadPermissions after setPublishPermissions has been called.");}if (validatePermissions(permissions, SessionAuthorizationType.READ, session)) {this.permissions = permissions;authorizationType = SessionAuthorizationType.READ;}}public void setPublishPermissions(List<String> permissions, Session session) {if (SessionAuthorizationType.READ.equals(authorizationType)) {throw new UnsupportedOperationException("Cannot call setPublishPermissions after setReadPermissions has been called.");}if (validatePermissions(permissions, SessionAuthorizationType.PUBLISH, session)) {this.permissions = permissions;authorizationType = SessionAuthorizationType.PUBLISH;}}private boolean validatePermissions(List<String> permissions,SessionAuthorizationType authType, Session currentSession) {if (SessionAuthorizationType.PUBLISH.equals(authType)) {if (Utility.isNullOrEmpty(permissions)) {throw new IllegalArgumentException("Permissions for publish actions cannot be null or empty.");}}if (currentSession != null && currentSession.isOpened()) {if (!Utility.isSubset(permissions, currentSession.getPermissions())) {Log.e(TAG, "Cannot set additional permissions when session is already open.");return false;}}return true;}List<String> getPermissions() {return permissions;}public void clearPermissions() {permissions = null;authorizationType = null;}public void setLoginBehavior(SessionLoginBehavior loginBehavior) {this.loginBehavior = loginBehavior;}public SessionLoginBehavior getLoginBehavior() {return loginBehavior;}public void setSessionStatusCallback(Session.StatusCallback callback) {this.sessionStatusCallback = callback;}public Session.StatusCallback getSessionStatusCallback() {return sessionStatusCallback;}}/*** Specifies a callback interface that will be called when the button's notion of the current* user changes (if the fetch_user_info attribute is true for this control).*/public interface UserInfoChangedCallback {/*** Called when the current user changes.* @param user the current user, or null if there is no user*/void onUserInfoFetched(GraphUser user);}/*** Callback interface that will be called when a network or other error is encountered* while logging in.*/public interface OnErrorListener {/*** Called when a network or other error is encountered.* @param error a FacebookException representing the error that was encountered.*/void onError(FacebookException error);}/*** Create the LoginButton.** @see View#View(Context)*/public LoginButton(Context context) {super(context);initializeActiveSessionWithCachedToken(context);// since onFinishInflate won't be called, we need to finish initialization ourselvesfinishInit();}/*** Create the LoginButton by inflating from XML** @see View#View(Context, AttributeSet)*/public LoginButton(Context context, AttributeSet attrs) {super(context, attrs);if (attrs.getStyleAttribute() == 0) {// apparently there's no method of setting a default style in xml,// so in case the users do not explicitly specify a style, we need// to use sensible defaults.this.setGravity(Gravity.CENTER);this.setTextColor(getResources().getColor(R.color.com_facebook_loginview_text_color));this.setTextSize(TypedValue.COMPLEX_UNIT_PX,getResources().getDimension(R.dimen.com_facebook_loginview_text_size));this.setTypeface(Typeface.DEFAULT_BOLD);if (isInEditMode()) {// cannot use a drawable in edit mode, so setting the background color instead// of a background resource.this.setBackgroundColor(getResources().getColor(R.color.com_facebook_blue));// hardcoding in edit mode as getResources().getString() doesn't seem to work in IntelliJloginText = "Log in with Facebook";} else {this.setBackgroundResource(R.drawable.com_facebook_button_blue);this.setCompoundDrawablesWithIntrinsicBounds(R.drawable.com_facebook_inverse_icon, 0, 0, 0);this.setCompoundDrawablePadding(getResources().getDimensionPixelSize(R.dimen.com_facebook_loginview_compound_drawable_padding));this.setPadding(getResources().getDimensionPixelSize(R.dimen.com_facebook_loginview_padding_left),getResources().getDimensionPixelSize(R.dimen.com_facebook_loginview_padding_top),getResources().getDimensionPixelSize(R.dimen.com_facebook_loginview_padding_right),getResources().getDimensionPixelSize(R.dimen.com_facebook_loginview_padding_bottom));}}parseAttributes(attrs);if (!isInEditMode()) {initializeActiveSessionWithCachedToken(context);}}/*** Create the LoginButton by inflating from XML and applying a style.** @see View#View(Context, AttributeSet, int)*/public LoginButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);parseAttributes(attrs);initializeActiveSessionWithCachedToken(context);}/*** Sets an OnErrorListener for this instance of LoginButton to call into when* certain exceptions occur.** @param onErrorListener The listener object to set*/public void setOnErrorListener(OnErrorListener onErrorListener) {properties.setOnErrorListener(onErrorListener);}/*** Returns the current OnErrorListener for this instance of LoginButton.** @return The OnErrorListener*/public OnErrorListener getOnErrorListener() {return properties.getOnErrorListener();}/*** Sets the default audience to use when the session is opened.* This value is only useful when specifying write permissions for the native* login dialog.** @param defaultAudience the default audience value to use*/public void setDefaultAudience(SessionDefaultAudience defaultAudience) {properties.setDefaultAudience(defaultAudience);}/*** Gets the default audience to use when the session is opened.* This value is only useful when specifying write permissions for the native* login dialog.** @return the default audience value to use*/public SessionDefaultAudience getDefaultAudience() {return properties.getDefaultAudience();}/*** Set the permissions to use when the session is opened. The permissions here* can only be read permissions. If any publish permissions are included, the login* attempt by the user will fail. The LoginButton can only be associated with either* read permissions or publish permissions, but not both. Calling both* setReadPermissions and setPublishPermissions on the same instance of LoginButton* will result in an exception being thrown unless clearPermissions is called in between.* <p/>* This method is only meaningful if called before the session is open. If this is called* after the session is opened, and the list of permissions passed in is not a subset* of the permissions granted during the authorization, it will log an error.* <p/>* Since the session can be automatically opened when the LoginButton is constructed,* it's important to always pass in a consistent set of permissions to this method, or* manage the setting of permissions outside of the LoginButton class altogether* (by managing the session explicitly).** @param permissions the read permissions to use** @throws UnsupportedOperationException if setPublishPermissions has been called*/public void setReadPermissions(List<String> permissions) {properties.setReadPermissions(permissions, sessionTracker.getSession());}/*** Set the permissions to use when the session is opened. The permissions here* can only be read permissions. If any publish permissions are included, the login* attempt by the user will fail. The LoginButton can only be associated with either* read permissions or publish permissions, but not both. Calling both* setReadPermissions and setPublishPermissions on the same instance of LoginButton* will result in an exception being thrown unless clearPermissions is called in between.* <p/>* This method is only meaningful if called before the session is open. If this is called* after the session is opened, and the list of permissions passed in is not a subset* of the permissions granted during the authorization, it will log an error.* <p/>* Since the session can be automatically opened when the LoginButton is constructed,* it's important to always pass in a consistent set of permissions to this method, or* manage the setting of permissions outside of the LoginButton class altogether* (by managing the session explicitly).** @param permissions the read permissions to use** @throws UnsupportedOperationException if setPublishPermissions has been called*/public void setReadPermissions(String... permissions) {properties.setReadPermissions(Arrays.asList(permissions), sessionTracker.getSession());}/*** Set the permissions to use when the session is opened. The permissions here* should only be publish permissions. If any read permissions are included, the login* attempt by the user may fail. The LoginButton can only be associated with either* read permissions or publish permissions, but not both. Calling both* setReadPermissions and setPublishPermissions on the same instance of LoginButton* will result in an exception being thrown unless clearPermissions is called in between.* <p/>* This method is only meaningful if called before the session is open. If this is called* after the session is opened, and the list of permissions passed in is not a subset* of the permissions granted during the authorization, it will log an error.* <p/>* Since the session can be automatically opened when the LoginButton is constructed,* it's important to always pass in a consistent set of permissions to this method, or* manage the setting of permissions outside of the LoginButton class altogether* (by managing the session explicitly).** @param permissions the publish permissions to use** @throws UnsupportedOperationException if setReadPermissions has been called* @throws IllegalArgumentException if permissions is null or empty*/public void setPublishPermissions(List<String> permissions) {properties.setPublishPermissions(permissions, sessionTracker.getSession());}/*** Set the permissions to use when the session is opened. The permissions here* should only be publish permissions. If any read permissions are included, the login* attempt by the user may fail. The LoginButton can only be associated with either* read permissions or publish permissions, but not both. Calling both* setReadPermissions and setPublishPermissions on the same instance of LoginButton* will result in an exception being thrown unless clearPermissions is called in between.* <p/>* This method is only meaningful if called before the session is open. If this is called* after the session is opened, and the list of permissions passed in is not a subset* of the permissions granted during the authorization, it will log an error.* <p/>* Since the session can be automatically opened when the LoginButton is constructed,* it's important to always pass in a consistent set of permissions to this method, or* manage the setting of permissions outside of the LoginButton class altogether* (by managing the session explicitly).** @param permissions the publish permissions to use** @throws UnsupportedOperationException if setReadPermissions has been called* @throws IllegalArgumentException if permissions is null or empty*/public void setPublishPermissions(String... permissions) {properties.setPublishPermissions(Arrays.asList(permissions), sessionTracker.getSession());}/*** Clears the permissions currently associated with this LoginButton.*/public void clearPermissions() {properties.clearPermissions();}/*** Sets the login behavior for the session that will be opened. If null is specified,* the default ({@link SessionLoginBehavior SessionLoginBehavior.SSO_WITH_FALLBACK}* will be used.** @param loginBehavior The {@link SessionLoginBehavior SessionLoginBehavior} that* specifies what behaviors should be attempted during* authorization.*/public void setLoginBehavior(SessionLoginBehavior loginBehavior) {properties.setLoginBehavior(loginBehavior);}/*** Gets the login behavior for the session that will be opened. If null is returned,* the default ({@link SessionLoginBehavior SessionLoginBehavior.SSO_WITH_FALLBACK}* will be used.** @return loginBehavior The {@link SessionLoginBehavior SessionLoginBehavior} that* specifies what behaviors should be attempted during* authorization.*/public SessionLoginBehavior getLoginBehavior() {return properties.getLoginBehavior();}/*** Set the application ID to be used to open the session.** @param applicationId the application ID to use*/public void setApplicationId(String applicationId) {this.applicationId = applicationId;}/*** Gets the callback interface that will be called when the current user changes.* @return the callback interface*/public UserInfoChangedCallback getUserInfoChangedCallback() {return userInfoChangedCallback;}/*** Sets the callback interface that will be called when the current user changes.** @param userInfoChangedCallback the callback interface*/public void setUserInfoChangedCallback(UserInfoChangedCallback userInfoChangedCallback) {this.userInfoChangedCallback = userInfoChangedCallback;}/*** Sets the callback interface that will be called whenever the status of the Session* associated with this LoginButton changes. Note that updates will only be sent to the* callback while the LoginButton is actually attached to a window.** @param callback the callback interface*/public void setSessionStatusCallback(Session.StatusCallback callback) {properties.setSessionStatusCallback(callback);}/*** Sets the callback interface that will be called whenever the status of the Session* associated with this LoginButton changes.* @return the callback interface*/public Session.StatusCallback getSessionStatusCallback() {return properties.getSessionStatusCallback();}/*** Sets the style (background) of the Tool Tip popup. Currently a blue style and a black* style are supported. Blue is default* @param nuxStyle The style of the tool tip popup.*/public void setToolTipStyle(ToolTipPopup.Style nuxStyle) {this.nuxStyle = nuxStyle;}/*** Sets the mode of the Tool Tip popup. Currently supported modes are default (normal* behavior), always_on (popup remains up until forcibly dismissed), and always_off (popup* doesn't show)* @param nuxMode The new mode for the tool tip*/public void setToolTipMode(ToolTipMode nuxMode) {this.nuxMode = nuxMode;}/*** Return the current {@link ToolTipMode} for this LoginButton* @return The {@link ToolTipMode}*/public ToolTipMode getToolTipMode() {return nuxMode;}/*** Sets the amount of time (in milliseconds) that the tool tip will be shown to the user. The* default is {@value ToolTipPopup#DEFAULT_POPUP_DISPLAY_TIME}. Any value that is less than or* equal to zero will cause the tool tip to be displayed indefinitely.* @param displayTime The amount of time (in milliseconds) that the tool tip will be displayed* to the user*/public void setToolTipDisplayTime(long displayTime) {this.nuxDisplayTime = displayTime;}/*** Gets the current amount of time (in ms) that the tool tip will be displayed to the user* @return*/public long getToolTipDisplayTime() {return nuxDisplayTime;}/*** Dismisses the Nux Tooltip if it is currently visible*/public void dismissToolTip() {if (nuxPopup != null) {nuxPopup.dismiss();nuxPopup = null;}}/*** Provides an implementation for {@link Activity#onActivityResult* onActivityResult} that updates the Session based on information returned* during the authorization flow. The Activity containing this view* should forward the resulting onActivityResult call here to* update the Session state based on the contents of the resultCode and* data.** @param requestCode* The requestCode parameter from the forwarded call. When this* onActivityResult occurs as part of Facebook authorization* flow, this value is the activityCode passed to open or* authorize.* @param resultCode* An int containing the resultCode parameter from the forwarded* call.* @param data* The Intent passed as the data parameter from the forwarded* call.* @return A boolean indicating whether the requestCode matched a pending* authorization request for this Session.* @see Session#onActivityResult(Activity, int, int, Intent)*/public boolean onActivityResult(int requestCode, int resultCode, Intent data) {Session session = sessionTracker.getSession();if (session != null) {return session.onActivityResult((Activity)getContext(), requestCode,resultCode, data);} else {return false;}}/*** Set the Session object to use instead of the active Session. Since a Session* cannot be reused, if the user logs out from this Session, and tries to* log in again, a new Active Session will be used instead.* <p/>* If the passed in session is currently opened, this method will also attempt to* load some user information for display (if needed).** @param newSession the Session object to use* @throws FacebookException if errors occur during the loading of user information*/public void setSession(Session newSession) {sessionTracker.setSession(newSession);fetchUserInfo();setButtonText();}@Overridepublic void onFinishInflate() {super.onFinishInflate();finishInit();}private void finishInit() {super.setOnClickListener(new LoginClickListener());setButtonText();if (!isInEditMode()) {sessionTracker = new SessionTracker(getContext(), new LoginButtonCallback(), null, false);fetchUserInfo();}}/*** Sets the fragment that contains this control. This allows the LoginButton to be* embedded inside a Fragment, and will allow the fragment to receive the* {@link Fragment#onActivityResult(int, int, android.content.Intent) onActivityResult}* call rather than the Activity.** @param fragment the fragment that contains this control*/public void setFragment(Fragment fragment) {parentFragment = fragment;}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();if (sessionTracker != null && !sessionTracker.isTracking()) {sessionTracker.startTracking();fetchUserInfo();setButtonText();}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (!nuxChecked && nuxMode != ToolTipMode.NEVER_DISPLAY && !isInEditMode()) {nuxChecked = true;checkNuxSettings();}}private void showNuxPerSettings(FetchedAppSettings settings) {if (settings != null && settings.getNuxEnabled() && getVisibility() == View.VISIBLE) {String nuxString = settings.getNuxContent();displayNux(nuxString);}}private void displayNux(String nuxString) {nuxPopup = new ToolTipPopup(nuxString, this);nuxPopup.setStyle(nuxStyle);nuxPopup.setNuxDisplayTime(nuxDisplayTime);nuxPopup.show();}private void checkNuxSettings() {if (nuxMode == ToolTipMode.DISPLAY_ALWAYS) {String nuxString = getResources().getString(R.string.com_facebook_tooltip_default);displayNux(nuxString);} else {// kick off an async requestfinal String appId = Utility.getMetadataApplicationId(getContext());AsyncTask<Void, Void, FetchedAppSettings> task = new AsyncTask<Void, Void, Utility.FetchedAppSettings>() {@Overrideprotected FetchedAppSettings doInBackground(Void... params) {FetchedAppSettings settings = Utility.queryAppSettings(appId, false);return settings;}@Overrideprotected void onPostExecute(FetchedAppSettings result) {showNuxPerSettings(result);}};task.execute((Void[])null);}}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();if (sessionTracker != null) {sessionTracker.stopTracking();}dismissToolTip();}@Overrideprotected void onVisibilityChanged(View changedView, int visibility) {super.onVisibilityChanged(changedView, visibility);// If the visibility is not VISIBLE, we want to dismiss the nux if it is thereif (visibility != VISIBLE) {dismissToolTip();}}// For testing purposes onlyList<String> getPermissions() {return properties.getPermissions();}void setProperties(LoginButtonProperties properties) {this.properties = properties;}void setLoginLogoutEventName(String eventName) {loginLogoutEventName = eventName;}private void parseAttributes(AttributeSet attrs) {TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.com_facebook_login_view);confirmLogout = a.getBoolean(R.styleable.com_facebook_login_view_confirm_logout, true);fetchUserInfo = a.getBoolean(R.styleable.com_facebook_login_view_fetch_user_info, true);loginText = a.getString(R.styleable.com_facebook_login_view_login_text);logoutText = a.getString(R.styleable.com_facebook_login_view_logout_text);a.recycle();}private void setButtonText() {if (sessionTracker != null && sessionTracker.getOpenSession() != null) {setText((logoutText != null) ? logoutText :getResources().getString(R.string.com_facebook_loginview_log_out_button));} else {setText((loginText != null) ? loginText :getResources().getString(R.string.com_facebook_loginview_log_in_button));}}private boolean initializeActiveSessionWithCachedToken(Context context) {if (context == null) {return false;}Session session = Session.getActiveSession();if (session != null) {return session.isOpened();}String applicationId = Utility.getMetadataApplicationId(context);if (applicationId == null) {return false;}return Session.openActiveSessionFromCache(context) != null;}private void fetchUserInfo() {if (fetchUserInfo) {final Session currentSession = sessionTracker.getOpenSession();if (currentSession != null) {if (currentSession != userInfoSession) {Request request = Request.newMeRequest(currentSession, new Request.GraphUserCallback() {@Overridepublic void onCompleted(GraphUser me, Response response) {if (currentSession == sessionTracker.getOpenSession()) {user = me;if (userInfoChangedCallback != null) {userInfoChangedCallback.onUserInfoFetched(user);}}if (response.getError() != null) {handleError(response.getError().getException());}}});Request.executeBatchAsync(request);userInfoSession = currentSession;}} else {user = null;if (userInfoChangedCallback != null) {userInfoChangedCallback.onUserInfoFetched(user);}}}}/*** Allow a developer to set the OnClickListener for the button. This will be called back after we do any handling* internally for login* @param clickListener*/@Overridepublic void setOnClickListener(OnClickListener clickListener) {listenerCallback = clickListener;}private class LoginClickListener implements OnClickListener {@Overridepublic void onClick(View v) {Context context = getContext();final Session openSession = sessionTracker.getOpenSession();if (openSession != null) {// If the Session is currently open, it must mean we need to log outif (confirmLogout) {// Create a confirmation dialogString logout = getResources().getString(R.string.com_facebook_loginview_log_out_action);String cancel = getResources().getString(R.string.com_facebook_loginview_cancel_action);String message;if (user != null && user.getName() != null) {message = String.format(getResources().getString(R.string.com_facebook_loginview_logged_in_as), user.getName());} else {message = getResources().getString(R.string.com_facebook_loginview_logged_in_using_facebook);}AlertDialog.Builder builder = new AlertDialog.Builder(context);builder.setMessage(message).setCancelable(true).setPositiveButton(logout, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {openSession.closeAndClearTokenInformation();}}).setNegativeButton(cancel, null);builder.create().show();} else {openSession.closeAndClearTokenInformation();}} else {Session currentSession = sessionTracker.getSession();if (currentSession == null || currentSession.getState().isClosed()) {sessionTracker.setSession(null);Session session = new Session.Builder(context).setApplicationId(applicationId).build();Session.setActiveSession(session);currentSession = session;}if (!currentSession.isOpened()) {Session.OpenRequest openRequest = null;if (parentFragment != null) {openRequest = new Session.OpenRequest(parentFragment);} else if (context instanceof Activity) {openRequest = new Session.OpenRequest((Activity)context);} else if (context instanceof ContextWrapper) {Context baseContext = ((ContextWrapper)context).getBaseContext();if (baseContext instanceof Activity) {openRequest = new Session.OpenRequest((Activity)baseContext);}}if (openRequest != null) {openRequest.setDefaultAudience(properties.defaultAudience);openRequest.setPermissions(properties.permissions);openRequest.setLoginBehavior(properties.loginBehavior);if (SessionAuthorizationType.PUBLISH.equals(properties.authorizationType)) {currentSession.openForPublish(openRequest);} else {currentSession.openForRead(openRequest);}}}}AppEventsLogger logger = AppEventsLogger.newLogger(getContext());Bundle parameters = new Bundle();parameters.putInt("logging_in", (openSession != null) ? 0 : 1);logger.logSdkEvent(loginLogoutEventName, null, parameters);if (listenerCallback != null) {listenerCallback.onClick(v);}}}private class LoginButtonCallback implements Session.StatusCallback {@Overridepublic void call(Session session, SessionState state,Exception exception) {fetchUserInfo();setButtonText();// if the client has a status callback registered, call it, otherwise// call the default handleError method, but don't call bothif (properties.sessionStatusCallback != null) {properties.sessionStatusCallback.call(session, state, exception);} else if (exception != null) {handleError(exception);}}};void handleError(Exception exception) {if (properties.onErrorListener != null) {if (exception instanceof FacebookException) {properties.onErrorListener.onError((FacebookException)exception);} else {properties.onErrorListener.onError(new FacebookException(exception));}}}}