/* * 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; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import com.facebook.HttpMethod; import com.socialize.ConfigUtils; import com.socialize.ShareUtils; import com.socialize.Socialize; import com.socialize.SocializeService; import com.socialize.api.SocializeSession; import com.socialize.api.action.share.ShareOptions; import com.socialize.api.action.share.SocialNetworkShareListener; import com.socialize.api.action.user.UserSystem; import com.socialize.auth.*; import com.socialize.auth.facebook.FacebookActivity; import com.socialize.auth.facebook.FacebookAuthProviderInfo; import com.socialize.auth.facebook.FacebookAuthProviderInfo.PermissionType; import com.socialize.config.SocializeConfig; import com.socialize.entity.Entity; import com.socialize.entity.PropagationInfo; import com.socialize.error.SocializeException; import com.socialize.listener.AuthProviderListener; import com.socialize.listener.ListenerHolder; import com.socialize.listener.SocializeAuthListener; import com.socialize.log.SocializeLogger; import com.socialize.networks.*; import com.socialize.ui.profile.UserSettings; import com.socialize.util.ArrayUtils; import com.socialize.util.ImageUtils; import java.util.*; import java.util.Map.Entry; /** * @author Jason Polites */ public abstract class BaseFacebookFacade implements FacebookFacade { protected ListenerHolder holder; // This is a singleton protected UserSystem userSystem; protected SocializeLogger logger; protected SocializeConfig config; protected ImageUtils imageUtils; /* * (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#authenticate(android.app.Activity, com.socialize.auth.facebook.FacebookAuthProviderInfo, com.socialize.listener.AuthProviderListener) */ @Override public void authenticateWithActivity(Activity context, FacebookAuthProviderInfo info, boolean sso, final AuthProviderListener listener) { final String listenerKey = "auth"; holder.push(listenerKey, new AuthProviderListener() { @Override public void onError(SocializeException error) { holder.remove(listenerKey); listener.onError(error); } @Override public void onAuthSuccess(AuthProviderResponse response) { holder.remove(listenerKey); listener.onAuthSuccess(response); } @Override public void onAuthFail(SocializeException error) { holder.remove(listenerKey); listener.onAuthFail(error); } @Override public void onCancel() { holder.remove(listenerKey); listener.onCancel(); } }); Intent i = new Intent(context, FacebookActivity.class); i.putExtra("appId", info.getAppId()); i.putExtra("sso", sso); i.putExtra("type", info.getPermissionType().toString()); switch(info.getPermissionType()) { case READ: if(info.getReadPermissions() != null) { i.putExtra("permissions", info.getReadPermissions()); } break; case WRITE: if(info.getWritePermissions() != null) { i.putExtra("permissions", info.getWritePermissions()); } break; } context.startActivity(i); } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#link(android.app.Activity, com.socialize.listener.SocializeAuthListener) */ @Deprecated @Override public void link(Activity context, SocializeAuthListener listener) { linkForWrite(context, listener); } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#link(android.app.Activity, com.socialize.listener.SocializeAuthListener, java.lang.String[]) */ @Deprecated @Override public void link(Activity context, SocializeAuthListener listener, String... permissions) { getSocialize().authenticate(context, AuthProviderType.FACEBOOK, listener, permissions); } @Deprecated @Override public void link(Activity context, String token, boolean verifyPermissions, SocializeAuthListener listener) { doLink(context, token, verifyPermissions, false, listener, DEFAULT_PERMISSIONS); } @Override public void linkForRead(Activity context, SocializeAuthListener listener, String...permissions) { if(ArrayUtils.isEmpty(permissions)) { permissions = FacebookFacade.READ_PERMISSIONS; } getSocialize().authenticateForRead(context, AuthProviderType.FACEBOOK, listener, permissions); } @Override public void linkForWrite(Activity context, SocializeAuthListener listener, String...permissions) { if(ArrayUtils.isEmpty(permissions)) { permissions = FacebookFacade.WRITE_PERMISSIONS; } getSocialize().authenticateForWrite(context, AuthProviderType.FACEBOOK, listener, permissions); } @Override public void linkForRead(Activity context, String token, boolean verifyPermissions, SocializeAuthListener listener, String...permissions) { if(ArrayUtils.isEmpty(permissions)) { permissions = FacebookFacade.READ_PERMISSIONS; } doLink(context, token, verifyPermissions, true, listener, permissions); } @Override public void linkForWrite(Activity context, String token, boolean verifyPermissions, SocializeAuthListener listener, String...permissions) { if(ArrayUtils.isEmpty(permissions)) { permissions = FacebookFacade.WRITE_PERMISSIONS; } doLink(context, token, verifyPermissions, false, listener, permissions); } private void doLink(final Activity context, final String token, final boolean verifyPermissions, final boolean read, final SocializeAuthListener listener, final String...permissions) { SocializeConfig config = ConfigUtils.getConfig(context); final FacebookAuthProviderInfo fbInfo = new FacebookAuthProviderInfo(); fbInfo.setAppId(config.getProperty(SocializeConfig.FACEBOOK_APP_ID)); if(read) { fbInfo.setPermissionType(PermissionType.READ); } else { fbInfo.setPermissionType(PermissionType.WRITE); } final boolean sso = config.getBooleanProperty(SocializeConfig.FACEBOOK_SSO_ENABLED, true); if(verifyPermissions) { // Get the permissions for this token getCurrentPermissions(context, token, new OnPermissionResult() { @Override public void onError(SocializeException error) { if(listener != null) { listener.onError(error); } } @Override public void onSuccess(String[] current) { // Set the permissions on the session to the REAL permissions. if(read) { fbInfo.setReadPermissions(current); } else { fbInfo.setWritePermissions(current); } // Ensure the user has the required permissions String[] required = (read) ? READ_PERMISSIONS : WRITE_PERMISSIONS; boolean authRequired = false; for (String permission : required) { if(Arrays.binarySearch(current, permission) < 0) { // Does NOT have permission, we need to auth authRequired = true; break; } } if(authRequired) { // We need to merge in the default permissions... // Just add to a set Set<String> allPermissions = new HashSet<String>(); allPermissions.addAll(Arrays.asList(permissions)); allPermissions.addAll(Arrays.asList(current)); allPermissions.addAll(Arrays.asList(required)); // Now set the merged permissions. This is the final set if(read) { fbInfo.setReadPermissions(allPermissions.toArray(new String[allPermissions.size()])); } else { fbInfo.setWritePermissions(allPermissions.toArray(new String[allPermissions.size()])); } // Now try to auth authenticateWithActivity(context, fbInfo, sso, new AuthProviderListener() { @Override public void onError(SocializeException error) { if(listener != null) { listener.onError(error); } } @Override public void onCancel() { if(listener != null) { listener.onCancel(); } } @Override public void onAuthSuccess(AuthProviderResponse response) { // Now to the actual auth! doSocializeAuthKnownUser(context, fbInfo, token, listener); } @Override public void onAuthFail(SocializeException error) { if(listener != null) { listener.onAuthFail(error); } } }); } else { doSocializeAuthKnownUser(context, fbInfo, token, listener); } } }); } else { if(read) { fbInfo.setReadPermissions(permissions); } else { fbInfo.setWritePermissions(permissions); } doSocializeAuthKnownUser(context, fbInfo, token, listener); } } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#isLinked(android.content.Context) */ @Deprecated @Override public boolean isLinked(Context context) { return getSocialize().isAuthenticated(AuthProviderType.FACEBOOK); } @Override public boolean isLinkedForRead(Context context, String...permissions) { if(ArrayUtils.isEmpty(permissions)) { permissions = FacebookFacade.READ_PERMISSIONS; } return getSocialize().isAuthenticatedForRead(AuthProviderType.FACEBOOK, permissions); } @Override public boolean isLinkedForWrite(Context context, String...permissions) { if(ArrayUtils.isEmpty(permissions)) { permissions = FacebookFacade.WRITE_PERMISSIONS; } return getSocialize().isAuthenticatedForWrite(AuthProviderType.FACEBOOK, permissions); } /* * (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#getAccessToken(android.content.Context) */ @Override public String getAccessToken(Context context) { UserProviderCredentials creds = getSocialize().getSession().getUserProviderCredentials(AuthProviderType.FACEBOOK); if(creds != null) { return creds.getAccessToken(); } return null; } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#unlink(android.content.Context, com.socialize.networks.SocializeDeAuthListener) */ @Override public void unlink(final Context context, final SocializeDeAuthListener listener) { // Clear the FB session try { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { SocializeSession session = getSocialize().getSession(); session.clear(AuthProviderType.FACEBOOK); UserSettings userSettings = session.getUserSettings(); if(userSettings != null) { userSettings.setAutoPostFacebook(false); } userSystem.saveSession(context, session); logout(context); if(listener != null) { listener.onSuccess(); } } catch (Exception e) { if(listener != null) { listener.onError(SocializeException.wrap(e)); } } return null; } }.execute(); } catch (Exception e) { if(logger != null) { logger.error("Error while logging out of Facebook", e); } else { e.printStackTrace(); } } } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#postLike(android.app.Activity, com.socialize.entity.Entity, com.socialize.entity.PropagationInfo, com.socialize.networks.SocialNetworkListener) */ @Override public void postLike(Activity parent, Entity entity, PropagationInfo propInfo, SocialNetworkListener listener) { if(config.isOGLike()) { Map<String, Object> params = new HashMap<String, Object>(); params.put("object", propInfo.getEntityUrl()); post(parent, "me/og.likes", params, listener); } else { post(parent, entity, "", propInfo, listener); } } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#postEntity(android.app.Activity, com.socialize.entity.Entity, java.lang.String, com.socialize.api.action.share.SocialNetworkShareListener) */ @Override public void postEntity(Activity context, Entity entity, String text, SocialNetworkShareListener listener) { ShareOptions options = ShareUtils.getUserShareOptions(context); options.setText(text); options.setShowAuthDialog(false); ShareUtils.shareViaSocialNetworks(context, entity, options, listener, SocialNetwork.FACEBOOK); } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#postComment(android.app.Activity, com.socialize.entity.Entity, java.lang.String, com.socialize.entity.PropagationInfo, com.socialize.networks.SocialNetworkListener) */ @Override public void postComment(Activity parent, Entity entity, String comment, PropagationInfo propInfo, SocialNetworkListener listener) { post(parent, entity, comment, propInfo, listener); } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#postOG(android.app.Activity, com.socialize.entity.Entity, java.lang.String, java.lang.String, com.socialize.entity.PropagationInfo, com.socialize.networks.SocialNetworkListener) */ @Override public void postOG(Activity parent, Entity entity, String message, String action, PropagationInfo propInfo, SocialNetworkListener listener) { String entityUrl = propInfo.getEntityUrl(); String linkName = entityUrl; String link = entityUrl; if(entity != null) { linkName = entity.getDisplayName(); } final Map<String, Object> params = new HashMap<String, Object>(); params.put("name", linkName); params.put("message", message); params.put("link", link); params.put("type", "link"); DefaultPostData postData = new DefaultPostData(); postData.setPostValues(params); postData.setEntity(entity); postData.setPropagationInfo(propInfo); post(parent, listener, postData); } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#post(android.app.Activity, com.socialize.entity.Entity, java.lang.String, com.socialize.entity.PropagationInfo, com.socialize.networks.SocialNetworkListener) */ @Override public void post(Activity parent, Entity entity, String message, PropagationInfo propInfo, SocialNetworkListener listener) { postOG(parent, entity, message, null, propInfo, listener); } /* * (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#post(android.app.Activity, com.socialize.networks.SocialNetworkListener, com.socialize.networks.PostData) */ @Override public void post(Activity parent, SocialNetworkListener listener, PostData postData) { doFacebookCall(parent, postData.getPostValues(), "me/feed", HttpMethod.POST, listener); } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#post(android.app.Activity, java.lang.String, java.util.Map, com.socialize.networks.SocialNetworkPostListener) */ @Override public void post(Activity parent, String graphPath, Map<String, Object> postData, SocialNetworkPostListener listener) { doFacebookCall(parent, postData, graphPath, HttpMethod.POST, listener); } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#get(android.app.Activity, java.lang.String, java.util.Map, com.socialize.networks.SocialNetworkPostListener) */ @Override public void get(Activity parent, String graphPath, Map<String, Object> postData, SocialNetworkPostListener listener) { doFacebookCall(parent, postData, graphPath, HttpMethod.GET, listener); } /* (non-Javadoc) * @see com.socialize.networks.facebook.FacebookFacade#delete(android.app.Activity, java.lang.String, java.util.Map, com.socialize.networks.SocialNetworkPostListener) */ @Override public void delete(Activity parent, String graphPath, Map<String, Object> postData, SocialNetworkPostListener listener) { doFacebookCall(parent, postData, graphPath, HttpMethod.DELETE, listener); } protected void doSocializeAuthKnownUser(Context context, AuthProviderInfo fbInfo, String token, SocializeAuthListener listener) { DefaultUserProviderCredentials credentials = new DefaultUserProviderCredentials(); credentials.setAuthProviderInfo(fbInfo); credentials.setAccessToken(token); getSocialize().authenticateKnownUser( context, credentials, listener); } protected void onError(final Activity parent, final String msg, final Throwable e, final SocialNetworkPostListener listener) { if(logger != null) { if(e != null) { logger.error(msg, e); } else { logger.error(msg); } } else { if(e != null) { SocializeLogger.e(msg, e); } else { System.err.println(msg); } } if(listener != null) { parent.runOnUiThread(new Runnable() { @Override public void run() { listener.onNetworkError(parent, SocialNetwork.FACEBOOK, SocializeException.wrap(e)); } }); } } protected void handlePermissionError(Activity parent, final OnPermissionResult callback, final Exception e) { if(callback != null) { parent.runOnUiThread(new Runnable() { @Override public void run() { callback.onError(SocializeException.wrap(e)); } }); } } protected void handleFacebookError(final Activity parent, int code, String msg, Throwable e, SocialNetworkPostListener listener) { // Check for token error: // http://fbdevwiki.com/wiki/Error_codes if(code == 190) { // Clear the session cache getSocialize().clear3rdPartySession(parent, AuthProviderType.FACEBOOK); } onError(parent, msg, e, listener); } protected void doFacebookCall(Activity parent, Map<String, Object> postData, String graphPath, HttpMethod method, SocialNetworkPostListener listener) { Bundle bundle = new Bundle(); if(postData != null) { Set<Entry<String, Object>> entries = postData.entrySet(); for (Entry<String, Object> entry : entries) { Object value = entry.getValue(); if(value instanceof byte[]) { bundle.putByteArray(entry.getKey(), (byte[]) value); } else { bundle.putString(entry.getKey(), value.toString()); } } } doFacebookCall(parent, bundle, graphPath, method, listener); } protected abstract void doFacebookCall(Activity parent, Bundle data, String graphPath, HttpMethod method, SocialNetworkPostListener listener); // So we can mock protected SocializeService getSocialize() { return Socialize.getSocialize(); } // So we can mock protected String getFacebookAppId() { return config.getProperty(SocializeConfig.FACEBOOK_APP_ID); } public void setUserSystem(UserSystem userSystem) { this.userSystem = userSystem; } public void setLogger(SocializeLogger logger) { this.logger = logger; } public void setConfig(SocializeConfig config) { this.config = config; } public void setImageUtils(ImageUtils imageUtils) { this.imageUtils = imageUtils; } public void setHolder(ListenerHolder holder) { this.holder = holder; } }