/* * Copyright (c) 2012 Socialize Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.socialize.networks.facebook.v3; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.util.Log; import com.facebook.*; import com.facebook.Session.StatusCallback; import com.facebook.model.GraphUser; import com.socialize.auth.AuthProviderResponse; import com.socialize.config.SocializeConfig; import com.socialize.error.SocializeException; import com.socialize.listener.AuthProviderListener; import com.socialize.listener.SocializeAuthListener; import com.socialize.listener.SocializeListener; import com.socialize.networks.SocialNetwork; import com.socialize.networks.SocialNetworkPostListener; import com.socialize.networks.facebook.BaseFacebookFacade; import com.socialize.networks.facebook.OnPermissionResult; import com.socialize.util.StringUtils; import java.util.Arrays; /** * @author Jason Polites */ public class FacebookFacadeV3 extends BaseFacebookFacade { @Override public void onActivityResult(Activity context, int requestCode, int resultCode, Intent data) { Session activeSession = getActiveSession(context); if(activeSession != null) { activeSession.onActivityResult(context, requestCode, resultCode, data); } } @Override public int getSDKMajorVersion() { return 3; } @Deprecated @Override public void authenticate(Activity context, String appId, String[] permissions, boolean sso, final AuthProviderListener listener) { // Clear current session logout(context); login(context, appId, permissions, sso, true, listener); } @Override public void authenticate(Activity context, String appId, String[] permissions, boolean sso, boolean read, AuthProviderListener listener) { login(context, appId, permissions, sso, read, listener); } // Protected so we can mock protected void login(Activity context, boolean read, final AuthProviderListener listener) { login(context, config.getProperty(SocializeConfig.FACEBOOK_APP_ID), (read) ? READ_PERMISSIONS : WRITE_PERMISSIONS, config.getBooleanProperty(SocializeConfig.FACEBOOK_SSO_ENABLED, true), read, listener); } @Override public void onResume(Activity context, SocializeAuthListener listener) { Session session = Session.getActiveSession(); if (session == null) { session = createNewSession(context, config.getProperty(SocializeConfig.FACEBOOK_APP_ID)); String strToken = getAccessToken(context); if(!StringUtils.isEmpty(strToken)) { AccessToken accessToken = AccessToken.createFromExistingAccessToken( strToken, null, null, null, null); openSessionWithToken(session, accessToken); Session.setActiveSession(session); } } } // So we can mock protected Session createNewSession(Activity context, String appId) { return new Session.Builder(context).setApplicationId(appId).build(); } // So we can mock protected void openSessionWithToken(Session session, AccessToken token) { session.open(token, null); } // So we can mock protected void openSessionForRead(Session session, Session.OpenRequest auth) { session.openForRead(auth); } // So we can mock protected void openSessionForPublish(Session session, Session.OpenRequest auth) { session.openForPublish(auth); } // Protected so we can mock protected void login(Activity context, String appId, String[] permissions, boolean sso, boolean read, final AuthProviderListener listener) { Session.OpenRequest auth = new Session.OpenRequest(context); if(permissions != null) { auth.setPermissions(Arrays.asList(permissions)); } if(sso) { auth.setLoginBehavior(SessionLoginBehavior.SSO_WITH_FALLBACK); } else { auth.setLoginBehavior(SessionLoginBehavior.SUPPRESS_SSO); } auth.setCallback(createLoginCallback(listener)); Session session = createNewSession(context, appId); Session.setActiveSession(session); if(read) { openSessionForRead(session ,auth); } else { openSessionForPublish(session ,auth); } } // So we can mock protected Session.StatusCallback createLoginCallback(final AuthProviderListener listener) { return new Session.StatusCallback() { // callback when session changes state @Override public void call(final Session session, SessionState state, Exception exception) { if(exception != null && exception instanceof FacebookOperationCanceledException) { if(logger != null) { logger.error("Facebook operation failed", exception); } handleCancel(listener); return; } switch(state) { case OPENED: if (isSessionOpen(session)) { // make request to the /me API getUser(session, listener); } break; case CLOSED: if(exception != null) { handleError(exception, listener); } break; case CLOSED_LOGIN_FAILED: if(exception != null) { handleAuthFail(exception, listener); } break; } } }; } protected boolean isSessionOpen(Session session) { return session.isOpened(); } // so we can mock protected void getUser(final Session session, final AuthProviderListener listener) { Request.executeMeRequestAsync(session, new Request.GraphUserCallback() { // callback after Graph API response with user object @Override public void onCompleted(GraphUser user, Response response) { if(response.getError() != null) { handleError(response.getError().getException(), listener); } else if (user != null) { handleResult(session, user, listener); } } }); } // Protected so we can mock protected void handleError(Exception exception, SocializeListener listener) { if(listener != null) { listener.onError(SocializeException.wrap(exception)); } } // Protected so we can mock protected void handleAuthFail(Exception exception, AuthProviderListener listener) { if(listener != null) { listener.onAuthFail(SocializeException.wrap(exception)); } } // Protected so we can mock protected void handleCancel(AuthProviderListener listener) { if(listener != null) { listener.onCancel(); } } // Protected so we can mock protected void handleResult(Session session, GraphUser user, AuthProviderListener listener) { if(listener != null) { AuthProviderResponse response = new AuthProviderResponse(); response.setUserId(user.getId()); response.setToken(session.getAccessToken()); listener.onAuthSuccess(response); } } @Override public void extendAccessToken(Activity context, SocializeAuthListener listener) { // Do Nothing. Session refreshes this automatically } @Override public void getCurrentPermissions(Activity context, String token, final OnPermissionResult callback) { Session activeSession = getActiveSession(context); if(activeSession != null) { if(activeSession.getAccessToken().equals(token)) { callback.onSuccess((String[])activeSession.getPermissions().toArray(new String[activeSession.getPermissions().size()])); } else { AccessToken accessToken = AccessToken.createFromExistingAccessToken( token, null, null, null, null); // We must close the current session if(activeSession.isOpened()) { activeSession.closeAndClearTokenInformation(); activeSession = new Session.Builder(context).setApplicationId(config.getProperty(SocializeConfig.FACEBOOK_APP_ID)).build(); } activeSession.open(accessToken, new StatusCallback() { @Override public void call(Session session, SessionState state, Exception exception) { if(exception != null) { if(callback != null) { callback.onError(SocializeException.wrap(exception)); } else if(logger != null) { logger.error("Error accessing permissions for alternate token", exception); } else { Log.e("Socialize", "Error accessing permissions for alternate token", exception); } } else { Session.setActiveSession(session); if(callback != null) { callback.onSuccess((String[])session.getPermissions().toArray(new String[session.getPermissions().size()])); } } } }); } } else { handleNotSignedIn(context, callback); } } @Override public void logout(Context context) { Session activeSession = getActiveSession(context); if(activeSession != null) { activeSession.closeAndClearTokenInformation(); } } @Deprecated @Override public boolean isLinked(Context context) { return super.isLinked(context) && getActiveSession(context) != null; } @Override protected void doFacebookCall(final Activity context, final Bundle data, final String graphPath, final HttpMethod method, final SocialNetworkPostListener listener) { Session activeSession = getActiveSession(context); boolean read = method.equals(HttpMethod.GET); if(activeSession != null) { if(activeSession.isOpened()) { Request request = new Request(activeSession, graphPath, data, method, new Request.Callback() { @Override public void onCompleted(Response response) { handleFBResponse(context, response, listener); } }); RequestAsyncTask task = new RequestAsyncTask(request); task.execute(); } else { login(context, read, new AuthProviderListener() { @Override public void onError(SocializeException error) { if(listener != null) { listener.onNetworkError(context, SocialNetwork.FACEBOOK, error); } handleNonListenerError("", error); } @Override public void onCancel() { if(listener != null) { listener.onCancel(); } } @Override public void onAuthSuccess(AuthProviderResponse response) { doFacebookCall(context, data, graphPath, method, listener); } @Override public void onAuthFail(SocializeException error) { if(listener != null) { listener.onNetworkError(context, SocialNetwork.FACEBOOK, error); } handleNonListenerError("", error); unlink(context, null); } }); } } else { handleNotSignedIn(context, listener); } } // Protected so we can mock protected Session getActiveSession(Context context) { Session activeSession = Session.getActiveSession(); if(activeSession == null) { activeSession = new Session.Builder(context).setApplicationId(config.getProperty(SocializeConfig.FACEBOOK_APP_ID)).build(); Session.setActiveSession(activeSession); } return activeSession; } // Protected so we can mock protected void handleFBResponse(final Activity context, Response response, final SocialNetworkPostListener listener) { FacebookRequestError error = response.getError(); if(error != null) { if(listener != null) { listener.onNetworkError(context, SocialNetwork.FACEBOOK, error.getException()); } if(logger != null) { logger.error(error.getErrorMessage(), error.getException()); } else { Log.e("Socialize", error.getErrorMessage(), error.getException()); } switch(error.getCategory()) { case AUTHENTICATION_REOPEN_SESSION: unlink(context, null); break; case AUTHENTICATION_RETRY: unlink(context, null); break; case PERMISSION: unlink(context, null); break; } } else if(listener != null) { listener.onAfterPost(context, SocialNetwork.FACEBOOK, response .getGraphObject() .getInnerJSONObject()); } } // Protected so we can mock protected void handleNotSignedIn(final Activity context, SocializeListener listener) { String msg = "Not signed into Facebook"; if(listener != null) { listener.onError(new SocializeException(msg)); } else { handleNonListenerError(msg, new SocializeException(msg)); } } // Protected so we can mock protected void handleNotSignedIn(final Activity context, SocialNetworkPostListener listener) { String msg = "Not signed into Facebook"; if(listener != null) { listener.onNetworkError(context, SocialNetwork.FACEBOOK, new SocializeException(msg)); } else { handleNonListenerError(msg, new SocializeException(msg)); } } // Protected so we can mock protected void handleNonListenerError(String msg, Exception error) { if(logger != null) { logger.error(msg, error); } else { Log.e("Socialize", msg, error); } } }