package com.socialize.ui.comment;

import android.app.ProgressDialog;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.socialize.ConfigUtils;
import com.socialize.android.ioc.IBeanFactory;
import com.socialize.api.SocializeSession;
import com.socialize.api.action.comment.CommentOptions;
import com.socialize.api.action.comment.CommentUtilsProxy;
import com.socialize.api.action.comment.SubscriptionUtilsProxy;
import com.socialize.api.action.user.UserUtilsProxy;
import com.socialize.config.SocializeConfig;
import com.socialize.entity.*;
import com.socialize.error.SocializeException;
import com.socialize.i18n.I18NConstants;
import com.socialize.listener.comment.CommentAddListener;
import com.socialize.listener.comment.CommentListListener;
import com.socialize.listener.subscription.SubscriptionCheckListener;
import com.socialize.listener.subscription.SubscriptionResultListener;
import com.socialize.log.SocializeLogger;
import com.socialize.notifications.SubscriptionType;
import com.socialize.ui.dialog.SimpleDialogFactory;
import com.socialize.ui.header.SocializeHeader;
import com.socialize.ui.image.ImageLoader;
import com.socialize.ui.profile.UserSettings;
import com.socialize.ui.slider.ActionBarSliderFactory;
import com.socialize.ui.slider.ActionBarSliderFactory.ZOrder;
import com.socialize.ui.slider.ActionBarSliderView;
import com.socialize.ui.util.CompatUtils;
import com.socialize.ui.view.CustomCheckbox;
import com.socialize.ui.view.LoadingListView;
import com.socialize.util.*;
import com.socialize.view.BaseView;

import java.util.List;

public class CommentListView extends BaseView {

	private int defaultGrabLength = 30;
	private int iconSize = 100;

	private IBeanFactory<CommentAdapter> commentAdapterFactory;
	private CommentAdapter commentAdapter;
	private boolean loading = true; // Default to true

	private boolean headerDisplayed = true;

	private Entity entity;

	private int startIndex = 0;
	private int endIndex = defaultGrabLength;

	private SocializeLogger logger;
	private SimpleDialogFactory<ProgressDialog> progressDialogFactory;
	private Drawables drawables;
	private AppUtils appUtils;
	private DisplayUtils displayUtils;
	private CommentUtilsProxy commentUtils;
	private SubscriptionUtilsProxy subscriptionUtils;
	private UserUtilsProxy userUtils;
	private ProgressDialog dialog = null;
	private SocializeConfig config;

	private IBeanFactory<SocializeHeader> commentHeaderFactory;

	private IBeanFactory<CommentEditField> commentEditFieldFactory;
	private IBeanFactory<LoadingListView> commentContentViewFactory;
	private IBeanFactory<CustomCheckbox> notificationEnabledOptionFactory;

	private CommentEditField commentEntryField;
	private SocializeHeader header;
	private LoadingListView content;
	private String customHeaderText;
	private boolean showCommentCountInHeader = true;

	private IBeanFactory<CommentEntrySliderItem> commentEntryFactory;

	private ActionBarSliderView commentEntrySlider;

	private ActionBarSliderFactory<ActionBarSliderView> sliderFactory;

	private RelativeLayout layoutAnchor;
	private ViewGroup sliderAnchor;
	private CustomCheckbox notifyBox;
	private ImageLoader imageLoader;

	private CommentEntrySliderItem commentEntrySliderItem;

	private OnCommentViewActionListener onCommentViewActionListener;

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

	public void init() {

		commentAdapter = commentAdapterFactory.getBean();

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

		setOrientation(LinearLayout.VERTICAL);
		setLayoutParams(fill);

		CompatUtils.setBackgroundDrawable(this, drawables.getDrawable("crosshatch.png", true, true, true));

		setPadding(0, 0, 0, 0);

		boolean landscape = displayUtils.isLandscape();

		iconSize = displayUtils.getDIP(iconSize);

		layoutAnchor = new RelativeLayout(getContext());

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

		layoutAnchor.setLayoutParams(innerParams);

		LinearLayout top = new LinearLayout(getContext());
		LinearLayout middle = new LinearLayout(getContext());
		sliderAnchor = new LinearLayout(getContext());

		int topId = getNextViewId(getParentView());
		int middleId = getNextViewId(getParentView());
		int bottomId = getNextViewId(getParentView());

		top.setPadding(0, 0, 0, 0);
		middle.setPadding(0, 0, 0, 0);
		sliderAnchor.setPadding(0, 0, 0, 0);

		top.setId(topId);
		middle.setId(middleId);
		sliderAnchor.setId(bottomId);

		RelativeLayout.LayoutParams topParams = new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
		RelativeLayout.LayoutParams middleParams = new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.FILL_PARENT);
		RelativeLayout.LayoutParams bottomParams = new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);

		topParams.setMargins(0, 0, 0, 0);
		middleParams.setMargins(0, 0, 0, 0);
		bottomParams.setMargins(0, 0, 0, 0);

		topParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
		bottomParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);

		middleParams.addRule(RelativeLayout.BELOW, topId);
		middleParams.addRule(RelativeLayout.ABOVE, bottomId);

		top.setLayoutParams(topParams);
		middle.setLayoutParams(middleParams);
		sliderAnchor.setLayoutParams(bottomParams);

		if(!landscape && config.isShowCommentHeader() && headerDisplayed) {
			header = commentHeaderFactory.getBean();
		}

		commentEntryField = commentEditFieldFactory.getBean();
		content = commentContentViewFactory.getBean();

		if(commentEntryFactory != null) {
			commentEntrySliderItem = commentEntryFactory.getBean(getCommentAddButtonListener());
		}

		boolean notificationsAvailable = appUtils.isNotificationsAvailable(getContext());

		if(notificationsAvailable) {
			notifyBox = notificationEnabledOptionFactory.getBean();
			notifyBox.setEnabled(false); // Start disabled
			notifyBox.setOnClickListener(new OnClickListener() {
				@Override
				public void onClick(View v) {
					doNotificationStatusSave();
				}
			});

			notifyBox.setVisibility(View.GONE);

			try {
				UserSettings user = userUtils.getUserSettings(getContext());
				if(user.isNotificationsEnabled()) {
					notifyBox.setVisibility(View.VISIBLE);
				}
			}
			catch (SocializeException e) {
				if(logger != null) {
					logger.error("Error getting user settings", e);
				}
				else {
					e.printStackTrace();
				}
			}

			sliderAnchor.addView(notifyBox);
		}

		content.setListAdapter(commentAdapter);
		content.setScrollListener(getCommentScrollListener());

		if(header != null) {
			top.addView(header);
		}

		middle.addView(content);

		sliderAnchor.addView(commentEntryField);

		layoutAnchor.addView(top);
		layoutAnchor.addView(middle);
		layoutAnchor.addView(sliderAnchor);

		addView(layoutAnchor);

		setDefaultGrabLength(ConfigUtils.getConfig(getContext()).getIntProperty("comment.page.size", 20));
	}

	protected CommentScrollListener getCommentScrollListener() {
		return new CommentScrollListener(new CommentScrollCallback() {
			@Override
			public void onGetNextSet() {
				getNextSet();
			}

			@Override
			public boolean isLoading() {
				return loading;
			}

			@Override
			public boolean hasMoreItems() {
				return !commentAdapter.isLast();
			}
		});
	}

	protected CommentAddButtonListener getCommentAddButtonListener() {
		return new CommentAddButtonListener(new CommentButtonCallback() {

			@Override
			public void onError(Context context, SocializeException e) {
				showError(getContext(), e);
				if(onCommentViewActionListener != null) {
					onCommentViewActionListener.onError(e);
				}
			}

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

			@Override
			public void onComment(String text, boolean shareLocation, boolean subscribe) {
				text = StringUtils.replaceNewLines(text, 3, 2);

				if(progressDialogFactory != null) {
					dialog = progressDialogFactory.show(getContext(), I18NConstants.DLG_COMMENT, I18NConstants.PLEASE_WAIT);
				}

				CommentOptions options = newShareOptions();
				options.setSubscribeToUpdates(subscribe);
				options.setShowAuthDialog(true);
				options.setShowShareDialog(true);

				commentUtils.addComment(CommentListView.this.getActivity(), entity, text, options, getCommentAddListener(subscribe));

				// Won't persist.. but that's ok.
				SocializeSession session = getSocialize().getSession();

				if(session != null && session.getUserSettings() != null) {
					session.getUserSettings().setLocationEnabled(shareLocation);
				}
			}
		});
	}

	protected CommentAddListener getCommentAddListener(final boolean subscribe) {
		return new CommentAddListener() {

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

				if(onCommentViewActionListener != null) {
					onCommentViewActionListener.onError(error);
				}

				if(commentEntrySliderItem != null) {
					commentEntrySliderItem.getCommentEntryView().getPostCommentButton().setEnabled(true);
				}
			}

			@Override
			public void onCreate(Comment entity) {
				List<Comment> comments = commentAdapter.getComments();
				if(comments != null) {
					comments.add(0, entity);
				}

				startIndex++;
				endIndex++;

				if(commentEntrySlider != null) {
					commentEntrySlider.clearContent();
					commentEntrySlider.close();
				}

				commentAdapter.setTotalCount(commentAdapter.getTotalCount() + 1);
				commentAdapter.notifyDataSetChanged();

				setHeaderText();

				content.scrollToTop();

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

				if(notifyBox != null) {
					notifyBox.setChecked(subscribe);
				}

				if(onCommentViewActionListener != null) {
					onCommentViewActionListener.onPostComment(entity);
				}

				if(commentEntrySliderItem != null) {
					commentEntrySliderItem.getCommentEntryView().getPostCommentButton().setEnabled(true);
				}
			}

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

				if(commentEntrySliderItem != null) {
					commentEntrySliderItem.getCommentEntryView().getPostCommentButton().setEnabled(true);
				}
			}
		};
	}

	public void reload() {
		content.showLoading();
		commentAdapter.reset();

		// We have to re-set the adapter.. otherwise we get crazy scrolling behavior
		// http://support.getsocialize.com/socialize/topics/problems_rendering_of_chat_room_using_android_2_8_2?utm_content=topic_link&utm_medium=email&utm_source=reply_notification
		content.setListAdapter(commentAdapter);

		if(onCommentViewActionListener != null) {
			onCommentViewActionListener.onReload(this);
		}

		doListComments(true);
	}

	public CommentAdapter getCommentAdapter() {
		return commentAdapter;
	}

	public void doListComments(final boolean update) {

		startIndex = 0;
		endIndex = defaultGrabLength;

		loading = true;

		final List<Comment> comments = commentAdapter.getComments();

		if(update || comments == null || comments.size() == 0) {

			commentUtils.getCommentsByEntity(getActivity(), entity.getKey(),
					startIndex,
					endIndex, new CommentListListener() {

				@Override
				public void onError(SocializeException error) {
					showError(getContext(), error);
					content.showList();

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

					loading = false;

					if(onCommentViewActionListener != null) {
						onCommentViewActionListener.onError(error);
					}
				}

				@Override
				public void onList(ListResult<Comment> entities) {

					if(entities != null) {
						int totalCount = entities.getTotalCount();

						List<Comment> items = entities.getItems();
						commentAdapter.setComments(items);
						commentAdapter.setTotalCount(totalCount);

						setHeaderText();

						if(totalCount <= endIndex) {
							commentAdapter.setLast(true);
						}

						if(update || comments == null || comments.size() == 0) {
							content.scrollToTop();
						}

						commentAdapter.notifyDataSetChanged();
					}

					content.showList();

					loading = false;

					if(onCommentViewActionListener != null && entities != null) {
						onCommentViewActionListener.onCommentList(CommentListView.this, entities.getItems(), startIndex, endIndex);
					}

					if(dialog != null)  dialog.dismiss();
				}
			});
		}
		else {
			content.showList();
			setHeaderText();
			commentAdapter.notifyDataSetChanged();
			if(dialog != null) {
				dialog.dismiss();
			}

			loading = false;

			if(onCommentViewActionListener != null) {
				onCommentViewActionListener.onCommentList(CommentListView.this, comments, startIndex, endIndex);
			}
		}
	}

	// SO we can mock
	protected Comment newComment() {
		return new Comment();
	}

	protected void setHeaderText() {
		if(header != null) {
			String name = (customHeaderText == null) ? entity.getName() : customHeaderText;
			if(StringUtils.isEmpty(name)) {
				if(showCommentCountInHeader) {
					header.setText(commentAdapter.getTotalCount() + " Comments");
				}
			}
			else {
				if(showCommentCountInHeader) {
					header.setText(name + " (" + commentAdapter.getTotalCount() + ")");
				}
				else {
					header.setText(name);
				}
			}
		}
	}

	protected void doNotificationStatusSave() {

		notifyBox.showLoading();

		if(notifyBox.isChecked()) {

			subscriptionUtils.subscribe(getActivity(), entity, SubscriptionType.NEW_COMMENTS, new SubscriptionResultListener() {
				@Override
				public void onError(SocializeException error) {
					showError(getContext(), error);
					notifyBox.setChecked(false);
					notifyBox.hideLoading();
				}

				@Override
				public void onCreate(Subscription entity) {
					if(commentEntrySliderItem != null) {
						commentEntrySliderItem.getCommentEntryView().setNotifySubscribeState(true);
					}
					notifyBox.hideLoading();
				}
			});
		}
		else {
			subscriptionUtils.unsubscribe(getActivity(), entity, SubscriptionType.NEW_COMMENTS, new SubscriptionResultListener() {
				@Override
				public void onError(SocializeException error) {
					showError(getContext(), error);
					notifyBox.setChecked(true);
					notifyBox.hideLoading();
				}

				@Override
				public void onCreate(Subscription entity) {
					if(commentEntrySliderItem != null) {
						commentEntrySliderItem.getCommentEntryView().setNotifySubscribeState(false);
					}
					notifyBox.hideLoading();
				}
			});
		}
	}

	protected void doNotificationStatusLoad() {
		if(notifyBox != null) {
			notifyBox.showLoading();

			// Now load the subscription status for the user
			subscriptionUtils.isSubscribed(getActivity(), entity, SubscriptionType.NEW_COMMENTS, new SubscriptionCheckListener() {

				@Override
				public void onSubscribed(Subscription subscription) {
					notifyBox.setChecked(subscription.isSubscribed());

					if(commentEntrySliderItem != null) {
						commentEntrySliderItem.getCommentEntryView().setNotifySubscribeState(subscription.isSubscribed());
					}

					notifyBox.hideLoading();
				}

				@Override
				public void onNotSubscribed() {
					notifyBox.setChecked(false);

					if(commentEntrySliderItem != null) {
						commentEntrySliderItem.getCommentEntryView().setNotifySubscribeState(true); // Default to true
					}

					notifyBox.hideLoading();
				}

				@Override
				public void onError(SocializeException error) {
					notifyBox.setChecked(false);

					if(logger != null) {
						logger.error("Error retrieving subscription info", error);
					}
					else {
						SocializeLogger.e(error.getMessage(), error);
					}

					if(commentEntrySliderItem != null) {
						commentEntrySliderItem.getCommentEntryView().setNotifySubscribeState(true); // Default to true
					}

					notifyBox.hideLoading();
				}
			});
		}
	}

	public void getNextSet() {

		if(logger != null && logger.isDebugEnabled()) {
			logger.debug("getNextSet called on CommentListView");
		}

		loading = true; // Prevent continuous load

		startIndex = endIndex;
		endIndex+=defaultGrabLength;

		int totalCount = commentAdapter.getTotalCount();

		if(endIndex > totalCount) {
			endIndex = totalCount;

			if(startIndex >= endIndex) {
				commentAdapter.setLast(true);
				commentAdapter.notifyDataSetChanged();
				loading = false;
				return;
			}
		}

		commentUtils.getCommentsByEntity(getActivity(), entity.getKey(),
				startIndex,
				endIndex,
				new CommentListListener() {

			@Override
			public void onError(SocializeException error) {

				// Don't show loading anymore
				if(logger != null) {
					logger.error("Error retrieving comments", error);
				}
				else {
					SocializeLogger.e(error.getMessage(), error);
				}

				loading = false;

				if(onCommentViewActionListener != null) {
					onCommentViewActionListener.onError(error);
				}
			}

			@Override
			public void onList(ListResult<Comment> entities) {
				List<Comment> comments = commentAdapter.getComments();

				if(entities != null) {
					comments.addAll(entities.getItems());
					preLoadImages(comments);
				}

				commentAdapter.setComments(comments);
				commentAdapter.notifyDataSetChanged();
				loading = false;

				if(onCommentViewActionListener != null && entities != null) {
					onCommentViewActionListener.onCommentList(CommentListView.this, comments, startIndex, endIndex);
				}
			}
		});
	}

	protected void preLoadImages(List<Comment> comments) {
		if(comments != null) {
			for (Comment comment : comments) {
				User user = comment.getUser();
				if(user != null) {
					String imageUrl = user.getSmallImageUri();
					if(!StringUtils.isEmpty(imageUrl)) {
						CacheableDrawable cached = drawables.getCache().get(imageUrl);
						if(cached == null || cached.isRecycled()) {
							imageLoader.loadImageByUrl(imageUrl, iconSize, iconSize, null);
						}
					}
				}
			}
		}
	}

	@Override
	public void onViewLoad() {
		super.onViewLoad();
		if(onCommentViewActionListener != null) {
			onCommentViewActionListener.onCreate(this);
		}

		if(commentAdapter != null && onCommentViewActionListener != null) {
			commentAdapter.setOnCommentViewActionListener(onCommentViewActionListener);
		}
	}

	@Override
	public void onViewRendered(int width, int height) {
		if(sliderFactory != null) {
			if(commentEntrySlider == null) {
				commentEntrySlider = sliderFactory.wrap(getSliderAnchor(), ZOrder.FRONT, height);
			}
		}

		if(commentEntrySliderItem != null) {

			commentEntrySlider.loadItem(commentEntrySliderItem);

			commentEntryField.setOnClickListener(new OnClickListener() {
				@Override
				public void onClick(View v) {
					if(commentEntrySlider != null) {
						commentEntrySlider.showSliderItem(commentEntrySliderItem);
					}
				}
			});
		}
		else if(commentEntryField != null) {
			commentEntryField.setVisibility(GONE);
		}

		if(getSocialize().isAuthenticated()) {
			doListComments(false);
			doNotificationStatusLoad();
		}
		else {
			SocializeException e = new SocializeException("Socialize not authenticated");

			showError(getContext(),e);

			if(onCommentViewActionListener != null) {
				onCommentViewActionListener.onError(e);
			}

			if(content != null) {
				content.showList();
			}
		}

		if(onCommentViewActionListener != null) {
			onCommentViewActionListener.onRender(this);
		}
	}

	public void setCommentAdapter(CommentAdapter commentAdapter) {
		this.commentAdapter = commentAdapter;
	}

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

	public void setProgressDialogFactory(SimpleDialogFactory<ProgressDialog> progressDialogFactory) {
		this.progressDialogFactory = progressDialogFactory;
	}

	public void setDrawables(Drawables drawables) {
		this.drawables = drawables;
	}

	public void setDefaultGrabLength(int defaultGrabLength) {
		this.defaultGrabLength = defaultGrabLength;
	}

	public void setCommentHeaderFactory(IBeanFactory<SocializeHeader> commentHeaderFactory) {
		this.commentHeaderFactory = commentHeaderFactory;
	}

	public void setCommentEditFieldFactory(IBeanFactory<CommentEditField> commentEditFieldFactory) {
		this.commentEditFieldFactory = commentEditFieldFactory;
	}

	public void setCommentContentViewFactory(IBeanFactory<LoadingListView> commentContentViewFactory) {
		this.commentContentViewFactory = commentContentViewFactory;
	}

	public void setAppUtils(AppUtils appUtils) {
		this.appUtils = appUtils;
	}

	public void setNotificationEnabledOptionFactory(IBeanFactory<CustomCheckbox> notificationEnabledOptionFactory) {
		this.notificationEnabledOptionFactory = notificationEnabledOptionFactory;
	}

	public Entity getEntity() {
		return entity;
	}

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

	public boolean isLoading() {
		return loading;
	}

	protected void setLoading(boolean loading) {
		this.loading = loading;
	}

	protected void setCommentEntryField(CommentEditField field) {
		this.commentEntryField = field;
	}

	protected void setHeader(SocializeHeader header) {
		this.header = header;
	}

	protected void setContent(LoadingListView content) {
		this.content = content;
	}

	protected void setStartIndex(int startIndex) {
		this.startIndex = startIndex;
	}

	protected void setEndIndex(int endIndex) {
		this.endIndex = endIndex;
	}

	public int getStartIndex() {
		return startIndex;
	}

	public int getEndIndex() {
		return endIndex;
	}

	public void setCommentEntryFactory(IBeanFactory<CommentEntrySliderItem> commentEntryFactory) {
		this.commentEntryFactory = commentEntryFactory;
	}

	public int getTotalCount() {
		return commentAdapter.getTotalCount();
	}

	/**
	 * Called when the current logged in user updates their profile.
	 */
	public void onProfileUpdate() {
		commentAdapter.notifyDataSetChanged();

		if(commentEntrySlider != null) {
			commentEntrySlider.updateContent();
		}

		if(notifyBox != null) {
			try {
				UserSettings user = userUtils.getUserSettings(getContext());
				if(user.isNotificationsEnabled()) {
					notifyBox.setVisibility(View.VISIBLE);
				}
				else {
					notifyBox.setVisibility(View.GONE);
				}
			}
			catch (SocializeException e) {
				if(logger != null) {
					logger.error("Error getting user settings", e);
				}
				else {
					e.printStackTrace();
				}

				notifyBox.setVisibility(View.GONE);
			}
		}
	}

	// So we can mock
	protected CommentOptions newShareOptions() {
		return commentUtils.getUserCommentOptions(getContext());
	}

	protected RelativeLayout getLayoutAnchor() {
		return layoutAnchor;
	}

	protected ViewGroup getSliderAnchor() {
		return sliderAnchor;
	}

	public void setSliderFactory(ActionBarSliderFactory<ActionBarSliderView> sliderFactory) {
		this.sliderFactory = sliderFactory;
	}

	public void setOnCommentViewActionListener(OnCommentViewActionListener onCommentViewActionListener) {
		this.onCommentViewActionListener = onCommentViewActionListener;

		if(commentAdapter != null) {
			commentAdapter.setOnCommentViewActionListener(onCommentViewActionListener);
		}
	}

	public ActionBarSliderView getCommentEntryViewSlider() {
		return commentEntrySlider;
	}

	public void setImageLoader(ImageLoader imageLoader) {
		this.imageLoader = imageLoader;
	}

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

	public void setCommentUtils(CommentUtilsProxy commentUtils) {
		this.commentUtils = commentUtils;
	}

	public void setSubscriptionUtils(SubscriptionUtilsProxy subscriptionUtils) {
		this.subscriptionUtils = subscriptionUtils;
	}

	public void setUserUtils(UserUtilsProxy userUtils) {
		this.userUtils = userUtils;
	}

	public void setConfig(SocializeConfig config) {
		this.config = config;
	}

	public CommentEditField getCommentEntryField() {
		return commentEntryField;
	}

	public String getCustomHeaderText() {
		return customHeaderText;
	}

	public void setCustomHeaderText(String customHeaderText) {
		this.customHeaderText = customHeaderText;
	}

	public boolean isShowCommentCountInHeader() {
		return showCommentCountInHeader;
	}

	public void setShowCommentCountInHeader(boolean showCommentCountInHeader) {
		this.showCommentCountInHeader = showCommentCountInHeader;
	}

	public void setCommentAdapterFactory(IBeanFactory<CommentAdapter> commentAdapterFactory) {
		this.commentAdapterFactory = commentAdapterFactory;
	}

	public SocializeHeader getHeader() {
		return header;
	}

	public LoadingListView getContent() {
		return content;
	}

	public void setHeaderDisplayed(boolean showHeader) {
		this.headerDisplayed = showHeader;
		if(header != null) {
			if(headerDisplayed) {
				header.setVisibility(View.VISIBLE);
			}
			else {
				header.setVisibility(View.GONE);
			}
		}
	}
}


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

import com.socialize.log.SocializeLogger;

import java.util.Map;

/**
 * @author Jason Polites
 *
 */
public class SocializeLaunchManager implements LaunchManager {

	private Map<String, Launcher> launchers;
	private SocializeLogger logger;

	/* (non-Javadoc)
	 * @see com.socialize.launcher.LaunchManager#getLaucher(java.lang.String)
	 */
	@Override
	public Launcher getLaucher(String action) {
		try {
			return getLaucher(LaunchAction.valueOf(action));
		} catch (Exception e) {

			if(logger != null) {
				logger.error("Launch action [" +
						action +
						"] provided is not a known action", e);
			}
			else {
				SocializeLogger.e(e.getMessage(), e);
			}
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see com.socialize.launcher.LaunchManager#getLaucher(com.socialize.launcher.LaunchAction)
	 */
	@Override
	public Launcher getLaucher(LaunchAction action) {
		if(launchers != null) {
			return launchers.get(action.name());
		}
		return null;
	}

	public void setLaunchers(Map<String, Launcher> launchers) {
		this.launchers = launchers;
	}

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


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

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.telephony.TelephonyManager;
import com.socialize.Socialize;
import com.socialize.SocializeService;
import com.socialize.auth.facebook.FacebookActivity;
import com.socialize.config.SocializeConfig;
import com.socialize.log.SocializeLogger;
import com.socialize.ui.SocializeLaunchActivity;
import com.socialize.ui.action.ActionDetailActivity;
import com.socialize.ui.comment.CommentActivity;
import com.socialize.ui.profile.ProfileActivity;

import java.util.List;
import java.util.Locale;

/**
 * @author Jason Polites
 */
public class DefaultAppUtils implements AppUtils {

	private String packageName;
	private String appName;
	private String userAgent;
	private String country;
	private String carrier = "unknown";
	private String networkType = "unknown";
	private SocializeLogger logger;
	private SocializeConfig config;

	private boolean locationAvailable = false;
	private boolean locationAssessed = false;

	private boolean notificationsAvailable = false;
	private boolean notificationsAssessed = false;

	private String lastNotificationWarning = null;

	public void init(Context context) {

		packageName = context.getPackageName();

		// Try to get the app name
		try {
			PackageManager pkgManager = context.getPackageManager();
			appName = pkgManager.getApplicationLabel(pkgManager.getApplicationInfo(packageName, 0)).toString();
		}
		catch (Exception e) {
			String msg = "Failed to lookup application label.  Make sure this is specified in your AndroidManifest.xml";

			if(logger != null) {
				logger.error(msg, e);
			}
			else {
				SocializeLogger.e(msg, e);
			}
		}

		if(StringUtils.isEmpty(appName)) {
			appName = packageName;
		}

		if(StringUtils.isEmpty(appName)) {
			appName = "A Socialize enabled app";
		}

		try {

			if(hasPermission(context, "android.permission.READ_PHONE_STATE")) {
				TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
				country = manager.getNetworkCountryIso();
				carrier = manager.getNetworkOperatorName();

				if(StringUtils.isEmpty(carrier)) {
					carrier = "unknown";
				}
			}
		}
		catch (Exception ignore) {}

		if(StringUtils.isEmpty(country)) {
			country = Locale.getDefault().getCountry();
		}
	}

	@Override
	public void onResume(Context context) {
		determineNetworkType(context);
	}

	private void determineNetworkType(Context context) {
		try {
			if(hasPermission(context, "android.permission.ACCESS_NETWORK_STATE")) {
				ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
				NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

				if(mWifi != null && mWifi.isConnected()) {
					networkType = "wifi";
				}
				else {
					networkType = "cell";
				}
			}
		}
		catch (Exception ignore) {}
	}

	@Override
	public String getAppStoreAbbreviation(String appStore) {
		if(appStore != null && appStore.equalsIgnoreCase("amazon")) {
			return "amz";
		}
		return null;
	}

	@Override
	public boolean isActivityAvailable(Context context, String activityClassName) {
		try {
			Class<?> activityClass = Class.forName(activityClassName);
			return isActivityAvailable(context, activityClass);
		}
		catch (ClassNotFoundException ignored) {}

		return false;
	}

	/* (non-Javadoc)
	 * @see com.socialize.util.IAppUtils#isActivityAvailable(android.content.Context, java.lang.Class)
	 */
	@Override
	public boolean isActivityAvailable(Context context, Class<?> activity) {
		Intent intent = new Intent(context, activity);
		return isIntentAvailable(context, intent);
	}

	/* (non-Javadoc)
	 * @see com.socialize.util.IAppUtils#isIntentAvailable(android.content.Context, java.lang.String)
	 */
	@Override
	public boolean isIntentAvailable(Context context, String action) {
		Intent intent = new Intent(action);
		return isIntentAvailable(context, intent);
	}

	/* (non-Javadoc)
	 * @see com.socialize.util.IAppUtils#isIntentAvailable(android.content.Context, android.content.Intent)
	 */
	@Override
	public boolean isIntentAvailable(Context context, Intent intent) {
		PackageManager packageManager = context.getPackageManager();
		return packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
	}

	/* (non-Javadoc)
	 * @see com.socialize.util.IAppUtils#isServiceAvailable(android.content.Context, java.lang.Class)
	 */
	@Override
	public boolean isServiceAvailable(Context context, Class<?> cls) {
	    final PackageManager packageManager = context.getPackageManager();
	    final Intent intent = new Intent(context, cls);
	    return packageManager.queryIntentServices(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
	}

	@Override
	public ActivityInfo getActivityInfo(Context context, Class<?> cls) {
		try {
			final PackageManager packageManager = context.getPackageManager();
			return packageManager.getActivityInfo(new ComponentName(context, cls), 0);
		}
		catch (NameNotFoundException e) {
			if(logger != null) {
				logger.error("Failed to locate info for activity [" +
						cls.getName() +
						"]", e);
			}
			else {
				SocializeLogger.e(e.getMessage(), e);
			}
		}

		return null;
	}

	/* (non-Javadoc)
	 * @see com.socialize.util.IAppUtils#isReceiverAvailable(android.content.Context, java.lang.Class)
	 */
	@Override
	public boolean isReceiverAvailable(Context context, Class<?> cls) {
	    final PackageManager packageManager = context.getPackageManager();
	    final Intent intent = new Intent(context, cls);
	    return packageManager.queryBroadcastReceivers(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
	}

	/* (non-Javadoc)
	 * @see com.socialize.util.IAppUtils#isLocationAvaiable(android.content.Context)
	 */
	@Override
	public boolean isLocationAvailable(Context context) {
		if(!locationAssessed && config != null) {
			locationAvailable = config.getBooleanProperty(SocializeConfig.SOCIALIZE_LOCATION_ENABLED, true) && (hasPermission(context, "android.permission.ACCESS_FINE_LOCATION") || hasPermission(context, "android.permission.ACCESS_COARSE_LOCATION"));
			locationAssessed = true;
		}
		return locationAvailable;
	}

	/* (non-Javadoc)
	 * @see com.socialize.util.IAppUtils#isNotificationsAvaiable(android.content.Context)
	 */
	@Override
	public boolean isNotificationsAvailable(Context context) {

		if(!notificationsAssessed) {

		    String permissionString = context.getPackageName() + ".permission.C2D_MESSAGE";

			boolean ok = true;

			if(config.getBooleanProperty(SocializeConfig.SOCIALIZE_NOTIFICATIONS_ENABLED, true)) {
				if(!hasPermission(context, permissionString)) {
					lastNotificationWarning = "Notifications not available, permission [" + permissionString + "] not specified in AndroidManifest.xml";
					if(logger.isInfoEnabled()) logger.info(lastNotificationWarning);
					ok = false;
				}

				if(!hasPermission(context, "com.google.android.c2dm.permission.RECEIVE")) {
					lastNotificationWarning = "Notifications not available, permission com.google.android.c2dm.permission.RECEIVE not specified in AndroidManifest.xml, or device does not include Google APIs";
					if(logger.isInfoEnabled()) logger.info(lastNotificationWarning);
					ok = false;
				}

				if(config.isEntityLoaderCheckEnabled() && getSocialize().getEntityLoader() == null) {
					lastNotificationWarning = "Notifications not available. Entity loader not found.";
					if(logger.isInfoEnabled()) logger.info(lastNotificationWarning);
					ok = false;
				}
			}
			else {
				ok = false;
				if(logger.isDebugEnabled()) {
					logger.debug("SmartAlerts disabled in config");
				}
			}

			notificationsAvailable = ok;
			notificationsAssessed = true;
		}
		else if(!notificationsAvailable) {
			if(lastNotificationWarning != null && logger.isInfoEnabled()) {
				logger.info(lastNotificationWarning);
			}
		}

		return notificationsAvailable;
	}

	protected SocializeService getSocialize() {
		return Socialize.getSocialize();
	}

	@Override
	public void checkAndroidManifest(Context context) {
		// Check the launch activity config
		checkActivitiesExist(context, CommentActivity.class, ActionDetailActivity.class, ProfileActivity.class, FacebookActivity.class, SocializeLaunchActivity.class);

		ActivityInfo info = getActivityInfo(context, SocializeLaunchActivity.class);
		if(info != null) {
			if((info.flags & ActivityInfo.FLAG_NO_HISTORY) != ActivityInfo.FLAG_NO_HISTORY) {
				logger.warn("Activity flag android:noHistory=\"true\" not found for " + SocializeLaunchActivity.class.getSimpleName() + ".  Please ensure this is added to the declaration of this activity in your AndroidManifest.xml");
			}

			if((info.launchMode & ActivityInfo.LAUNCH_SINGLE_TOP) == ActivityInfo.LAUNCH_SINGLE_TOP) {
				logger.warn("Activity flag android:launchMode=\"singleTop\" found for " + SocializeLaunchActivity.class.getSimpleName() + ".  This should be removed from the declaration of this activity in your AndroidManifest.xml");
			}
		}

		info = getActivityInfo(context, CommentActivity.class);

		if(info != null) {
			if((info.configChanges & ActivityInfo.CONFIG_ORIENTATION) != ActivityInfo.CONFIG_ORIENTATION) {
				logger.warn("Activity flag android:configChanges=\"orientation|keyboardHidden|screenSize\" not found for " + CommentActivity.class.getSimpleName() + ".  Please ensure this is added to the declaration of this activity in your AndroidManifest.xml");
			}
		}
	}

	protected void checkActivitiesExist(Context context, Class<?>...classes) {
		for (Class<?> cls : classes) {
			ActivityInfo info = getActivityInfo(context, cls);

			if(info == null) {
				logger.warn("No activity element declared for [" +
						cls.getName() +
						"].  Please ensure you have included this in your AndroidManifest.xml");
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.socialize.util.IAppUtils#hasPermission(android.content.Context, java.lang.String)
	 */
	@Override
	public boolean hasPermission(Context context, String permission) {
		return context.getPackageManager().checkPermission(permission, context.getPackageName()) == PackageManager.PERMISSION_GRANTED;
	}

	public static boolean launchMainApp(Activity origin) {
		Intent mainIntent = getMainAppIntent(origin);
		if(mainIntent != null) {
			origin.startActivity(mainIntent);
			return true;
		}
		return false;
	}

	public static Intent getMainAppIntent(Context context) {
		PackageManager pm = context.getPackageManager();

		Intent mainIntent = new Intent(Intent.ACTION_MAIN);
		mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
		mainIntent.setPackage(context.getPackageName());

		List<ResolveInfo> appList = pm.queryIntentActivities(mainIntent, 0);

		if(appList != null && appList.size() > 0) {
			ResolveInfo resolveInfo = appList.get(0);
			mainIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
			mainIntent.setComponent(new ComponentName(resolveInfo.activityInfo.applicationInfo.packageName, resolveInfo.activityInfo.name));
			return mainIntent;
		}

		return null;
	}

	/**
	 * Attempts to get the resource if for the app icon.
	 * @param context
	 * @return
	 */
	public int getAppIconId(Context context) {
		ApplicationInfo applicationInfo = context.getApplicationInfo();
		return applicationInfo.icon;
	}

	public String getUserAgentString() {
		if (userAgent == null) {
			userAgent = "Android-" + android.os.Build.VERSION.SDK_INT + "/" + android.os.Build.MODEL + " SocializeSDK/v" + Socialize.VERSION + "; " + Locale.getDefault().getLanguage() + "_"
					+ getCountry() + "; BundleID/" + getPackageName() + "; Carrier/" + carrier + ";";
		}

		// Always add the network state in case it changed
		return userAgent + " Network/" + networkType + ";";
	}

	/* (non-Javadoc)
	 * @see com.socialize.util.IAppUtils#getAppName()
	 */
	@Override
	public String getAppName() {
		return appName;
	}

	@Override
	public String getCountry() {
		return country;
	}

	@Override
	public String getCarrier() {
		return carrier;
	}

	@Override
	public String getNetworkType() {
		return networkType;
	}

	@Override
	public String getPackageName() {
		return packageName;
	}

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

	public void setConfig(SocializeConfig config) {
		this.config = config;
	}

	void setNotificationsAssessed(boolean notificationsAssessed) {
		this.notificationsAssessed = notificationsAssessed;
	}
}


/*
 * 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.api.action.activity;

import com.socialize.api.SocializeApi;
import com.socialize.api.SocializeSession;
import com.socialize.api.action.ActionType;
import com.socialize.api.action.comment.CommentSystem;
import com.socialize.api.action.like.LikeSystem;
import com.socialize.api.action.share.ShareSystem;
import com.socialize.api.action.view.ViewSystem;
import com.socialize.entity.SocializeAction;
import com.socialize.error.SocializeException;
import com.socialize.listener.activity.ActionListener;
import com.socialize.provider.SocializeProvider;

/**
 * @author Jason Polites
 */
public class SocializeActivitySystem extends SocializeApi<SocializeAction, SocializeProvider<SocializeAction>> implements ActivitySystem {

	public SocializeActivitySystem(SocializeProvider<SocializeAction> provider) {
		super(provider);
	}

	/*
	 * (non-Javadoc)
	 * @see com.socialize.api.action.ActivitySystem#getAction(com.socialize.api.SocializeSession, long, com.socialize.api.action.ActionType)
	 */
	@Override
	public SocializeAction getAction(SocializeSession session, long id, ActionType type) throws SocializeException {
		String endpoint = null;
		switch(type) {

			case COMMENT:
				endpoint = CommentSystem.ENDPOINT;
				break;
			case LIKE:
				endpoint = LikeSystem.ENDPOINT;
				break;
			case SHARE:
				endpoint = ShareSystem.ENDPOINT;
				break;
			case VIEW:
				endpoint = ViewSystem.ENDPOINT;
				break;
		}

		return get(session, endpoint, String.valueOf(id), type);
	}

	/*
	 * (non-Javadoc)
	 * @see com.socialize.api.action.activity.ActivitySystem#getActivityByUser(com.socialize.api.SocializeSession, long, com.socialize.listener.activity.ActionListener)
	 */
	@Override
	public void getActivityByUser(SocializeSession session, long id, ActionListener listener) {
		String userId = String.valueOf(id);
		String endpoint = getEndpoint(userId);
		listAsync(session, endpoint, listener);
	}

	/*
	 * (non-Javadoc)
	 * @see com.socialize.api.action.activity.ActivitySystem#getActivityByUser(com.socialize.api.SocializeSession, long, int, int, com.socialize.listener.activity.ActionListener)
	 */
	@Override
	public void getActivityByUser(SocializeSession session, long id, int startIndex, int endIndex, ActionListener listener) {
		String userId = String.valueOf(id);
		String endpoint = getEndpoint(userId);
		listAsync(session, endpoint, startIndex, endIndex, listener);
	}

	@Override
	public void getActivityByUserAndEntity(SocializeSession session, long userId, String entityKey, int startIndex, int endIndex, ActionListener listener) {
		String strId = String.valueOf(userId);
		String endpoint = getEndpoint(strId);
		listAsync(session, endpoint, entityKey, startIndex, endIndex, listener);
	}

	@Override
	public void getActivityByApplication(SocializeSession session, int startIndex, int endIndex, ActionListener listener) {
		listAsync(session, ACTIVITY_ENDPOINT, startIndex, endIndex, listener);
	}

	@Override
	public void getActivityByEntity(SocializeSession session, String entityKey, int startIndex, int endIndex, ActionListener listener) {
		listAsync(session, ACTIVITY_ENDPOINT, entityKey, startIndex, endIndex, listener);
	}

	protected String getEndpoint(String id) {
		return USER_ENDPOINT + id + ACTIVITY_ENDPOINT;
	}
}


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

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import com.socialize.LikeUtils;
import com.socialize.demo.R;
import com.socialize.entity.Entity;
import com.socialize.ui.actionbutton.LikeButtonListener;

/**
 * @author Jason Polites
 *
 */
public class LikeButtonSnippets extends Activity {

// begin-snippet-0
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);

	// Define/obtain your entity
	Entity entity = Entity.newInstance("http://getsocialize.com", "Socialize");

	// Get a reference to the button you want to transform
	// This can be any type of CompoundButton (CheckBox, RadioButton, Switch, ToggleButton)

	CheckBox btnCustomCheckBoxLike = (CheckBox) findViewById(R.id.btnCustomCheckBoxLike);

	// Make the button a socialize like button!
	LikeUtils.makeLikeButton(this, btnCustomCheckBoxLike, entity, new LikeButtonListener() {

		@Override
		public void onClick(CompoundButton button) {
			// You can use this callback to set any loading text or display a progress dialog
			button.setText("--");
		}

		@Override
		public void onCheckedChanged(CompoundButton button, boolean isChecked) {
			// The like was posted successfully, change the button to reflect the change
			if(isChecked) {
				button.setText("Unlike");
			}
			else {
				button.setText("Like");
			}
		}

		@Override
		public void onError(CompoundButton button, Exception error) {
			// An error occurred posting the like, we need to return the button to its original state
			Log.e("Socialize", "Error on like button", error);
			if(button.isChecked()) {
				button.setText("Unlike");
			}
			else {
				button.setText("Like");
			}
		}
	});
}
//end-snippet-0
}


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

import com.socialize.EntityUtils;
import com.socialize.demo.SDKDemoActivity;
import com.socialize.entity.Entity;
import com.socialize.entity.ListResult;
import com.socialize.error.SocializeException;
import com.socialize.listener.entity.EntityListListener;

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

	/* (non-Javadoc)
	 * @see com.socialize.demo.DemoActivity#executeDemo()
	 */
	@Override
	public void executeDemo(String text) {

		EntityUtils.getEntities(this, 0, PAGE_SIZE, new EntityListListener() {

			@Override
			public void onList(ListResult<Entity> entities) {
				handleBasicSocializeResult(entities);
			}

			@Override
			public void onError(SocializeException error) {
				handleError(GetAllEntitiesActivity.this, error);
			}
		});
	}

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

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


/* Copyright (c) 2008, 2009 Netflix, 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;

import android.util.Log;
import com.socialize.google.gdata.util.common.base.PercentEscaper;
import com.socialize.oauth.signpost.http.HttpParameters;

import java.io.*;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class OAuth {

    public static final String VERSION_1_0 = "1.0";

    public static final String ENCODING = "UTF-8";

    public static final String FORM_ENCODED = "application/x-www-form-urlencoded";

    public static final String HTTP_AUTHORIZATION_HEADER = "Authorization";

    public static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key";

    public static final String OAUTH_TOKEN = "oauth_token";

    public static final String OAUTH_TOKEN_SECRET = "oauth_token_secret";

    public static final String OAUTH_SIGNATURE_METHOD = "oauth_signature_method";

    public static final String OAUTH_SIGNATURE = "oauth_signature";

    public static final String OAUTH_TIMESTAMP = "oauth_timestamp";

    public static final String OAUTH_NONCE = "oauth_nonce";

    public static final String OAUTH_VERSION = "oauth_version";

    public static final String OAUTH_CALLBACK = "oauth_callback";

    public static final String OAUTH_CALLBACK_CONFIRMED = "oauth_callback_confirmed";

    public static final String OAUTH_VERIFIER = "oauth_verifier";

    /**
     * Pass this value as the callback "url" upon retrieving a request token if
     * your application cannot receive callbacks (e.g. because it's a desktop
     * app). This will tell the service provider that verification happens
     * out-of-band, which basically means that it will generate a PIN code (the
     * OAuth verifier) and display that to your user. You must obtain this code
     * from your user and pass it to
     * {@link OAuthProvider#retrieveAccessToken(OAuthConsumer, String)} in order
     * to complete the token handshake.
     */
    public static final String OUT_OF_BAND = "oob";

    private static final PercentEscaper percentEncoder = new PercentEscaper(
            "-._~", false);

    public static String percentEncode(String s) {
        if (s == null) {
            return "";
        }
        return percentEncoder.escape(s);
    }

    public static String percentDecode(String s) {
        try {
            if (s == null) {
                return "";
            }
            return URLDecoder.decode(s, ENCODING);
            // This implements http://oauth.pbwiki.com/FlexibleDecoding
        } catch (java.io.UnsupportedEncodingException wow) {
            throw new RuntimeException(wow.getMessage(), wow);
        }
    }

    /**
     * Construct a x-www-form-urlencoded document containing the given sequence
     * of name/value pairs. Use OAuth percent encoding (not exactly the encoding
     * mandated by x-www-form-urlencoded).
     */
    public static <T extends Map.Entry<String, String>> void formEncode(Collection<T> parameters,
            OutputStream into) throws IOException {
        if (parameters != null) {
            boolean first = true;
            for (Map.Entry<String, String> entry : parameters) {
                if (first) {
                    first = false;
                } else {
                    into.write('&');
                }
                into.write(percentEncode(safeToString(entry.getKey())).getBytes());
                into.write('=');
                into.write(percentEncode(safeToString(entry.getValue())).getBytes());
            }
        }
    }

    /**
     * Construct a x-www-form-urlencoded document containing the given sequence
     * of name/value pairs. Use OAuth percent encoding (not exactly the encoding
     * mandated by x-www-form-urlencoded).
     */
    public static <T extends Map.Entry<String, String>> String formEncode(Collection<T> parameters)
            throws IOException {
        ByteArrayOutputStream b = new ByteArrayOutputStream();
        formEncode(parameters, b);
        return new String(b.toByteArray());
    }

    /** Parse a form-urlencoded document. */
    public static HttpParameters decodeForm(String form) {
        HttpParameters params = new HttpParameters();
        if (isEmpty(form)) {
            return params;
        }
        for (String nvp : form.split("\\&")) {
            int equals = nvp.indexOf('=');
            String name;
            String value;
            if (equals < 0) {
                name = percentDecode(nvp);
                value = null;
            } else {
                name = percentDecode(nvp.substring(0, equals));
                value = percentDecode(nvp.substring(equals + 1));
            }

            params.put(name, value);
        }
        return params;
    }

    public static HttpParameters decodeForm(InputStream content)
            throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                content));
        StringBuilder sb = new StringBuilder();
        String line = reader.readLine();
        while (line != null) {
            sb.append(line);
            line = reader.readLine();
        }

        return decodeForm(sb.toString());
    }

    /**
     * Construct a Map containing a copy of the given parameters. If several
     * parameters have the same name, the Map will contain the first value,
     * only.
     */
    public static <T extends Map.Entry<String, String>> Map<String, String> toMap(Collection<T> from) {
        HashMap<String, String> map = new HashMap<String, String>();
        if (from != null) {
            for (Map.Entry<String, String> entry : from) {
                String key = entry.getKey();
                if (!map.containsKey(key)) {
                    map.put(key, entry.getValue());
                }
            }
        }
        return map;
    }

    public static final String safeToString(Object from) {
        return (from == null) ? null : from.toString();
    }

    public static boolean isEmpty(String str) {
        return (str == null) || (str.length() == 0);
    }

    /**
     * Appends a list of key/value pairs to the given URL, e.g.:
     *
     *

     * String url = OAuth.addQueryParameters("http://example.com?a=1", b, 2, c, 3);
     *

     *
     * which yields:
     *
     *

     * http://example.com?a=1&b=2&c=3
     *

     *
     * All parameters will be encoded according to OAuth's percent encoding
     * rules.
     *
     * @param url
     *        the URL
     * @param kvPairs
     *        the list of key/value pairs
     * @return
     */
    public static String addQueryParameters(String url, String... kvPairs) {
        String queryDelim = url.contains("?") ? "&" : "?";
        StringBuilder sb = new StringBuilder(url + queryDelim);
        for (int i = 0; i < kvPairs.length; i += 2) {
            if (i > 0) {
                sb.append("&");
            }
            sb.append(OAuth.percentEncode(kvPairs[i]) + "="
                    + OAuth.percentEncode(kvPairs[i + 1]));
        }
        return sb.toString();
    }

    public static String addQueryParameters(String url, Map<String, String> params) {
        String[] kvPairs = new String[params.size() * 2];
        int idx = 0;
        for (String key : params.keySet()) {
            kvPairs[idx] = key;
            kvPairs[idx + 1] = params.get(key);
            idx += 2;
        }
        return addQueryParameters(url, kvPairs);
    }

    /**
     * Builds an OAuth header from the given list of header fields. All
     * parameters starting in 'oauth_*' will be percent encoded.
     *
     *

     * String authHeader = OAuth.prepareOAuthHeader("realm", "http://example.com", "oauth_token", "x%y");
     *

     *
     * which yields:
     *
     *

     * OAuth realm="http://example.com", oauth_token="x%25y"
     *

     *
     * @param kvPairs
     *        the list of key/value pairs
     * @return a string eligible to be used as an OAuth HTTP Authorization
     *         header.
     */
    public static String prepareOAuthHeader(String... kvPairs) {
        StringBuilder sb = new StringBuilder("OAuth ");
        for (int i = 0; i < kvPairs.length; i += 2) {
            if (i > 0) {
                sb.append(", ");
            }
            String value = kvPairs[i].startsWith("oauth_") ? OAuth
                .percentEncode(kvPairs[i + 1]) : kvPairs[i + 1];
            sb.append(OAuth.percentEncode(kvPairs[i]) + "=\"" + value + "\"");
        }
        return sb.toString();
    }

    public static HttpParameters oauthHeaderToParamsMap(String oauthHeader) {
        HttpParameters params = new HttpParameters();
        if (oauthHeader == null || !oauthHeader.startsWith("OAuth ")) {
            return params;
        }
        oauthHeader = oauthHeader.substring("OAuth ".length());
        String[] elements = oauthHeader.split(",");
        for (String keyValuePair : elements) {
            String[] keyValue = keyValuePair.split("=");
            params.put(keyValue[0].trim(), keyValue[1].replace("\"", "").trim());
        }
        return params;
    }

    /**
     * Helper method to concatenate a parameter and its value to a pair that can
     * be used in an HTTP header. This method percent encodes both parts before
     * joining them.
     *
     * @param name
     *        the OAuth parameter name, e.g. oauth_token
     * @param value
     *        the OAuth parameter value, e.g. 'hello oauth'
     * @return a name/value pair, e.g. oauth_token="hello%20oauth"
     */
    public static String toHeaderElement(String name, String value) {
        return OAuth.percentEncode(name) + "=\"" + OAuth.percentEncode(value) + "\"";
    }

    public static void debugOut(String key, String value) {
        if (System.getProperty("debug") != null) {
        	Log.i("[SIGNPOST]", key + ": " + value);
        }
    }
}


/*
 * Copyright (C) 2007 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.gdata.util.common.base;

import java.util.Collection;
import java.util.NoSuchElementException;

/**
 * Simple static methods to be called at the start of your own methods to verify
 * correct arguments and state. This allows constructs such as
 *

 *     if (count <= 0) {
 *       throw new IllegalArgumentException("must be positive: " + count);
 *     }

 *
 * to be replaced with the more compact
 *

 *     checkArgument(count > 0, "must be positive: %s", count);

 *
 * Note that the sense of the expression is inverted; with {@code Preconditions}
 * you declare what you expect to be true, just as you do with an
 *
 * {@code assert} or a JUnit {@code assertTrue()} call.
 *
 *
Take care not to confuse precondition checking with other similar types
 * of checks! Precondition exceptions -- including those provided here, but also
 * {@link IndexOutOfBoundsException}, {@link NoSuchElementException}, {@link
 * UnsupportedOperationException} and others -- are used to signal that the
 * calling method has made an error. This tells the caller that it should
 * not have invoked the method when it did, with the arguments it did, or
 * perhaps ever. Postcondition or other invariant failures should not
 * throw these types of exceptions.
 *
 *
Note: The methods of the {@code Preconditions} class are highly
 * unusual in one way: they are supposed to throw exceptions, and promise
 * in their specifications to do so even when given perfectly valid input. That
 * is, {@code null} is a valid parameter to the method {@link
 * #checkNotNull(Object)} -- and technically this parameter could be even marked
 * as {@link Nullable} -- yet the method will still throw an exception anyway,
 * because that's what its contract says to do.
 *
 *
This class may be used with the Google Web Toolkit (GWT).
 *
 *
 */
public final class Preconditions {
  private Preconditions() {}

  /**
   * Ensures the truth of an expression involving one or more parameters to the
   * calling method.
   *
   * @param expression a boolean expression
   * @throws IllegalArgumentException if {@code expression} is false
   */
  public static void checkArgument(boolean expression) {
    if (!expression) {
      throw new IllegalArgumentException();
    }
  }

  /**
   * Ensures the truth of an expression involving one or more parameters to the
   * calling method.
   *
   * @param expression a boolean expression
   * @param errorMessage the exception message to use if the check fails; will
   *     be converted to a string using {@link String#valueOf(Object)}
   * @throws IllegalArgumentException if {@code expression} is false
   */
  public static void checkArgument(boolean expression, Object errorMessage) {
    if (!expression) {
      throw new IllegalArgumentException(String.valueOf(errorMessage));
    }
  }

  /**
   * Ensures the truth of an expression involving one or more parameters to the
   * calling method.
   *
   * @param expression a boolean expression
   * @param errorMessageTemplate a template for the exception message should the
   *     check fail. The message is formed by replacing each {@code %s}
   *     placeholder in the template with an argument. These are matched by
   *     position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc.
   *     Unmatched arguments will be appended to the formatted message in square
   *     braces. Unmatched placeholders will be left as-is.
   * @param errorMessageArgs the arguments to be substituted into the message
   *     template. Arguments are converted to strings using
   *     {@link String#valueOf(Object)}.
   * @throws IllegalArgumentException if {@code expression} is false
   * @throws NullPointerException if the check fails and either {@code
   *     errorMessageTemplate} or {@code errorMessageArgs} is null (don't let
   *     this happen)
   */
  public static void checkArgument(boolean expression,
      String errorMessageTemplate, Object... errorMessageArgs) {
    if (!expression) {
      throw new IllegalArgumentException(
          format(errorMessageTemplate, errorMessageArgs));
    }
  }

  /**
   * Ensures the truth of an expression involving the state of the calling
   * instance, but not involving any parameters to the calling method.
   *
   * @param expression a boolean expression
   * @throws IllegalStateException if {@code expression} is false
   */
  public static void checkState(boolean expression) {
    if (!expression) {
      throw new IllegalStateException();
    }
  }

  /**
   * Ensures the truth of an expression involving the state of the calling
   * instance, but not involving any parameters to the calling method.
   *
   * @param expression a boolean expression
   * @param errorMessage the exception message to use if the check fails; will
   *     be converted to a string using {@link String#valueOf(Object)}
   * @throws IllegalStateException if {@code expression} is false
   */
  public static void checkState(boolean expression, Object errorMessage) {
    if (!expression) {
      throw new IllegalStateException(String.valueOf(errorMessage));
    }
  }

  /**
   * Ensures the truth of an expression involving the state of the calling
   * instance, but not involving any parameters to the calling method.
   *
   * @param expression a boolean expression
   * @param errorMessageTemplate a template for the exception message should the
   *     check fail. The message is formed by replacing each {@code %s}
   *     placeholder in the template with an argument. These are matched by
   *     position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc.
   *     Unmatched arguments will be appended to the formatted message in square
   *     braces. Unmatched placeholders will be left as-is.
   * @param errorMessageArgs the arguments to be substituted into the message
   *     template. Arguments are converted to strings using
   *     {@link String#valueOf(Object)}.
   * @throws IllegalStateException if {@code expression} is false
   * @throws NullPointerException if the check fails and either {@code
   *     errorMessageTemplate} or {@code errorMessageArgs} is null (don't let
   *     this happen)
   */
  public static void checkState(boolean expression,
      String errorMessageTemplate, Object... errorMessageArgs) {
    if (!expression) {
      throw new IllegalStateException(
          format(errorMessageTemplate, errorMessageArgs));
    }
  }

  /**
   * Ensures that an object reference passed as a parameter to the calling
   * method is not null.
   *
   * @param reference an object reference
   * @return the non-null reference that was validated
   * @throws NullPointerException if {@code reference} is null
   */
  public static <T> T checkNotNull(T reference) {
    if (reference == null) {
      throw new NullPointerException();
    }
    return reference;
  }

  /**
   * Ensures that an object reference passed as a parameter to the calling
   * method is not null.
   *
   * @param reference an object reference
   * @param errorMessage the exception message to use if the check fails; will
   *     be converted to a string using {@link String#valueOf(Object)}
   * @return the non-null reference that was validated
   * @throws NullPointerException if {@code reference} is null
   */
  public static <T> T checkNotNull(T reference, Object errorMessage) {
    if (reference == null) {
      throw new NullPointerException(String.valueOf(errorMessage));
    }
    return reference;
  }

  /**
   * Ensures that an object reference passed as a parameter to the calling
   * method is not null.
   *
   * @param reference an object reference
   * @param errorMessageTemplate a template for the exception message should the
   *     check fail. The message is formed by replacing each {@code %s}
   *     placeholder in the template with an argument. These are matched by
   *     position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc.
   *     Unmatched arguments will be appended to the formatted message in square
   *     braces. Unmatched placeholders will be left as-is.
   * @param errorMessageArgs the arguments to be substituted into the message
   *     template. Arguments are converted to strings using
   *     {@link String#valueOf(Object)}.
   * @return the non-null reference that was validated
   * @throws NullPointerException if {@code reference} is null
   */
  public static <T> T checkNotNull(T reference, String errorMessageTemplate,
      Object... errorMessageArgs) {
    if (reference == null) {
      // If either of these parameters is null, the right thing happens anyway
      throw new NullPointerException(
          format(errorMessageTemplate, errorMessageArgs));
    }
    return reference;
  }

  /**
   * Ensures that an {@code Iterable} object passed as a parameter to the
   * calling method is not null and contains no null elements.
   *
   * @param iterable the iterable to check the contents of
   * @return the non-null {@code iterable} reference just validated
   * @throws NullPointerException if {@code iterable} is null or contains at
   *     least one null element
   */
  public static <T extends Iterable<?>> T checkContentsNotNull(T iterable) {
    if (containsOrIsNull(iterable)) {
      throw new NullPointerException();
    }
    return iterable;
  }

  /**
   * Ensures that an {@code Iterable} object passed as a parameter to the
   * calling method is not null and contains no null elements.
   *
   * @param iterable the iterable to check the contents of
   * @param errorMessage the exception message to use if the check fails; will
   *     be converted to a string using {@link String#valueOf(Object)}
   * @return the non-null {@code iterable} reference just validated
   * @throws NullPointerException if {@code iterable} is null or contains at
   *     least one null element
   */
  public static <T extends Iterable<?>> T checkContentsNotNull(
      T iterable, Object errorMessage) {
    if (containsOrIsNull(iterable)) {
      throw new NullPointerException(String.valueOf(errorMessage));
    }
    return iterable;
  }

  /**
   * Ensures that an {@code Iterable} object passed as a parameter to the
   * calling method is not null and contains no null elements.
   *
   * @param iterable the iterable to check the contents of
   * @param errorMessageTemplate a template for the exception message should the
   *     check fail. The message is formed by replacing each {@code %s}
   *     placeholder in the template with an argument. These are matched by
   *     position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc.
   *     Unmatched arguments will be appended to the formatted message in square
   *     braces. Unmatched placeholders will be left as-is.
   * @param errorMessageArgs the arguments to be substituted into the message
   *     template. Arguments are converted to strings using
   *     {@link String#valueOf(Object)}.
   * @return the non-null {@code iterable} reference just validated
   * @throws NullPointerException if {@code iterable} is null or contains at
   *     least one null element
   */
  public static <T extends Iterable<?>> T checkContentsNotNull(T iterable,
      String errorMessageTemplate, Object... errorMessageArgs) {
    if (containsOrIsNull(iterable)) {
      throw new NullPointerException(
          format(errorMessageTemplate, errorMessageArgs));
    }
    return iterable;
  }

  private static boolean containsOrIsNull(Iterable<?> iterable) {
    if (iterable == null) {
      return true;
    }

    if (iterable instanceof Collection) {
      Collection<?> collection = (Collection<?>) iterable;
      try {
        return collection.contains(null);
      } catch (NullPointerException e) {
        // A NPE implies that the collection doesn't contain null.
        return false;
      }
    } else {
      for (Object element : iterable) {
        if (element == null) {
          return true;
        }
      }
      return false;
    }
  }

  /**
   * Ensures that {@code index} specifies a valid element in an array,
   * list or string of size {@code size}. An element index may range from zero,
   * inclusive, to {@code size}, exclusive.
   *
   * @param index a user-supplied index identifying an element of an array, list
   *     or string
   * @param size the size of that array, list or string
   * @throws IndexOutOfBoundsException if {@code index} is negative or is not
   *     less than {@code size}
   * @throws IllegalArgumentException if {@code size} is negative
   */
  public static void checkElementIndex(int index, int size) {
    checkElementIndex(index, size, "index");
  }

  /**
   * Ensures that {@code index} specifies a valid element in an array,
   * list or string of size {@code size}. An element index may range from zero,
   * inclusive, to {@code size}, exclusive.
   *
   * @param index a user-supplied index identifying an element of an array, list
   *     or string
   * @param size the size of that array, list or string
   * @param desc the text to use to describe this index in an error message
   * @throws IndexOutOfBoundsException if {@code index} is negative or is not
   *     less than {@code size}
   * @throws IllegalArgumentException if {@code size} is negative
   */
  public static void checkElementIndex(int index, int size, String desc) {
    checkArgument(size >= 0, "negative size: %s", size);
    if (index < 0) {
      throw new IndexOutOfBoundsException(
          format("%s (%s) must not be negative", desc, index));
    }
    if (index >= size) {
      throw new IndexOutOfBoundsException(
          format("%s (%s) must be less than size (%s)", desc, index, size));
    }
  }

  /**
   * Ensures that {@code index} specifies a valid position in an array,
   * list or string of size {@code size}. A position index may range from zero
   * to {@code size}, inclusive.
   *
   * @param index a user-supplied index identifying a position in an array, list
   *     or string
   * @param size the size of that array, list or string
   * @throws IndexOutOfBoundsException if {@code index} is negative or is
   *     greater than {@code size}
   * @throws IllegalArgumentException if {@code size} is negative
   */
  public static void checkPositionIndex(int index, int size) {
    checkPositionIndex(index, size, "index");
  }

  /**
   * Ensures that {@code index} specifies a valid position in an array,
   * list or string of size {@code size}. A position index may range from zero
   * to {@code size}, inclusive.
   *
   * @param index a user-supplied index identifying a position in an array, list
   *     or string
   * @param size the size of that array, list or string
   * @param desc the text to use to describe this index in an error message
   * @throws IndexOutOfBoundsException if {@code index} is negative or is
   *     greater than {@code size}
   * @throws IllegalArgumentException if {@code size} is negative
   */
  public static void checkPositionIndex(int index, int size, String desc) {
    checkArgument(size >= 0, "negative size: %s", size);
    if (index < 0) {
      throw new IndexOutOfBoundsException(format(
          "%s (%s) must not be negative", desc, index));
    }
    if (index > size) {
      throw new IndexOutOfBoundsException(format(
          "%s (%s) must not be greater than size (%s)", desc, index, size));
    }
  }

  /**
   * Ensures that {@code start} and {@code end} specify a valid positions
   * in an array, list or string of size {@code size}, and are in order. A
   * position index may range from zero to {@code size}, inclusive.
   *
   * @param start a user-supplied index identifying a starting position in an
   *     array, list or string
   * @param end a user-supplied index identifying a ending position in an array,
   *     list or string
   * @param size the size of that array, list or string
   * @throws IndexOutOfBoundsException if either index is negative or is
   *     greater than {@code size}, or if {@code end} is less than {@code start}
   * @throws IllegalArgumentException if {@code size} is negative
   */
  public static void checkPositionIndexes(int start, int end, int size) {
    checkPositionIndex(start, size, "start index");
    checkPositionIndex(end, size, "end index");
    if (end < start) {
      throw new IndexOutOfBoundsException(format(
          "end index (%s) must not be less than start index (%s)", end, start));
    }
  }

  /**
   * Substitutes each {@code %s} in {@code template} with an argument. These
   * are matched by position - the first {@code %s} gets {@code args[0]}, etc.
   * If there are more arguments than placeholders, the unmatched arguments will
   * be appended to the end of the formatted message in square braces.
   *
   * @param template a non-null string containing 0 or more {@code %s}
   *     placeholders.
   * @param args the arguments to be substituted into the message
   *     template. Arguments are converted to strings using
   *     {@link String#valueOf(Object)}. Arguments can be null.
   */
  // VisibleForTesting
  static String format(String template, Object... args) {
    // start substituting the arguments into the '%s' placeholders
    StringBuilder builder = new StringBuilder(
        template.length() + 16 * args.length);
    int templateStart = 0;
    int i = 0;
    while (i < args.length) {
      int placeholderStart = template.indexOf("%s", templateStart);
      if (placeholderStart == -1) {
        break;
      }
      builder.append(template.substring(templateStart, placeholderStart));
      builder.append(args[i++]);
      templateStart = placeholderStart + 2;
    }
    builder.append(template.substring(templateStart));

    // if we run out of placeholders, append the extra args in square braces
    if (i < args.length) {
      builder.append(" [");
      builder.append(args[i++]);
      while (i < args.length) {
        builder.append(", ");
        builder.append(args[i++]);
      }
      builder.append("]");
    }

    return builder.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.demo.implementations.action;

import com.socialize.ActionUtils;
import com.socialize.UserUtils;
import com.socialize.demo.SDKDemoActivity;
import com.socialize.entity.ListResult;
import com.socialize.entity.SocializeAction;
import com.socialize.error.SocializeException;
import com.socialize.listener.activity.ActionListListener;

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

	/* (non-Javadoc)
	 * @see com.socialize.demo.DemoActivity#executeDemo()
	 */
	@Override
	public void executeDemo(String text) {

		try {
			ActionUtils.getActionsByUserAndEntity(this, UserUtils.getCurrentUser(this).getId(), entity.getKey(), 0, PAGE_SIZE, new ActionListListener() {

				@Override
				public void onList(ListResult<SocializeAction> result) {

					handleSocializeResult(result);
				}

				@Override
				public void onError(SocializeException error) {
					handleError(GetActionsByUserEntityActivity.this, error);
				}
			});
		}
		catch (SocializeException e) {
			e.printStackTrace();
		}
	}

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

	/* (non-Javadoc)
	 * @see com.socialize.demo.DemoActivity#getButtonText()
	 */
	@Override
	public String getButtonText() {
		return "List " + PAGE_SIZE + " Actions for Current User & Entity";
	}
}


/*
 * 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.log.SocializeLogger;
import com.socialize.notifications.NotificationType;
import com.socialize.util.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;

import java.text.ParseException;

/**
 * @author Jason Polites
 *
 */
public class SubscriptionFactory extends SocializeObjectFactory<Subscription> {

	private UserFactory userFactory;
	private EntityFactory entityFactory;
	private SocializeLogger logger;

	/* (non-Javadoc)
	 * @see com.socialize.entity.SocializeObjectFactory#postFromJSON(org.json.JSONObject, com.socialize.entity.SocializeObject)
	 */
	@Override
	protected void postFromJSON(JSONObject from, Subscription to) throws JSONException {

		try {

			if(from.has("user") && !from.isNull("user")) {
				JSONObject user = from.getJSONObject("user");
				if(user != null) {
					to.setUser(userFactory.fromJSON(user));
				}
			}

			if(from.has("entity") && !from.isNull("entity")) {
				JSONObject entity = from.getJSONObject("entity");
				if(entity != null) {
					to.setEntity(entityFactory.fromJSON(entity));
				}
			}

			if(from.has("subscribed") && !from.isNull("subscribed")) {
				to.setSubscribed(from.getBoolean("subscribed"));
			}

			if(from.has("date") && !from.isNull("date")) {
				try {
					to.setDate(DATE_FORMAT.parse(from.getString("date")).getTime());
				}
				catch (ParseException e) {
					if(logger != null && logger.isWarnEnabled()) {
						logger.warn("Could not parse date [" +
								from.getString("date") +
								"] using format [" +
								DATE_FORMAT_STRING +
								"]");
					}
				}
			}

			if(from.has("type") && !from.isNull("type")) {
				try {
					to.setNotificationType(NotificationType.valueOf(from.getString("type").toUpperCase()));
				}
				catch (Exception e) {
					if(logger != null && logger.isWarnEnabled()) {
						logger.warn("Could not parse type [" +
								from.getString("type") +
								"] into " + NotificationType.class.getName());
					}
				}
			}
		}
		catch (Throwable e) {
			throw new JSONException(e.getMessage());
		}
	}

	/* (non-Javadoc)
	 * @see com.socialize.entity.SocializeObjectFactory#postToJSON(com.socialize.entity.SocializeObject, org.json.JSONObject)
	 */
	@Override
	protected void postToJSON(Subscription from, JSONObject to) throws JSONException {

		try {
			Entity entityObject = from.getEntity();
			User userObject = from.getUser();
			Long date = from.getDate();

			if(entityObject != null) {
				if(!StringUtils.isEmpty(entityObject.getName()) && !StringUtils.isEmpty(entityObject.getKey())) {
					JSONObject entity = entityFactory.toJSON(entityObject);
					to.put("entity", entity);
				}
				else if(!StringUtils.isEmpty(entityObject.getKey())) {
					to.put("entity_key", entityObject.getKey());
				}
			}

			if(userObject != null) {
				JSONObject user = userFactory.toJSON(userObject);
				to.put("user", user);
			}

			if(date != null) {
				to.put("date", DATE_FORMAT.format(date));
			}

			to.put("subscribed", from.isSubscribed());
			to.put("type", from.getNotificationType().name().toLowerCase());
		}
		catch (Exception e) {
			throw new JSONException(e.getMessage());
		}
	}

	/* (non-Javadoc)
	 * @see com.socialize.entity.JSONFactory#instantiateObject(org.json.JSONObject)
	 */
	@Override
	public Object instantiateObject(JSONObject object) {
		return new Subscription();
	}

	public void setUserFactory(UserFactory userFactory) {
		this.userFactory = userFactory;
	}

	public void setEntityFactory(EntityFactory entityFactory) {
		this.entityFactory = entityFactory;
	}

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


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

import com.socialize.entity.DeviceRegistration;
import com.socialize.error.SocializeException;
import com.socialize.provider.SocializeProvider;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Jason Polites
 *
 */
public class SocializeDeviceRegistrationSystem extends SocializeApi<DeviceRegistration, SocializeProvider<DeviceRegistration>> implements DeviceRegistrationSystem {

	public SocializeDeviceRegistrationSystem(SocializeProvider<DeviceRegistration> provider) {
		super(provider);
	}

	/* (non-Javadoc)
	 * @see com.socialize.api.DeviceRegistrationSystem#registerDevice(com.socialize.api.SocializeSession, com.socialize.entity.DeviceRegistration)
	 */
	@Override
	public void registerDevice(SocializeSession session, DeviceRegistration registration, DeviceRegistrationListener listener) {
		List<DeviceRegistration> registrations = new ArrayList<DeviceRegistration>(1);
		registrations.add(registration);
		postAsync(session, ENDPOINT, registrations, listener);
	}

	@Override
	public void registerDeviceSynchronous(SocializeSession session, DeviceRegistration registration) throws SocializeException {
		List<DeviceRegistration> registrations = new ArrayList<DeviceRegistration>(1);
		registrations.add(registration);
		post(session, ENDPOINT, registrations);
	}
}


/*
 * 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 com.socialize.auth.*;
import com.socialize.error.SocializeException;
import com.socialize.listener.AuthProviderListener;

/**
 * @author Jason Polites
 *
 */
public class TwitterAuthProvider implements AuthProvider<TwitterAuthProviderInfo> {

	private TwitterAuthUtils twitterAuthUtils;
	private AuthProviderInfoBuilder authProviderInfoBuilder;

	/* (non-Javadoc)
	 * @see com.socialize.auth.AuthProvider#authenticate(com.socialize.auth.AuthProviderInfo, com.socialize.listener.AuthProviderListener)
	 */
	@Override
	public void authenticate(Context context, TwitterAuthProviderInfo info, final AuthProviderListener listener) {
		 twitterAuthUtils.showAuthDialog(context, info, newTwitterAuthListener(listener));
	}

	@Override
	public boolean validateForRead(TwitterAuthProviderInfo info, String...permissions) {
		if(authProviderInfoBuilder != null) {
			AuthProviderInfo expected = authProviderInfoBuilder.getFactory(AuthProviderType.TWITTER).getInstanceForRead();
			return info.matches(expected);
		}
		return true;
	}

	@Override
	public boolean validateForWrite(TwitterAuthProviderInfo info, String...permissions) {
		return validateForRead(info, permissions);
	}

	@Deprecated
	@Override
	public boolean validate(TwitterAuthProviderInfo info) {
		return validateForWrite(info);
	}

	protected TwitterAuthListener newTwitterAuthListener(final AuthProviderListener listener) {
		return new TwitterAuthListener() {

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

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

			@Override
			public void onAuthSuccess(String token, String secret, String screenName, String userId) {
				if(listener != null) {
					AuthProviderResponse response = newAuthProviderResponse();
					response.setToken(token);
					response.setSecret(secret);
					response.setUserId(userId);
					listener.onAuthSuccess(response);
				}
			}
		};
	}

	protected AuthProviderResponse newAuthProviderResponse() {
		return new AuthProviderResponse();
	}

	/* (non-Javadoc)
	 * @see com.socialize.auth.AuthProvider#clearCache(android.content.Context, com.socialize.auth.AuthProviderInfo)
	 */
	@Override
	public void clearCache(Context context, TwitterAuthProviderInfo info) {}

	public void setTwitterAuthUtils(TwitterAuthUtils twitterUtils) {
		this.twitterAuthUtils = twitterUtils;
	}

	public void setAuthProviderInfoBuilder(AuthProviderInfoBuilder authProviderInfoBuilder) {
		this.authProviderInfoBuilder = authProviderInfoBuilder;
	}
}