/*
 * 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.demo.implementations.comment;

import com.socialize.CommentUtils;
import com.socialize.demo.SDKDemoActivity;
import com.socialize.entity.Comment;
import com.socialize.error.SocializeException;
import com.socialize.listener.comment.CommentAddListener;

/**
 * @author Jason Polites
 *
 */
public class AddCommentActivity extends SDKDemoActivity {

	/* (non-Javadoc)
	 * @see com.socialize.demo.DemoActivity#executeDemo()
	 */
	@Override
	public void executeDemo(String text) {
		CommentUtils.addComment(this, entity, text , new CommentAddListener() {
			@Override
			public void onError(SocializeException error) {
				handleError(AddCommentActivity.this, error);
			}

			@Override
			public void onCreate(Comment comment) {
				handleSocializeResult(comment);
			}

			@Override
			public void onCancel() {
				handleCancel();
			}
		});
	}

	@Override
	public boolean isTextEntryRequired() {
		return true;
	}

	/* (non-Javadoc)
	 * @see com.socialize.demo.DemoActivity#getButtonText()
	 */
	@Override
	public String getButtonText() {
		return "Add Comment";
	}
}


/*
 * 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.entity;

import com.socialize.api.action.ActionType;
import com.socialize.util.StringUtils;

/**
 * @author Jason Polites
 */
public abstract class SocializeAction extends SocializeObject {

	private static final long serialVersionUID = -1988365314114134147L;

	private Application application;

	private Propagation propagation;
	private Propagation propagationInfoRequest;
	private PropagationInfoResponse propagationInfoResponse;

	private Entity entity;
	private String entityKey; // Used when only the key is supplied.
	private User user;
	private Double lon;
	private Double lat;
	private Long date;
	private boolean locationShared;

	private boolean notificationsEnabled;

	public Application getApplication() {
		return application;
	}
	public void setApplication(Application application) {
		this.application = application;
	}

	public Entity getEntity() {
		return entity;
	}
	public void setEntity(Entity entity) {
		this.entity = entity;
	}

	/**
	 * Reverts to only set the entity key if the name is not defined.
	 * This ensures an existing entity name is not wiped out when the action is saved.
	 * @param entity
	 */
	public void setEntitySafe(Entity entity) {
		if(StringUtils.isEmpty(entity.getName())) {
			setEntityKey(entity.getKey());
		}
		else {
			setEntity(entity);
		}
	}
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
	public Double getLon() {
		return lon;
	}
	public void setLon(Double lon) {
		this.lon = lon;
	}
	public Double getLat() {
		return lat;
	}
	public void setLat(Double lat) {
		this.lat = lat;
	}
	public Long getDate() {
		return date;
	}
	public void setDate(Long date) {
		this.date = date;
	}

	public String getEntityDisplayName() {
		if(entity != null) {
			return entity.getDisplayName();
		}
		return entityKey;
	}

	public String getEntityKey() {
		if(StringUtils.isEmpty(entityKey)) {
			if(entity != null) {
				return entity.getKey();
			}
		}

		return entityKey;
	}
	public void setEntityKey(String entityKey) {
		this.entityKey = entityKey;
	}

	public boolean hasLocation() {
		return lon != null && lat != null;
	}

	public boolean isLocationShared() {
		return locationShared;
	}

	public void setLocationShared(boolean locationShared) {
		this.locationShared = locationShared;
	}

	public boolean isNotificationsEnabled() {
		return notificationsEnabled;
	}

	public void setNotificationsEnabled(boolean notificationsEnabled) {
		this.notificationsEnabled = notificationsEnabled;
	}

	public Propagation getPropagation() {
		return propagation;
	}

	public void setPropagation(Propagation propagation) {
		this.propagation = propagation;
	}

	public Propagation getPropagationInfoRequest() {
		return propagationInfoRequest;
	}

	public void setPropagationInfoRequest(Propagation propagationRequest) {
		this.propagationInfoRequest = propagationRequest;
	}

	public PropagationInfoResponse getPropagationInfoResponse() {
		return propagationInfoResponse;
	}

	public void setPropagationInfoResponse(PropagationInfoResponse propagationInfoResponse) {
		this.propagationInfoResponse = propagationInfoResponse;
	}
	public abstract ActionType getActionType();

	/**
	 * Returns the text to display for this action when rendering on screen (e.g. in a list)
	 * @return
	 */
	public String getDisplayText() {
		if(entity != null) {
			String name = entity.getName();
			String key = entity.getKey();
			if(!StringUtils.isEmpty(name)) {
				return name;
			}
			return key;
		}
		return entityKey;
	}
}


package com.socialize.auth.facebook;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

public class FacebookUrlBuilder {

	/**
	 * Constructs the URL to access the FB graph API and retrieve a profile image for the
	 * user with the given facebook id.
	 * @param id
	 * @return
	 */
	public String buildProfileImageUrl(String id) {
		StringBuilder builder = new StringBuilder();
		builder.append("http://graph.facebook.com/");
		builder.append(id);
		builder.append("/picture?type=large");
		return builder.toString();
	}

	public URL getProfileImageUrl(String id) throws MalformedURLException {
		return new URL(buildProfileImageUrl(id));
	}

	public InputStream getProfileImageStream(String id) throws IOException {
		return getProfileImageUrl(id).openStream();
	}

}


/*
 * 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.auth.twitter;

import android.content.Context;
import android.view.View;
import android.webkit.WebView;
import com.socialize.android.ioc.IBeanFactory;
import com.socialize.error.SocializeException;
import com.socialize.oauth.signpost.OAuth;
import com.socialize.oauth.signpost.OAuthTokenListener;
import com.socialize.oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
import com.socialize.oauth.signpost.http.HttpParameters;

/**
 * @author Jason Polites
 */
public class TwitterAuthWebView extends WebView implements ITwitterAuthWebView {

	private TwitterWebViewClient twitterWebViewClient;
	private IBeanFactory<TwitterWebViewClient> twitterWebViewClientFactory;

	public TwitterAuthWebView(Context context) {
		super(context);
	}

	public void init() {
		twitterWebViewClient = newTwitterWebViewClient();
		setWebViewClient(twitterWebViewClient);
	}

	/**
	 * Starts the authentication process using the given twitter key/secret.
	 * @param consumerKey
	 * @param consumerSecret
	 */
	public synchronized void authenticate(final String consumerKey, final String consumerSecret, final TwitterAuthListener listener)  {

		TwitterOAuthProvider provider = newTwitterOAuthProvider();
		CommonsHttpOAuthConsumer consumer = newCommonsHttpOAuthConsumer(consumerKey, consumerSecret);
		OAuthRequestListener oAuthRequestListener = newOAuthRequestListener(listener, provider, consumer);

		twitterWebViewClient.setOauthRequestListener(oAuthRequestListener);

		provider.retrieveRequestTokenAsync(consumer, TwitterOAuthProvider.OAUTH_CALLBACK_URL, newOAuthRequestTokenUrlListener(listener));
	}

	public void setTwitterWebViewClientFactory(IBeanFactory<TwitterWebViewClient> twitterWebViewClientFactory) {
		this.twitterWebViewClientFactory = twitterWebViewClientFactory;
	}

	protected OAuthRequestTokenUrlListener newOAuthRequestTokenUrlListener(final TwitterAuthListener listener) {
		return new OAuthRequestTokenUrlListener() {
			@Override
			public void onRequestUrl(String url) {
				loadUrl(url);
			}

			@Override
			public void onError(Exception e) {
				if(listener != null) {
					listener.onError(SocializeException.wrap(e));
				}
			}
		};
	}

	protected OAuthRequestListener newOAuthRequestListener(final TwitterAuthListener listener, final TwitterOAuthProvider provider, final CommonsHttpOAuthConsumer consumer) {
		return new OAuthRequestListener() {

			@Override
			public void onCancel(String cancelToken) {
				if(listener != null) {
					listener.onCancel();
				}
			}

			@Override
			public void onRequestToken(String token, String verifier) {
				try {
					provider.retrieveAccessTokenAsync(consumer, verifier, newOAuthTokenListener(listener));
				}
				catch (Exception e) {
					if(listener != null) {
						listener.onError(SocializeException.wrap(e));
					}
				}
			}
		};
	}

	protected OAuthTokenListener newOAuthTokenListener(final TwitterAuthListener listener) {
		return new OAuthTokenListener() {
			@Override
			public void onResponse(HttpParameters parameters) {
				String token = parameters.getFirst(OAuth.OAUTH_TOKEN);
				String secret = parameters.getFirst(OAuth.OAUTH_TOKEN_SECRET);

				// See https://dev.twitter.com/docs/auth/implementing-sign-twitter
				String userId = parameters.getFirst("user_id");
				String screenName = parameters.getFirst("screen_name");

				if(listener != null) {
					listener.onAuthSuccess(token, secret, screenName, userId);
				}
			}

			@Override
			public void onError(Exception e) {
				if(listener != null) {
					listener.onError(SocializeException.wrap(e));
				}
			}
		};
	}

	protected TwitterWebViewClient newTwitterWebViewClient() {
		if(twitterWebViewClientFactory != null) {
			return twitterWebViewClientFactory.getBean();
		}
		return new TwitterWebViewClient();
	}

	protected TwitterOAuthProvider newTwitterOAuthProvider() {
		return new TwitterOAuthProvider();
	}

	protected CommonsHttpOAuthConsumer newCommonsHttpOAuthConsumer(String consumerKey, String consumerSecret) {
		return new CommonsHttpOAuthConsumer(consumerKey, consumerSecret);
	}

	@Override
	public View getView() {
		return this;
	}
}


package com.socialize.ui.profile;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.widget.LinearLayout;
import com.socialize.UserUtils;
import com.socialize.android.ioc.IBeanFactory;
import com.socialize.entity.User;
import com.socialize.error.SocializeException;
import com.socialize.i18n.I18NConstants;
import com.socialize.listener.user.UserGetListener;
import com.socialize.log.SocializeLogger;
import com.socialize.ui.dialog.ProgressDialogFactory;
import com.socialize.ui.header.SocializeHeader;
import com.socialize.util.BitmapUtils;
import com.socialize.util.DisplayUtils;
import com.socialize.view.BaseView;

public class ProfileLayoutView extends BaseView {

	private String userId;

	private IBeanFactory<SocializeHeader> profileHeaderFactory;
	private IBeanFactory<ProfileContentView> profileContentViewFactory;

	private SocializeHeader header;
	private ProfileContentView content;
	private ProgressDialog dialog = null;
	private ProgressDialogFactory progressDialogFactory;
	private BitmapUtils bitmapUtils;
	private DisplayUtils displayUtils;

	public ProfileLayoutView(Activity context, String userId) {
		this(context);
		this.userId = userId;

		if(this.userId != null) {
			this.userId = this.userId.trim();
		}
	}

	public ProfileLayoutView(Context context) {
		super(context);
	}

	public void init() {

		LayoutParams fill = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.FILL_PARENT);

		setOrientation(LinearLayout.VERTICAL);
		setLayoutParams(fill);
		setPadding(0, 0, 0, 0);

		boolean landscape = false;

		if(displayUtils != null) {
			landscape = displayUtils.isLandscape();
		}

		if(!landscape) {
			header = profileHeaderFactory.getBean();
			addView(header);
		}

		content = profileContentViewFactory.getBean(this);
		addView(content);
	}

	@Override
	public void onViewLoad() {
		super.onViewLoad();
		if(getSocialize().isAuthenticated()) {
			doGetUserProfile();
		}
		else {
			showError(getContext(), new SocializeException("Socialize not authenticated"));
		}
	}

	public void doGetUserProfile() {
		long id = Long.parseLong(userId);
		dialog = progressDialogFactory.show(getContext(), I18NConstants.LOADING, I18NConstants.PLEASE_WAIT);

		UserUtils.getUser(getContext(), id, new UserGetListener() {

			@Override
			public void onGet(User user) {

				try {
					// Merge the current session user
					UserSettings settings = UserUtils.getUserSettings(getContext());
					User currentUser = UserUtils.getCurrentUser(getContext());

					if(currentUser.getId().equals(user.getId())) {
						currentUser.update(user);
						settings.update(user);
						user = currentUser;
					}

					// Set the user details into the view elements
					setUserDetails(user, settings);
				}
				catch (Exception e) {
					SocializeLogger.e("Error getting user", e);
				}

				if(dialog != null) {
					dialog.dismiss();
				}
			}

			@Override
			public void onError(SocializeException error) {
				showError(getContext(), error);
				if(dialog != null) {
					dialog.dismiss();
				}
			}
		});
	}

	/**
	 * Called when the profile picture has been changed by the user.
	 * @param bitmap
	 */
	public void onImageChange(Bitmap bitmap) {
		if(bitmap != null) {
			Bitmap scaled = bitmapUtils.getScaledBitmap(bitmap, 200, 200);
			content.onProfilePictureChange(scaled);
		}
	}

	public void setUserDetails(User user, UserSettings settings) {
		content.setUserDetails(user, settings);
	}

	public void setUserId(String entityKey) {
		this.userId = entityKey;
	}

	public void setProgressDialogFactory(ProgressDialogFactory progressDialogFactory) {
		this.progressDialogFactory = progressDialogFactory;
	}

	public void setProfileHeaderFactory(IBeanFactory<SocializeHeader> profileHeaderFactory) {
		this.profileHeaderFactory = profileHeaderFactory;
	}

	public void setProfileContentViewFactory(IBeanFactory<ProfileContentView> profileContentViewFactory) {
		this.profileContentViewFactory = profileContentViewFactory;
	}

	public void setBitmapUtils(BitmapUtils bitmapUtils) {
		this.bitmapUtils = bitmapUtils;
	}

	public void setDisplayUtils(DisplayUtils displayUtils) {
		this.displayUtils = displayUtils;
	}
}


/*
 * Copyright (c) 2009 Matthias Kaeppler 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.socialize.oauth.signpost.signature;

import com.socialize.oauth.signpost.OAuth;
import com.socialize.oauth.signpost.exception.OAuthMessageSignerException;
import com.socialize.oauth.signpost.http.HttpParameters;
import com.socialize.oauth.signpost.http.HttpRequest;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;

public class SignatureBaseString {

    private HttpRequest request;

    private HttpParameters requestParameters;

    /**
     * Constructs a new SBS instance that will operate on the given request
     * object and parameter set.
     *
     * @param request
     *        the HTTP request
     * @param requestParameters
     *        the set of request parameters from the Authorization header, query
     *        string and form body
     */
    public SignatureBaseString(HttpRequest request, HttpParameters requestParameters) {
        this.request = request;
        this.requestParameters = requestParameters;
    }

    /**
     * Builds the signature base string from the data this instance was
     * configured with.
     *
     * @return the signature base string
     * @throws OAuthMessageSignerException
     */
    public String generate() throws OAuthMessageSignerException {

        try {
            String normalizedUrl = normalizeRequestUrl();
            String normalizedParams = normalizeRequestParameters();

            return request.getMethod() + '&' + OAuth.percentEncode(normalizedUrl) + '&'
                    + OAuth.percentEncode(normalizedParams);
        } catch (Exception e) {
            throw new OAuthMessageSignerException(e);
        }
    }

    public String normalizeRequestUrl() throws URISyntaxException {
        URI uri = new URI(request.getRequestUrl());
        String scheme = uri.getScheme().toLowerCase();
        String authority = uri.getAuthority().toLowerCase();
        boolean dropPort = (scheme.equals("http") && uri.getPort() == 80)
                || (scheme.equals("https") && uri.getPort() == 443);
        if (dropPort) {
            // find the last : in the authority
            int index = authority.lastIndexOf(":");
            if (index >= 0) {
                authority = authority.substring(0, index);
            }
        }
        String path = uri.getRawPath();
        if (path == null || path.length() <= 0) {
            path = "/"; // conforms to RFC 2616 section 3.2.2
        }
        // we know that there is no query and no fragment here.
        return scheme + "://" + authority + path;
    }

    /**
     * Normalizes the set of request parameters this instance was configured
     * with, as per OAuth spec section 9.1.1.
     *
     * @param parameters
     *        the set of request parameters
     * @return the normalized params string
     * @throws IOException
     */
    public String normalizeRequestParameters() throws IOException {
        if (requestParameters == null) {
            return "";
        }

        StringBuilder sb = new StringBuilder();
        Iterator<String> iter = requestParameters.keySet().iterator();

        for (int i = 0; iter.hasNext(); i++) {
            String param = iter.next();

            if (OAuth.OAUTH_SIGNATURE.equals(param) || "realm".equals(param)) {
                continue;
            }

            if (i > 0) {
                sb.append("&");
            }

            sb.append(requestParameters.getAsQueryString(param));
        }
        return sb.toString();
    }
}


package com.socialize.notifications;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.socialize.android.ioc.IOCContainer;
import com.socialize.log.SocializeLogger;

public class SocializeC2DMReceiverHandler implements C2DMReceiverHandler {

	private SocializeLogger logger;
	private NotificationContainer container;
	private C2DMCallback notificationCallback;

	// Must be parameterless constructor
	public SocializeC2DMReceiverHandler() {
		this.logger = newSocializeLogger();
		this.container = newNotificationContainer();
	}

	protected void initBeans() {
		IOCContainer ioc = container.getContainer();
		if(ioc != null) {
			logger = ioc.getBean("logger");
			notificationCallback = ioc.getBean("notificationCallback");
		}
	}

	/* (non-Javadoc)
	 * @see com.socialize.notifications.C2DMReceiverHandler#onMessage(android.content.Context, android.content.Intent)
	 */
	@Override
	public void onMessage(Context context, Intent intent) {
		try {
			if(notificationCallback != null) {

				if(logger != null && logger.isDebugEnabled()) {
					logger.debug("SocializeC2DMReceiver received message");
				}

				Bundle extras = intent.getExtras();

				if(extras != null) {
					notificationCallback.onMessage(context, extras);
				}
				else {
					logWarn("No extras bundle found in notification intent.  Notification cannot be processed");
				}
			}
			else {
				logWarn("No notificationCallback found in GCM receiver.  Initialization may have failed.");
			}
		}
		catch (Exception e) {
			logError("Error processing GCM message", e);
		}
	}

	/* (non-Javadoc)
	 * @see com.socialize.notifications.C2DMReceiverHandler#onError(android.content.Context, java.lang.String)
	 */
	@Override
	public void onError(Context context, String errorId) {
		if(logger != null) {
			logger.error("GCM registration failed with error: " + errorId);
		}

		if(notificationCallback != null) {
			notificationCallback.onError(context, errorId);
		}
		else {
			logWarn("No notificationCallback found in GCM receiver.  Initialization may have failed.");
		}
	}

	/* (non-Javadoc)
	 * @see com.socialize.notifications.C2DMReceiverHandler#onRegistrered(android.content.Context, java.lang.String)
	 */
	@Override
	public void onRegistered(Context context, String registrationId)  {
		try {
			if(notificationCallback != null) {
				notificationCallback.onRegister(context, registrationId);
			}
			else {
				logWarn("No notificationCallback found in GCM receiver.  Initialization may have failed.");
			}
		}
		catch (Exception e) {
			logError("GCM registration failed", e);
		}
	}

	/* (non-Javadoc)
	 * @see com.socialize.notifications.C2DMReceiverHandler#onUnregistered(android.content.Context)
	 */
	@Override
	public void onUnregistered(Context context) {
		if(notificationCallback != null) {
			notificationCallback.onUnregister(context);

			if(logger != null && logger.isDebugEnabled()) {
				logger.debug("SocializeC2DMReceiver successfully unregistered");
			}
		}
		else {
			logWarn("No notificationCallback found in GCM receiver.  Initialization may have failed.");
		}
	}

	/* (non-Javadoc)
	 * @see com.socialize.notifications.C2DMReceiverHandler#onCreate()
	 */
	@Override
	public void onCreate(Context context) {
		if(logger != null && logger.isDebugEnabled()) {
			logger.debug("SocializeC2DMReceiver creating..");
		}

		try {
			container.onCreate(context);
			initBeans();
		}
		catch (Exception e) {
			logError("Error initializing GCM receiver!", e);
		}
	}

	protected void logError(String msg, Exception e) {
		if(logger != null) {
			logger.error(msg, e);
		}
		else {
			SocializeLogger.e(msg, e);
		}
	}

	protected void logWarn(String msg) {
		if(logger != null) {
			logger.warn(msg);
		}
		else {
			System.err.println(msg);
		}
	}

	// So we can mock.
	protected NotificationContainer newNotificationContainer() {
		return new NotificationContainer();
	}

	// So we can mock.
	protected SocializeLogger newSocializeLogger() {
		return new SocializeLogger();
	}

	protected void setNotificationCallback(C2DMCallback notificationCallback) {
		this.notificationCallback = notificationCallback;
	}

	/* (non-Javadoc)
	 * @see com.socialize.notifications.C2DMReceiverHandler#onDestroy()
	 */
	@Override
	public void onDestroy(Context context) {
		if(container != null) {
			container.onDestroy(context);
		}
	}

	/**
	 * Expert only (not documented)
	 * @return
	 */
	NotificationContainer getNotificationContainer() {
		return container;
	}
}


/*
 * 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.twitter;

import android.app.Activity;
import com.socialize.LocationUtils;
import com.socialize.UserUtils;
import com.socialize.api.action.ActionType;
import com.socialize.entity.Entity;
import com.socialize.entity.PropagationInfo;
import com.socialize.error.SocializeException;
import com.socialize.networks.AbstractSocialNetworkSharer;
import com.socialize.networks.SocialNetwork;
import com.socialize.networks.SocialNetworkListener;
import com.socialize.ui.profile.UserSettings;
import com.socialize.util.StringUtils;

/**
 * @author Jason Polites
 *
 */
public class TwitterSharer extends AbstractSocialNetworkSharer {

	private TwitterUtilsProxy twitterUtils;

	/* (non-Javadoc)
	 * @see com.socialize.networks.AbstractSocialNetworkSharer#getNetwork()
	 */
	@Override
	protected SocialNetwork getNetwork() {
		return SocialNetwork.TWITTER;
	}

	/*
	 * (non-Javadoc)
	 * @see com.socialize.networks.AbstractSocialNetworkSharer#doShare(android.app.Activity, com.socialize.entity.Entity, com.socialize.entity.PropagationUrlSet, java.lang.String, com.socialize.networks.SocialNetworkListener, com.socialize.api.action.ActionType)
	 */
	@Override
	protected void doShare(Activity context, Entity entity, PropagationInfo urlSet, String comment, SocialNetworkListener listener, ActionType type) throws SocializeException {

		Tweet tweet = new Tweet();

		switch(type) {

			case SHARE:
				if(StringUtils.isEmpty(comment))  comment = "Shared " + entity.getDisplayName();
				break;
			case LIKE:
				comment = "\u2764 likes " + entity.getDisplayName();
				break;
			case VIEW:
				comment = "Viewed " + entity.getDisplayName();
				break;
		}

		StringBuilder status = new StringBuilder();

		if(StringUtils.isEmpty(comment)) {
			status.append(entity.getDisplayName());
		}
		else {
			status.append(comment);
		}

		status.append(", ");
		status.append(urlSet.getEntityUrl());

		tweet.setText(status.toString());

		UserSettings settings = UserUtils.getUserSettings(context);

		if(settings != null && settings.isLocationEnabled()) {
			tweet.setLocation(LocationUtils.getLastKnownLocation(context));
			tweet.setShareLocation(true);
		}

		twitterUtils.tweet(context, tweet, listener);
	}

	public void setTwitterUtils(TwitterUtilsProxy twitterUtils) {
		this.twitterUtils = twitterUtils;
	}
}


/*
 * Copyright 2010 Facebook, Inc.
 *
 * 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.socialize.facebook;

import android.Manifest;
import android.app.Activity;
import android.content.*;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.os.*;
import android.text.TextUtils;
import android.webkit.CookieSyncManager;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;

/**
 * Main Facebook object for interacting with the Facebook developer API.
 * Provides methods to log in and log out a user, make requests using the REST
 * and Graph APIs, and start user interface interactions with the API (such as
 * pop-ups promoting for credentials, permissions, stream posts, etc.)
 *
 * @author  Jim Brusstar (jimbru@facebook.com),
 *          Yariv Sadan (yariv@facebook.com),
 *          Luke Shepard (lshepard@facebook.com)
 */
@Deprecated
public class Facebook {

    // Strings used in the authorization flow
    public static final String REDIRECT_URI = "fbconnect://success";
    public static final String CANCEL_URI = "fbconnect://cancel";
    public static final String TOKEN = "access_token";
    public static final String EXPIRES = "expires_in";
    public static final String SINGLE_SIGN_ON_DISABLED = "service_disabled";

    public static final int FORCE_DIALOG_AUTH = -1;

    private static final String LOGIN = "oauth";

    // Used as default activityCode by authorize(). See authorize() below.
    private static final int DEFAULT_AUTH_ACTIVITY_CODE = 32665;

    // Facebook server endpoints: may be modified in a subclass for testing
    protected static String DIALOG_BASE_URL =
        "https://m.facebook.com/dialog/";
    protected static String GRAPH_BASE_URL =
        "https://graph.facebook.com/";
    protected static String RESTSERVER_URL =
        "https://api.facebook.com/restserver.php";

    private String mAccessToken = null;
    private long mLastAccessUpdate = 0;
    private long mAccessExpires = 0;
    private String mAppId;

    private Activity mAuthActivity;
    private String[] mAuthPermissions;
    private int mAuthActivityCode;
    private DialogListener mAuthDialogListener;

    // If the last time we extended the access token was more than 24 hours ago
    // we try to refresh the access token again.
    final private long REFRESH_TOKEN_BARRIER = 24L * 60L * 60L * 1000L;

    /**
     * Constructor for Facebook object.
     *
     * @param appId
     *          Your Facebook application ID. Found at
     *          www.facebook.com/developers/apps.php.
     */
    public Facebook(String appId) {
        if (appId == null) {
            throw new IllegalArgumentException(
                    "You must specify your application ID when instantiating " +
                    "a Facebook object. See README for details.");
        }
        mAppId = appId;
    }

    /**
     * Default authorize method. Grants only basic permissions.
     *
     * See authorize() below for @params.
     */
    public void authorize(Activity activity, final DialogListener listener) {
        authorize(activity, new String[] {}, DEFAULT_AUTH_ACTIVITY_CODE,
                listener);
    }

    /**
     * Authorize method that grants custom permissions.
     *
     * See authorize() below for @params.
     */
    public void authorize(Activity activity, String[] permissions,
            final DialogListener listener) {
        authorize(activity, permissions, DEFAULT_AUTH_ACTIVITY_CODE, listener);
    }

    /**
     * Full authorize method.
     *
     * Starts either an Activity or a dialog which prompts the user to log in to
     * Facebook and grant the requested permissions to the given application.
     *
     * This method will, when possible, use Facebook's single sign-on for
     * Android to obtain an access token. This involves proxying a call through
     * the Facebook for Android stand-alone application, which will handle the
     * authentication flow, and return an OAuth access token for making API
     * calls.
     *
     * Because this process will not be available for all users, if single
     * sign-on is not possible, this method will automatically fall back to the
     * OAuth 2.0 User-Agent flow. In this flow, the user credentials are handled
     * by Facebook in an embedded WebView, not by the client application. As
     * such, the dialog makes a network request and renders HTML content rather
     * than a native UI. The access token is retrieved from a redirect to a
     * special URL that the WebView handles.
     *
     * Note that User credentials could be handled natively using the OAuth 2.0
     * Username and Password Flow, but this is not supported by this SDK.
     *
     * See http://developers.facebook.com/docs/authentication/ and
     * http://wiki.oauth.net/OAuth-2 for more details.
     *
     * Note that this method is asynchronous and the callback will be invoked in
     * the original calling thread (not in a background thread).
     *
     * Also note that requests may be made to the API without calling authorize
     * first, in which case only public information is returned.
     *
     * IMPORTANT: Note that single sign-on authentication will not function
     * correctly if you do not include a call to the authorizeCallback() method
     * in your onActivityResult() function! Please see below for more
     * information. single sign-on may be disabled by passing FORCE_DIALOG_AUTH
     * as the activityCode parameter in your call to authorize().
     *
     * @param activity
     *            The Android activity in which we want to display the
     *            authorization dialog.
     * @param applicationId
     *            The Facebook application identifier e.g. "350685531728"
     * @param permissions
     *            A list of permissions required for this application: e.g.
     *            "read_stream", "publish_stream", "offline_access", etc. see
     *            http://developers.facebook.com/docs/authentication/permissions
     *            This parameter should not be null -- if you do not require any
     *            permissions, then pass in an empty String array.
     * @param activityCode
     *            Single sign-on requires an activity result to be called back
     *            to the client application -- if you are waiting on other
     *            activities to return data, pass a custom activity code here to
     *            avoid collisions. If you would like to force the use of legacy
     *            dialog-based authorization, pass FORCE_DIALOG_AUTH for this
     *            parameter. Otherwise just omit this parameter and Facebook
     *            will use a suitable default. See
     *            http://developer.android.com/reference/android/
     *              app/Activity.html for more information.
     * @param listener
     *            Callback interface for notifying the calling application when
     *            the authentication dialog has completed, failed, or been
     *            canceled.
     */
    public void authorize(Activity activity, String[] permissions,
            int activityCode, final DialogListener listener) {

        boolean singleSignOnStarted = false;

        mAuthDialogListener = listener;

        // Prefer single sign-on, where available.
        if (activityCode >= 0) {
            singleSignOnStarted = startSingleSignOn(activity, mAppId,
                    permissions, activityCode);
        }
        // Otherwise fall back to traditional dialog.
        if (!singleSignOnStarted) {
            startDialogAuth(activity, permissions);
        }
    }

    /**
     * Internal method to handle single sign-on backend for authorize().
     *
     * @param activity
     *            The Android Activity that will parent the ProxyAuth Activity.
     * @param applicationId
     *            The Facebook application identifier.
     * @param permissions
     *            A list of permissions required for this application. If you do
     *            not require any permissions, pass an empty String array.
     * @param activityCode
     *            Activity code to uniquely identify the result Intent in the
     *            callback.
     */
    private boolean startSingleSignOn(Activity activity, String applicationId,
            String[] permissions, int activityCode) {
        boolean didSucceed = true;
        Intent intent = new Intent();

        intent.setClassName("com.facebook.katana",
                "com.facebook.katana.ProxyAuth");
        intent.putExtra("client_id", applicationId);
        if (permissions.length > 0) {
            intent.putExtra("scope", TextUtils.join(",", permissions));
        }

        // Verify that the application whose package name is
        // com.facebook.katana.ProxyAuth
        // has the expected FB app signature.
        if (!validateActivityIntent(activity, intent)) {
            return false;
        }

        mAuthActivity = activity;
        mAuthPermissions = permissions;
        mAuthActivityCode = activityCode;
        try {
            activity.startActivityForResult(intent, activityCode);
        } catch (ActivityNotFoundException e) {
            didSucceed = false;
        }

        return didSucceed;
    }

    /**
     * Helper to validate an activity intent by resolving and checking the
     * provider's package signature.
     *
     * @param context
     * @param intent
     * @return true if the service intent resolution happens successfully and the
     * 	signatures match.
     */
    private boolean validateActivityIntent(Context context, Intent intent) {
        ResolveInfo resolveInfo =
            context.getPackageManager().resolveActivity(intent, 0);
        if (resolveInfo == null) {
            return false;
        }

        return validateAppSignatureForPackage(
            context,
            resolveInfo.activityInfo.packageName);
    }

    /**
     * Helper to validate a service intent by resolving and checking the
     * provider's package signature.
     *
     * @param context
     * @param intent
     * @return true if the service intent resolution happens successfully and the
     * 	signatures match.
     */
    private boolean validateServiceIntent(Context context, Intent intent) {
        ResolveInfo resolveInfo =
            context.getPackageManager().resolveService(intent, 0);
        if (resolveInfo == null) {
            return false;
        }

        return validateAppSignatureForPackage(
            context,
            resolveInfo.serviceInfo.packageName);
    }

    /**
     * Query the signature for the application that would be invoked by the
     * given intent and verify that it matches the FB application's signature.
     *
     * @param context
     * @param packageName
     * @return true if the app's signature matches the expected signature.
     */
    private boolean validateAppSignatureForPackage(Context context,
        String packageName) {

        PackageInfo packageInfo;
        try {
            packageInfo = context.getPackageManager().getPackageInfo(
                    packageName, PackageManager.GET_SIGNATURES);
        } catch (NameNotFoundException e) {
            return false;
        }

        for (Signature signature : packageInfo.signatures) {
            if (signature.toCharsString().equals(FB_APP_SIGNATURE)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Internal method to handle dialog-based authentication backend for
     * authorize().
     *
     * @param activity
     *            The Android Activity that will parent the auth dialog.
     * @param applicationId
     *            The Facebook application identifier.
     * @param permissions
     *            A list of permissions required for this application. If you do
     *            not require any permissions, pass an empty String array.
     */
	private void startDialogAuth(Activity activity, String[] permissions) {
        Bundle params = new Bundle();
        if (permissions.length > 0) {
            params.putString("scope", TextUtils.join(",", permissions));
        }
        CookieSyncManager.createInstance(activity);
        dialog(activity, LOGIN, params, new DialogListener() {

            public void onComplete(Bundle values) {
                // ensure any cookies set by the dialog are saved
                CookieSyncManager.getInstance().sync();
                setAccessToken(values.getString(TOKEN));
                setAccessExpiresIn(values.getString(EXPIRES));
                if (isSessionValid()) {
                    Util.logd("Facebook-authorize", "Login Success! access_token="
                            + getAccessToken() + " expires="
                            + getAccessExpires());
                    mAuthDialogListener.onComplete(values);
                } else {
                    mAuthDialogListener.onFacebookError(new FacebookError(
                                    "Failed to receive access token."));
                }
            }

            public void onError(DialogError error) {
                Util.logd("Facebook-authorize", "Login failed: " + error);
                mAuthDialogListener.onError(error);
            }

            public void onFacebookError(FacebookError error) {
                Util.logd("Facebook-authorize", "Login failed: " + error);
                mAuthDialogListener.onFacebookError(error);
            }

            public void onCancel() {
                Util.logd("Facebook-authorize", "Login canceled");
                mAuthDialogListener.onCancel();
            }
        });
    }

    /**
     * IMPORTANT: This method must be invoked at the top of the calling
     * activity's onActivityResult() function or Facebook authentication will
     * not function properly!
     *
     * If your calling activity does not currently implement onActivityResult(),
     * you must implement it and include a call to this method if you intend to
     * use the authorize() method in this SDK.
     *
     * For more information, see
     * http://developer.android.com/reference/android/app/
     *   Activity.html#onActivityResult(int, int, android.content.Intent)
     */
    public void authorizeCallback(int requestCode, int resultCode, Intent data) {
        if (requestCode == mAuthActivityCode) {

            // Successfully redirected.
            if (resultCode == Activity.RESULT_OK) {

                // Check OAuth 2.0/2.10 error code.
                String error = data.getStringExtra("error");
                if (error == null) {
                    error = data.getStringExtra("error_type");
                }

                // A Facebook error occurred.
                if (error != null) {
                    if (error.equals(SINGLE_SIGN_ON_DISABLED)
                            || error.equals("AndroidAuthKillSwitchException")) {
                        Util.logd("Facebook-authorize", "Hosted auth currently "
                            + "disabled. Retrying dialog auth...");
                        startDialogAuth(mAuthActivity, mAuthPermissions);
                    } else if (error.equals("access_denied")
                            || error.equals("OAuthAccessDeniedException")) {
                        Util.logd("Facebook-authorize", "Login canceled by user.");
                        mAuthDialogListener.onCancel();
                    } else {
                        String description = data.getStringExtra("error_description");
                        if (description != null) {
                            error = error + ":" + description;
                        }
                        Util.logd("Facebook-authorize", "Login failed: " + error);
                        mAuthDialogListener.onFacebookError(
                          new FacebookError(error));
                    }

                // No errors.
                } else {
                    setAccessToken(data.getStringExtra(TOKEN));
                    setAccessExpiresIn(data.getStringExtra(EXPIRES));
                    if (isSessionValid()) {
                        Util.logd("Facebook-authorize",
                                "Login Success! access_token="
                                        + getAccessToken() + " expires="
                                        + getAccessExpires());
                        mAuthDialogListener.onComplete(data.getExtras());
                    } else {
                        mAuthDialogListener.onFacebookError(new FacebookError(
                                        "Failed to receive access token."));
                    }
                }

            // An error occurred before we could be redirected.
            } else if (resultCode == Activity.RESULT_CANCELED) {

                // An Android error occured.
                if (data != null) {
                    Util.logd("Facebook-authorize",
                            "Login failed: " + data.getStringExtra("error"));
                    mAuthDialogListener.onError(
                            new DialogError(
                                    data.getStringExtra("error"),
                                    data.getIntExtra("error_code", -1),
                                    data.getStringExtra("failing_url")));

                // User pressed the 'back' button.
                } else {
                    Util.logd("Facebook-authorize", "Login canceled by user.");
                    mAuthDialogListener.onCancel();
                }
            }
        }
    }

    /**
     * Refresh OAuth access token method. Binds to Facebook for Android
     * stand-alone application application to refresh the access token. This
     * method tries to connect to the Facebook App which will handle the
     * authentication flow, and return a new OAuth access token. This method
     * will automatically replace the old token with a new one. Note that this
     * method is asynchronous and the callback will be invoked in the original
     * calling thread (not in a background thread).
     *
     * @param context
     *            The Android Context that will be used to bind to the Facebook
     *            RefreshToken Service
     * @param serviceListener
     *            Callback interface for notifying the calling application when
     *            the refresh request has completed or failed (can be null). In
     *            case of a success a new token can be found inside the result
     *            Bundle under Facebook.ACCESS_TOKEN key.
     * @return true if the binding to the RefreshToken Service was created
     */
    public boolean extendAccessToken(Context context, ServiceListener serviceListener) {
        Intent intent = new Intent();

        intent.setClassName("com.facebook.katana",
                "com.facebook.katana.platform.TokenRefreshService");

        // Verify that the application whose package name is
        // com.facebook.katana
        // has the expected FB app signature.
        if (!validateServiceIntent(context, intent)) {
            return false;
        }

        return context.bindService(intent,
                new TokenRefreshServiceConnection(context, serviceListener),
                Context.BIND_AUTO_CREATE);
    }

    /**
    * Calls extendAccessToken if shouldExtendAccessToken returns true.
    *
    * @return the same value as extendAccessToken if the the token requires
    *           refreshing, true otherwise
    */
    public boolean extendAccessTokenIfNeeded(Context context, ServiceListener serviceListener) {
        if (shouldExtendAccessToken()) {
            return extendAccessToken(context, serviceListener);
        }
        return true;
    }

    /**
     * Check if the access token requires refreshing.
     *
     * @return true if the last time a new token was obtained was over 24 hours ago.
     */
    public boolean shouldExtendAccessToken() {
        return isSessionValid() &&
                (System.currentTimeMillis() - mLastAccessUpdate >= REFRESH_TOKEN_BARRIER);
    }

    /**
     * Handles connection to the token refresh service (this service is a part
     * of Facebook App).
     */
    private class TokenRefreshServiceConnection implements ServiceConnection {

        final Messenger messageReceiver = new Messenger(new Handler() {
            @Override
            public void handleMessage(Message msg) {
                String token = msg.getData().getString(TOKEN);
                long expiresAt = msg.getData().getLong(EXPIRES) * 1000L;

                // To avoid confusion we should return the expiration time in
                // the same format as the getAccessExpires() function - that
                // is in milliseconds.
                Bundle resultBundle = (Bundle) msg.getData().clone();
                resultBundle.putLong(EXPIRES, expiresAt);

                if (token != null) {
                    setAccessToken(token);
                    setAccessExpires(expiresAt);
                    if (serviceListener != null) {
                        serviceListener.onComplete(resultBundle);
                    }
                } else if (serviceListener != null) { // extract errors only if client wants them
                    String error = msg.getData().getString("error");
                    if (msg.getData().containsKey("error_code")) {
                        int errorCode = msg.getData().getInt("error_code");
                        serviceListener.onFacebookError(new FacebookError(error, null, errorCode));
                    } else {
                        serviceListener.onError(new Error(error != null ? error
                                : "Unknown service error"));
                    }
                }

                // The refreshToken function should be called rarely,
                // so there is no point in keeping the binding open.
                applicationsContext.unbindService(TokenRefreshServiceConnection.this);
            }
        });

        final ServiceListener serviceListener;
        final Context applicationsContext;

        Messenger messageSender = null;

        public TokenRefreshServiceConnection(Context applicationsContext,
                ServiceListener serviceListener) {
            this.applicationsContext = applicationsContext;
            this.serviceListener = serviceListener;
        }

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            messageSender = new Messenger(service);
            refreshToken();
        }

        @Override
        public void onServiceDisconnected(ComponentName arg) {
            serviceListener.onError(new Error("Service disconnected"));
            // We returned an error so there's no point in
            // keeping the binding open.
            applicationsContext.unbindService(TokenRefreshServiceConnection.this);
        }

        private void refreshToken() {
            Bundle requestData = new Bundle();
            requestData.putString(TOKEN, mAccessToken);

            Message request = Message.obtain();
            request.setData(requestData);
            request.replyTo = messageReceiver;

            try {
                messageSender.send(request);
            } catch (RemoteException e) {
                serviceListener.onError(new Error("Service connection error"));
            }
        }
    };

    /**
     * Invalidate the current user session by removing the access token in
     * memory, clearing the browser cookie, and calling auth.expireSession
     * through the API.
     *
     * Note that this method blocks waiting for a network response, so do not
     * call it in a UI thread.
     *
     * @param context
     *            The Android context in which the logout should be called: it
     *            should be the same context in which the login occurred in
     *            order to clear any stored cookies
     * @throws IOException
     * @throws MalformedURLException
     * @return JSON string representation of the auth.expireSession response
     *            ("true" if successful)
     */
    public String logout(Context context)
            throws MalformedURLException, IOException {
        Util.clearCookies(context);
        Bundle b = new Bundle();
        b.putString("method", "auth.expireSession");
        String response = request(b);
        setAccessToken(null);
        setAccessExpires(0);
        return response;
    }

    /**
     * Make a request to Facebook's old (pre-graph) API with the given
     * parameters. One of the parameter keys must be "method" and its value
     * should be a valid REST server API method.
     *
     * See http://developers.facebook.com/docs/reference/rest/
     *
     * Note that this method blocks waiting for a network response, so do not
     * call it in a UI thread.
     *
     * Example:
     * <code>
     *  Bundle parameters = new Bundle();
     *  parameters.putString("method", "auth.expireSession");
     *  String response = request(parameters);
     * </code>
     *
     * @param parameters
     *            Key-value pairs of parameters to the request. Refer to the
     *            documentation: one of the parameters must be "method".
     * @throws IOException
     *            if a network error occurs
     * @throws MalformedURLException
     *            if accessing an invalid endpoint
     * @throws IllegalArgumentException
     *            if one of the parameters is not "method"
     * @return JSON string representation of the response
     */
    public String request(Bundle parameters)
            throws MalformedURLException, IOException {
        if (!parameters.containsKey("method")) {
            throw new IllegalArgumentException("API method must be specified. "
                    + "(parameters must contain key \"method\" and value). See"
                    + " http://developers.facebook.com/docs/reference/rest/");
        }
        return request(null, parameters, "GET");
    }

    /**
     * Make a request to the Facebook Graph API without any parameters.
     *
     * See http://developers.facebook.com/docs/api
     *
     * Note that this method blocks waiting for a network response, so do not
     * call it in a UI thread.
     *
     * @param graphPath
     *            Path to resource in the Facebook graph, e.g., to fetch data
     *            about the currently logged authenticated user, provide "me",
     *            which will fetch http://graph.facebook.com/me
     * @throws IOException
     * @throws MalformedURLException
     * @return JSON string representation of the response
     */
    public String request(String graphPath)
            throws MalformedURLException, IOException {
        return request(graphPath, new Bundle(), "GET");
    }

    /**
     * Make a request to the Facebook Graph API with the given string parameters
     * using an HTTP GET (default method).
     *
     * See http://developers.facebook.com/docs/api
     *
     * Note that this method blocks waiting for a network response, so do not
     * call it in a UI thread.
     *
     * @param graphPath
     *            Path to resource in the Facebook graph, e.g., to fetch data
     *            about the currently logged authenticated user, provide "me",
     *            which will fetch http://graph.facebook.com/me
     * @param parameters
     *            key-value string parameters, e.g. the path "search" with
     *            parameters "q" : "facebook" would produce a query for the
     *            following graph resource:
     *            https://graph.facebook.com/search?q=facebook
     * @throws IOException
     * @throws MalformedURLException
     * @return JSON string representation of the response
     */
    public String request(String graphPath, Bundle parameters)
            throws MalformedURLException, IOException {
        return request(graphPath, parameters, "GET");
    }

    /**
     * Synchronously make a request to the Facebook Graph API with the given
     * HTTP method and string parameters. Note that binary data parameters
     * (e.g. pictures) are not yet supported by this helper function.
     *
     * See http://developers.facebook.com/docs/api
     *
     * Note that this method blocks waiting for a network response, so do not
     * call it in a UI thread.
     *
     * @param graphPath
     *            Path to resource in the Facebook graph, e.g., to fetch data
     *            about the currently logged authenticated user, provide "me",
     *            which will fetch http://graph.facebook.com/me
     * @param params
     *            Key-value string parameters, e.g. the path "search" with
     *            parameters {"q" : "facebook"} would produce a query for the
     *            following graph resource:
     *            https://graph.facebook.com/search?q=facebook
     * @param httpMethod
     *            http verb, e.g. "GET", "POST", "DELETE"
     * @throws IOException
     * @throws MalformedURLException
     * @return JSON string representation of the response
     */
    public String request(String graphPath, Bundle params, String httpMethod)
            throws FileNotFoundException, MalformedURLException, IOException {
        params.putString("format", "json");
        if (isSessionValid()) {
            params.putString(TOKEN, getAccessToken());
        }
        String url = (graphPath != null) ? GRAPH_BASE_URL + graphPath
                                         : RESTSERVER_URL;
        return Util.openUrl(url, httpMethod, params);
    }

    /**
     * Generate a UI dialog for the request action in the given Android context.
     *
     * Note that this method is asynchronous and the callback will be invoked in
     * the original calling thread (not in a background thread).
     *
     * @param context
     *            The Android context in which we will generate this dialog.
     * @param action
     *            String representation of the desired method: e.g. "login",
     *            "stream.publish", ...
     * @param listener
     *            Callback interface to notify the application when the dialog
     *            has completed.
     */
    public void dialog(Context context, String action,
            DialogListener listener) {
        dialog(context, action, new Bundle(), listener);
    }

    /**
     * Generate a UI dialog for the request action in the given Android context
     * with the provided parameters.
     *
     * Note that this method is asynchronous and the callback will be invoked in
     * the original calling thread (not in a background thread).
     *
     * @param context
     *            The Android context in which we will generate this dialog.
     * @param action
     *            String representation of the desired method: e.g. "feed" ...
     * @param parameters
     *            String key-value pairs to be passed as URL parameters.
     * @param listener
     *            Callback interface to notify the application when the dialog
     *            has completed.
     */
    public void dialog(Context context, String action, Bundle parameters,
            final DialogListener listener) {

        String endpoint = DIALOG_BASE_URL + action;
        parameters.putString("display", "touch");
        parameters.putString("redirect_uri", REDIRECT_URI);

        if (action.equals(LOGIN)) {
            parameters.putString("type", "user_agent");
            parameters.putString("client_id", mAppId);
        } else {
            parameters.putString("app_id", mAppId);
        }

        if (isSessionValid()) {
            parameters.putString(TOKEN, getAccessToken());
        }
        String url = endpoint + "?" + Util.encodeUrl(parameters);
        if (context.checkCallingOrSelfPermission(Manifest.permission.INTERNET)
                != PackageManager.PERMISSION_GRANTED) {
            Util.showAlert(context, "Error",
                    "Application requires permission to access the Internet");
        } else {
            new FbDialog(context, url, listener).show();
        }
    }

    /**
     * @return boolean - whether this object has an non-expired session token
     */
    public boolean isSessionValid() {
        return (getAccessToken() != null) &&
                ((getAccessExpires() == 0) ||
                        (System.currentTimeMillis() < getAccessExpires()));
    }

    /**
     * Retrieve the OAuth 2.0 access token for API access: treat with care.
     * Returns null if no session exists.
     *
     * @return String - access token
     */
    public String getAccessToken() {
        return mAccessToken;
    }

    /**
     * Retrieve the current session's expiration time (in milliseconds since
     * Unix epoch), or 0 if the session doesn't expire or doesn't exist.
     *
     * @return long - session expiration time
     */
    public long getAccessExpires() {
        return mAccessExpires;
    }

    /**
     * Set the OAuth 2.0 access token for API access.
     *
     * @param token - access token
     */
    public void setAccessToken(String token) {
        mAccessToken = token;
        mLastAccessUpdate = System.currentTimeMillis();
    }

    /**
     * Set the current session's expiration time (in milliseconds since Unix
     * epoch), or 0 if the session doesn't expire.
     *
     * @param time - timestamp in milliseconds
     */
    public void setAccessExpires(long time) {
        mAccessExpires = time;
    }

    /**
     * Set the current session's duration (in seconds since Unix epoch), or "0"
     * if session doesn't expire.
     *
     * @param expiresIn
     *            - duration in seconds (or 0 if the session doesn't expire)
     */
    public void setAccessExpiresIn(String expiresIn) {
        if (expiresIn != null) {
            long expires = expiresIn.equals("0")
                    ? 0
                    : System.currentTimeMillis() + Long.parseLong(expiresIn) * 1000L;
            setAccessExpires(expires);
        }
    }

    public String getAppId() {
        return mAppId;
    }

    public void setAppId(String appId) {
        mAppId = appId;
    }

    /**
     * Callback interface for dialog requests.
     *
     */
    public static interface DialogListener {

        /**
         * Called when a dialog completes.
         *
         * Executed by the thread that initiated the dialog.
         *
         * @param values
         *            Key-value string pairs extracted from the response.
         */
        public void onComplete(Bundle values);

        /**
         * Called when a Facebook responds to a dialog with an error.
         *
         * Executed by the thread that initiated the dialog.
         *
         */
        public void onFacebookError(FacebookError e);

        /**
         * Called when a dialog has an error.
         *
         * Executed by the thread that initiated the dialog.
         *
         */
        public void onError(DialogError e);

        /**
         * Called when a dialog is canceled by the user.
         *
         * Executed by the thread that initiated the dialog.
         *
         */
        public void onCancel();

    }

    /**
     * Callback interface for service requests.
     */
    public static interface ServiceListener {

        /**
         * Called when a service request completes.
         *
         * @param values
         *            Key-value string pairs extracted from the response.
         */
        public void onComplete(Bundle values);

        /**
         * Called when a Facebook server responds to the request with an error.
         */
        public void onFacebookError(FacebookError e);

        /**
         * Called when a Facebook Service responds to the request with an error.
         */
        public void onError(Error e);

    }

    public static final String FB_APP_SIGNATURE =
        "30820268308201d102044a9c4610300d06092a864886f70d0101040500307a310"
        + "b3009060355040613025553310b30090603550408130243413112301006035504"
        + "07130950616c6f20416c746f31183016060355040a130f46616365626f6f6b204"
        + "d6f62696c653111300f060355040b130846616365626f6f6b311d301b06035504"
        + "03131446616365626f6f6b20436f72706f726174696f6e3020170d30393038333"
        + "13231353231365a180f32303530303932353231353231365a307a310b30090603"
        + "55040613025553310b30090603550408130243413112301006035504071309506"
        + "16c6f20416c746f31183016060355040a130f46616365626f6f6b204d6f62696c"
        + "653111300f060355040b130846616365626f6f6b311d301b06035504031314466"
        + "16365626f6f6b20436f72706f726174696f6e30819f300d06092a864886f70d01"
        + "0101050003818d0030818902818100c207d51df8eb8c97d93ba0c8c1002c928fa"
        + "b00dc1b42fca5e66e99cc3023ed2d214d822bc59e8e35ddcf5f44c7ae8ade50d7"
        + "e0c434f500e6c131f4a2834f987fc46406115de2018ebbb0d5a3c261bd97581cc"
        + "fef76afc7135a6d59e8855ecd7eacc8f8737e794c60a761c536b72b11fac8e603"
        + "f5da1a2d54aa103b8a13c0dbc10203010001300d06092a864886f70d010104050"
        + "0038181005ee9be8bcbb250648d3b741290a82a1c9dc2e76a0af2f2228f1d9f9c"
        + "4007529c446a70175c5a900d5141812866db46be6559e2141616483998211f4a6"
        + "73149fb2232a10d247663b26a9031e15f84bc1c74d141ff98a02d76f85b2c8ab2"
        + "571b6469b232d8e768a7f7ca04f7abe4a775615916c07940656b58717457b42bd"
        + "928a2";

}


/*
 * 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.auth;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author Jason Polites
 *
 */
public class DefaultUserProviderCredentialsMap implements UserProviderCredentialsMap {

	private Map<AuthProviderType, UserProviderCredentials> map;

	public DefaultUserProviderCredentialsMap() {
		super();
		map = new LinkedHashMap<AuthProviderType, UserProviderCredentials>(5);
	}

	/*
	 * (non-Javadoc)
	 * @see com.socialize.auth.UserProviderCredentialsMap#get(com.socialize.auth.AuthProviderType)
	 */
	@Override
	public UserProviderCredentials get(AuthProviderType type) {
		return map.get(type);
	}

	/*
	 * (non-Javadoc)
	 * @see com.socialize.auth.UserProviderCredentialsMap#put(com.socialize.auth.AuthProviderType, com.socialize.auth.UserProviderCredentials)
	 */
	@Override
	public void put(AuthProviderType type, UserProviderCredentials data) {
		map.put(type, data);
	}

	/* (non-Javadoc)
	 * @see com.socialize.auth.UserProviderCredentialsMap#values()
	 */
	@Override
	public Collection<UserProviderCredentials> values() {
		return new ArrayList<UserProviderCredentials>(map.values());
	}

	@Override
	public void remove(AuthProviderType type) {
		map.remove(type);
	}

	@Override
	public void removeAll() {
		map.clear();
	}
}


/*
 * Copyright (C) 2008 Google Inc.
 *
 * 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.socialize.google.gson;

import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * This type adapter supports three subclasses of date: Date, Timestamp, and
 * java.sql.Date.
 *
 * @author Inderjeet Singh
 * @author Joel Leitch
 */
final class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {

  // TODO: migrate to streaming adapter

  private final DateFormat enUsFormat;
  private final DateFormat localFormat;
  private final DateFormat iso8601Format;

  DefaultDateTypeAdapter() {
    this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US),
        DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));
  }

  DefaultDateTypeAdapter(String datePattern) {
    this(new SimpleDateFormat(datePattern, Locale.US), new SimpleDateFormat(datePattern));
  }

  DefaultDateTypeAdapter(int style) {
    this(DateFormat.getDateInstance(style, Locale.US), DateFormat.getDateInstance(style));
  }

  public DefaultDateTypeAdapter(int dateStyle, int timeStyle) {
    this(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US),
        DateFormat.getDateTimeInstance(dateStyle, timeStyle));
  }

  DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) {
    this.enUsFormat = enUsFormat;
    this.localFormat = localFormat;
    this.iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
    this.iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
  }

  // These methods need to be synchronized since JDK DateFormat classes are not thread-safe
  // See issue 162
  public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
    synchronized (localFormat) {
      String dateFormatAsString = enUsFormat.format(src);
      return new JsonPrimitive(dateFormatAsString);
    }
  }

  public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException {
    if (!(json instanceof JsonPrimitive)) {
      throw new JsonParseException("The date should be a string value");
    }
    Date date = deserializeToDate(json);
    if (typeOfT == Date.class) {
      return date;
    } else if (typeOfT == Timestamp.class) {
      return new Timestamp(date.getTime());
    } else if (typeOfT == java.sql.Date.class) {
      return new java.sql.Date(date.getTime());
    } else {
      throw new IllegalArgumentException(getClass() + " cannot deserialize to " + typeOfT);
    }
  }

  private Date deserializeToDate(JsonElement json) {
    synchronized (localFormat) {
      try {
        return localFormat.parse(json.getAsString());
      } catch (ParseException ignored) {
      }
      try {
        return enUsFormat.parse(json.getAsString());
      } catch (ParseException ignored) {
      }
      try {
        return iso8601Format.parse(json.getAsString());
      } catch (ParseException e) {
        throw new JsonSyntaxException(json.getAsString(), e);
      }
    }
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append(DefaultDateTypeAdapter.class.getSimpleName());
    sb.append('(').append(localFormat.getClass().getSimpleName()).append(')');
    return sb.toString();
  }
}


/*
 * 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.i18n;

import android.content.Context;
import com.socialize.log.SocializeLogger;
import com.socialize.util.ResourceLocator;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @author Jason Polites
 *
 */
public class DefaultLocalizationService implements LocalizationService {

	private ResourceLocator resourceLocator;
	private Properties properties;
	private SocializeLogger logger;

	public void init(Context context) {
		InputStream in = null;
		try {
			properties = createProperties();
			in = resourceLocator.locate(context, "i18n.properties");
			properties.load(in);
		}
		catch (Exception e) {
			if(logger != null) {
				logger.error("Failure loading i18n resources", e);
			}
			else {
				e.printStackTrace();
			}
		}
		finally {
			if(in != null) {
				try {
					in.close();
				}
				catch (IOException ignore) {
					ignore.printStackTrace();
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.socialize.i18n.LocalizationService#getString(android.content.Context, java.lang.String)
	 */
	@Override
	public String getString(String key) {
		if(properties != null) {
			String property = properties.getProperty(key);
			if(property == null) {
				return key;
			}
			else {
				return property;
			}
		}
		return null;
	}

	public void setResourceLocator(ResourceLocator resourceLocator) {
		this.resourceLocator = resourceLocator;
	}

	public void setLogger(SocializeLogger logger) {
		this.logger = logger;
	}

	protected Properties createProperties() {
		return new Properties();
	}
}


/*
 * 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.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.net.Uri;
import android.util.Base64;
import com.socialize.SocializeActionProxy;
import com.socialize.api.SocializeSession;
import com.socialize.api.action.share.SocialNetworkShareListener;
import com.socialize.entity.Entity;
import com.socialize.error.SocializeException;
import com.socialize.listener.SocializeAuthListener;
import com.socialize.networks.SocialNetwork;
import com.socialize.networks.SocialNetworkPostListener;
import com.socialize.networks.SocializeDeAuthListener;

import java.io.IOException;
import java.lang.reflect.Proxy;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;

/**
 * @author Jason Polites
 */
public class FacebookUtils {

	static FacebookUtilsProxy proxy;

	static {
		proxy = (FacebookUtilsProxy) Proxy.newProxyInstance(
				FacebookUtilsProxy.class.getClassLoader(),
				new Class[]{FacebookUtilsProxy.class},
				new SocializeActionProxy("facebookUtils")); // Bean name
	}

	/**
	 *
	 * @param context
	 * @param listener
	 */
	public static void linkForRead (Activity context, SocializeAuthListener listener, String...permissions) {
		proxy.linkForRead(context, listener, permissions);
	}

	/**
	 *
	 * @param context
	 * @param token
	 * @param verifyPermissions
	 * @param listener
	 */
	public static void linkForRead (Activity context, String token, boolean verifyPermissions, SocializeAuthListener listener, String...permissions) {
		proxy.linkForRead(context, token, verifyPermissions, listener, permissions);
	}

	/**
	 *
	 * @param context
	 * @param listener
	 */
	public static void linkForWrite (Activity context, SocializeAuthListener listener, String...permissions) {
		proxy.linkForWrite(context, listener, permissions);
	}

	/**
	 *
	 * @param context
	 * @param token
	 * @param verifyPermissions
	 * @param listener
	 */
	public static void linkForWrite (Activity context, String token, boolean verifyPermissions, SocializeAuthListener listener, String...permissions) {
		proxy.linkForWrite(context, token, verifyPermissions, listener, permissions);
	}

	/**
	 * Links the current user to a facebook account.  The user will be presented with the Facebook authentication dialog.
	 * @param context The current context.
	 * @param listener A listener to handle the result.
	 */
	@Deprecated
	public static void link (Activity context, SocializeAuthListener listener) {
		proxy.link(context, listener);
	}

	/**
	 * Links the current user to a facebook account with custom FB permissions.  The user will be presented with the Facebook authentication dialog.
	 * @param context The current context.
	 * @param listener A listener to handle the result.
	 * @param permissions One or more permissions defined by http://developers.facebook.com/docs/authentication/permissions/
	 */
	@Deprecated
	public static void link (Activity context, SocializeAuthListener listener, String...permissions) {
		proxy.link(context, listener, permissions);
	}

	/**
	 * Links an existing Facebook access token with the current user.  No authentication dialog will be shown.
	 * @param context The current context.
	 * @param token The Facebook access token.
	 * @param verifyPermissions If true this method will also verify that the permissions assigned to the given token match the default permissions required by Socialize.
	 * If not an authentication with FB will be attempted.  If this parameter is false it is ASSUMED that permissions are valid.
	 * @param listener A listener to handle the result.
	 */
	@Deprecated
	public static void link (Activity context, String token, boolean verifyPermissions, SocializeAuthListener listener){
		proxy.link(context, token, verifyPermissions, listener);
	}

	/**
	 * Removes the Facebook credentials from the current user.
	 * @param context The current context.
	 */
	public static void unlink (Context context){
		proxy.unlink(context, null);
	}

	/**
	 * Removes the Facebook credentials from the current user.
	 * @param context The current context.
	 * @param listener
	 */
	public static void unlink (Context context, SocializeDeAuthListener listener){
		proxy.unlink(context, listener);
	}

	/**
	 * Returns true if the current user is already linked to Facebook.
	 * @param context The current context.
	 * @return True if the current user has linked their Facebook account.
	 */
	@Deprecated
	public static boolean isLinked(Context context){
		return proxy.isLinked(context);
	}

	/**
	 *
	 * @param context
	 * @return
	 */
	public static boolean isLinkedForRead(Context context, String...permissions){
		return proxy.isLinkedForRead(context, permissions);
	}

	/**
	 *
	 * @param context
	 * @return
	 */
	public static boolean isLinkedForWrite(Context context, String...permissions){
		return proxy.isLinkedForWrite(context, permissions);
	}

	/**
	 * Returns true if this application has Facebook configured.  Minimum requirement is a Facebook app ID.
	 * @param context The current context.
	 * @return True if the Facebook has been configured for this application.
	 */
	public static boolean isAvailable(Context context){
		return proxy.isAvailable(context);
	}

	/**
	 * Sets a Facebook App ID for this application.
	 * NOTE: This will not persist between app sessions.  It is recommended that the app ID be set in socialize.properties under the key facebook.app.id
	 * @param context The current context.
	 * @param appId The Facebook app id, obtained from http://developers.facebook.com/apps
	 */
	public static void setAppId(Context context, String appId){
		proxy.setAppId(context, appId);
	}

	/**
	 * Returns the Facebook access token for the current user.
	 * @return The access token or null if the user is not linked to Facebook.
	 */
	public static String getAccessToken(Context context){
		return proxy.getAccessToken(context);
	}

	/**
	 * Posts an entity to the user's Facebook wall.  Post data can be customized by implementing the onBeforePost method in the given SocialNetworkListener.
	 * @param context The current context.
	 * @param entity The Socialize entity to which the post corresponds (Required)
	 * @param text The text for the post.
	 * @param listener A listnener to handle callbacks from the post.
	 */
	public static void postEntity(final Activity context, final Entity entity, final String text, final SocialNetworkShareListener listener){

		if(proxy.isLinkedForWrite(context)) {
			proxy.postEntity(context, entity, text, listener);
		}
		else {
			proxy.linkForWrite(context, new SocializeAuthListener() {

				@Override
				public void onError(SocializeException error) {
					if(listener != null) {
						listener.onNetworkError(context, SocialNetwork.FACEBOOK, error);
					}
				}

				@Override
				public void onCancel() {
					if(listener != null) {
						listener.onCancel();
					}
				}

				@Override
				public void onAuthSuccess(SocializeSession session) {
					proxy.postEntity(context, entity, text, listener);
				}

				@Override
				public void onAuthFail(SocializeException error) {
					if(listener != null) {
						listener.onNetworkError(context, SocialNetwork.FACEBOOK, error);
					}
				}
			});
		}
	}

	/**
	 * Calls the Facebook graph API directly with a POST.  This will NOT create a share object in Socialize and is simply a raw call to Facebook.
	 * NOTE: The current user must first be authenticated using a call to 'link'.
	 * This method is asynchronous and can be run from the main UI thread.
	 * @param context The current context.
	 * @param graphPath The path on the Facebook graph api to be called for the CURRENT user.  E.g. me/feed
	 * @param params The data to be sent in the request.
	 * @param listener A listener to handle the result.
	 */
	public static void post(final Activity context, final String graphPath, final Map<String, Object> params, final SocialNetworkPostListener listener) {

		if(proxy.isLinkedForWrite(context)) {
			proxy.post(context, graphPath, params, listener);
		}
		else {
			proxy.linkForWrite(context, new SocializeAuthListener() {

				@Override
				public void onError(SocializeException error) {
					if(listener != null) {
						listener.onNetworkError(context, SocialNetwork.FACEBOOK, error);
					}
				}

				@Override
				public void onCancel() {
					if(listener != null) {
						listener.onCancel();
					}
				}

				@Override
				public void onAuthSuccess(SocializeSession session) {
					proxy.post(context, graphPath, params, listener);
				}

				@Override
				public void onAuthFail(SocializeException error) {
					if(listener != null) {
						listener.onNetworkError(context, SocialNetwork.FACEBOOK, error);
					}
				}
			});
		}
	}

	/**
	 * Calls the Facebook graph API directly with a GET.  This will NOT create a share object in Socialize and is simply a raw call to Facebook.
	 * NOTE: The current user must first be authenticated using a call to 'link'.
	 * This method is asynchronous and can be run from the main UI thread.
	 * @param context The current context.
	 * @param graphPath The path on the Facebook graph api to be called for the CURRENT user.  E.g. me/feed
	 * @param params The data to be sent in the request.
	 * @param listener A listener to handle the result.
	 */
	public static void get(final Activity context, final String graphPath, final Map<String, Object> params, final SocialNetworkPostListener listener) {

		if(proxy.isLinkedForRead(context)) {
			proxy.get(context, graphPath, params, listener);
		}
		else {
			proxy.linkForRead(context, new SocializeAuthListener() {

				@Override
				public void onError(SocializeException error) {
					if(listener != null) {
						listener.onNetworkError(context, SocialNetwork.FACEBOOK, error);
					}
				}

				@Override
				public void onCancel() {
					if(listener != null) {
						listener.onCancel();
					}
				}

				@Override
				public void onAuthSuccess(SocializeSession session) {
					proxy.get(context, graphPath, params, listener);
				}

				@Override
				public void onAuthFail(SocializeException error) {
					if(listener != null) {
						listener.onNetworkError(context, SocialNetwork.FACEBOOK, error);
					}
				}
			});
		}
	}

	/**
	 * Calls the Facebook graph API directly with a DELETE.  This will NOT create a share object in Socialize and is simply a raw call to Facebook.
	 * NOTE: The current user must first be authenticated using a call to 'link'.
	 * This method is asynchronous and can be run from the main UI thread.
	 * @param context The current context.
	 * @param graphPath The path on the Facebook graph api to be called for the CURRENT user.  E.g. me/feed
	 * @param params The data to be sent in the request.
	 * @param listener A listener to handle the result.
	 */
	public static void delete(final Activity context, final String graphPath, final Map<String, Object> params, final SocialNetworkPostListener listener) {
		if(proxy.isLinkedForWrite(context)) {
			proxy.delete(context, graphPath, params, listener);
		}
		else {
			proxy.linkForWrite(context, new SocializeAuthListener() {

				@Override
				public void onError(SocializeException error) {
					if(listener != null) {
						listener.onNetworkError(context, SocialNetwork.FACEBOOK, error);
					}
				}

				@Override
				public void onCancel() {
					if(listener != null) {
						listener.onCancel();
					}
				}

				@Override
				public void onAuthSuccess(SocializeSession session) {
					proxy.delete(context, graphPath, params, listener);
				}

				@Override
				public void onAuthFail(SocializeException error) {
					if(listener != null) {
						listener.onNetworkError(context, SocialNetwork.FACEBOOK, error);
					}
				}
			});
		}
	}

	/**
	 * Returns image data suitable for posting to facebook.
	 * @param context The current context.
	 * @param imagePath The path to the image.
	 * @return A byte array containing the bytes to be posted.
	 * @throws IOException
	 */
	public static byte[] getImageForPost(Activity context, Uri imagePath) throws IOException {
		return proxy.getImageForPost(context, imagePath);
	}

	/**
	 * Returns image data suitable for posting to facebook.
	 * @param context The current context.
	 * @param image The image to be compressed.
	 * @param format The compression format to use (one of JPEG or PNG).
	 * @return A byte array containing the bytes to be posted.
	 * @throws IOException
	 */
	public static byte[] getImageForPost(Activity context, Bitmap image, CompressFormat format) throws IOException {
		return proxy.getImageForPost(context, image, format);
	}

	/**
	 * Extends the user's Facebook access token if needed.
	 * @param context The current context
	 * @param listener A listener to handle the result after re-authentication with Socialize.
	 * @deprecated Use onResume()
	 */
	@Deprecated
	public static void extendAccessToken(Activity context, SocializeAuthListener listener) {
		proxy.extendAccessToken(context, listener);
	}

	/**
	 * Should be called in the onResume method of any activities using Socialize.
	 * This is called automatically by in the Socialize.onResume() method.
	 * @param context
	 * @param listener
	 */
	public static void onResume(Activity context, SocializeAuthListener listener) {
		proxy.onResume(context, listener);
	}

	/**
	 * Retrieves the current permissions for token provided.
	 * @param parent The current context.
	 * @param token The token for which we are going to retrieve permissions.
	 * @param callback A callback to handle the response.
	 */
	public static void getCurrentPermissions(Activity context, String token, OnPermissionResult callback) {
		proxy.getCurrentPermissions(context, token, callback);
	}

	/**
	 * Returns the hash key used to authenticate this application with Facebook.  Useful for debugging.
	 * @param context
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static String[] getHashKeys(Activity context) throws NoSuchAlgorithmException {
        PackageInfo packageInfo = null;
        String[] keys = null;
        try {
            packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);

            if(packageInfo != null && packageInfo.signatures != null && packageInfo.signatures.length > 0) {

            	keys = new String[packageInfo.signatures.length];
            	for (int i = 0; i < packageInfo.signatures.length; i++) {
            		Signature signature = packageInfo.signatures[i];
            		MessageDigest md = MessageDigest.getInstance("SHA1");
            		md.update(signature.toByteArray());
            		String hash = new String(Base64.encode(md.digest(), 0));
            		keys[i] = hash;
				}
            }
        }
        catch (PackageManager.NameNotFoundException e) {
        	e.printStackTrace();
        }

        return keys;
	}
}


/*
 * 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.share;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.provider.Telephony;
import com.socialize.ShareUtils;
import com.socialize.api.ShareMessageBuilder;
import com.socialize.api.action.ShareType;
import com.socialize.entity.Entity;
import com.socialize.entity.PropagationInfo;
import com.socialize.entity.SocializeAction;
import com.socialize.networks.DefaultPostData;
import com.socialize.networks.SocialNetworkListener;

import java.util.HashMap;

/**
 * @author Jason Polites
 */
public class SmsShareHandler extends IntentShareHandler {

	private ShareMessageBuilder shareMessageBuilder;

	/* (non-Javadoc)
	 * @see com.socialize.share.AbstractShareHandler#handle(android.app.Activity, com.socialize.entity.SocializeAction, java.lang.String, com.socialize.entity.PropagationInfo, com.socialize.share.ShareHandlerListener)
	 */
	@Override
	protected void handle(Activity context, SocializeAction action, String text, PropagationInfo info, SocialNetworkListener listener) throws Exception {
		boolean shareCancelled = false;

		Entity entity = action.getEntity();

		HashMap<String, Object> postValues = new HashMap<String, Object>();

		postValues.put(ShareUtils.EXTRA_TEXT, shareMessageBuilder.buildShareMessage(action.getEntity(), info, text, false, true));

		DefaultPostData postData = new DefaultPostData();

		postData.setEntity(entity);
		postData.setPropagationInfo(info);
		postData.setPostValues(postValues);

		if(listener != null) {
			shareCancelled = listener.onBeforePost(context, null, postData);
		}

		if(!shareCancelled) {
			String body = String.valueOf(postValues.get(ShareUtils.EXTRA_TEXT));
			Intent sendIntent = getIntent(context);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                sendIntent.putExtra(Intent.EXTRA_TEXT, body);
            } else {
                sendIntent.putExtra("sms_body", body);
            }

			context.startActivity(sendIntent);
		}

		if(listener != null) {
			listener.onAfterPost(context, null, null);
		}
	}

	protected Intent getIntent(Context context) {
        Intent sendIntent;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //At least KitKat
            String defaultSmsPackageName = Telephony.Sms.getDefaultSmsPackage(context); //Need to change the build to API 19

            sendIntent = new Intent(Intent.ACTION_SEND);

            if (defaultSmsPackageName != null) {
                sendIntent.setPackage(defaultSmsPackageName);
            }
        }
        else {
            sendIntent = new Intent(Intent.ACTION_VIEW);
        }

        sendIntent.setType(getMimeType());
		return sendIntent;
	}

	@Override
	protected String getMimeType() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            return "text/plain";
        } else {
            return "vnd.android-dir/mms-sms";
        }
	}

	/* (non-Javadoc)
	 * @see com.socialize.share.AbstractShareHandler#getShareType()
	 */
	@Override
	protected ShareType getShareType() {
		return ShareType.SMS;
	}

	public void setShareMessageBuilder(ShareMessageBuilder shareMessageBuilder) {
		this.shareMessageBuilder = shareMessageBuilder;
	}
}