package com.airlocksoftware.hackernews.adapter; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import android.widget.ListView; import com.airlocksoftware.hackernews.R; import com.airlocksoftware.hackernews.activity.MainActivity; import com.airlocksoftware.hackernews.activity.MainActivity.CommentsTab; import com.airlocksoftware.hackernews.activity.UserActivity; import com.airlocksoftware.hackernews.data.UserPrefs; import com.airlocksoftware.hackernews.fragment.StoryFragment; import com.airlocksoftware.hackernews.interfaces.TabletLayout; import com.airlocksoftware.hackernews.model.Story; import com.airlocksoftware.hackernews.view.SharePopup; import com.airlocksoftware.holo.adapters.GroupAdapter; import com.airlocksoftware.holo.image.IconView; import com.airlocksoftware.holo.type.FontText; import com.airlocksoftware.holo.utils.AnimUtils; import com.airlocksoftware.holo.utils.Utils; import com.airlocksoftware.holo.utils.ViewUtils; import org.apache.commons.lang3.StringUtils; import java.io.Serializable; import java.util.List; /** Adapter for StoryFragment and SubmissionsFragment. **/ public class StoryAdapter extends GroupAdapter<Story> { private long mActiveStoryId; private SharePopup mShare; private Context mContext; private ListView mList; private StoryFragment.Callbacks mStoryCallbacks; private TabletLayout mTabletInterface; private LayoutInflater mInflater; // cached resource values private final int BRIGHT_ACCENT_COLOR; private final int TEXT_COLOR_PRIMARY; private final int TEXT_COLOR_SECONDARY; private final int DARK_ORANGE_COLOR; private final int GRAY_50_COLOR; private final String UPVOTE; private final String UPVOTED; private final Drawable UPVOTE_INDICATOR; private static final int STORY_LAYOUT = R.layout.vw_story; private static final String STORIES = StoryAdapter.class.getSimpleName() + ".stories"; private static final String ACTIVE_STORY = StoryAdapter.class.getSimpleName() + ".activeStory"; private static class ViewHolder { public Story story; public FontText title, numPoints, numComments, domain, pointsLabel, upvoteLabel; public View commentsButton, webButton, ctrlContainer, container, divider; public FrameLayout upvoteBtn, shareBtn, userBtn; public IconView upvoteIcon, commentIcon; /** Whether or not this View is in "active mode" (i.e. Orange background, white text.) **/ public boolean activeMode = false; /** Whether or not this view is in "upvoted mode".(i.e. disabled vote listener, orange icon, etc.) **/ public boolean upvotedMode = false; } public StoryAdapter(Context context, ListView list, SharePopup share, StoryFragment.Callbacks storyCallbacks, TabletLayout tabletLayout) { mContext = context; mList = list; mShare = share; mStoryCallbacks = storyCallbacks; mTabletInterface = tabletLayout; mInflater = LayoutInflater.from(mContext); // cache reusable resources Resources res = mContext.getResources(); TEXT_COLOR_PRIMARY = res.getColor(Utils.getThemedResourceId(mContext, R.attr.textColorPrimary)); TEXT_COLOR_SECONDARY = res.getColor(Utils.getThemedResourceId(mContext, R.attr.textColorSecondary)); BRIGHT_ACCENT_COLOR = res.getColor(R.color.bright_accent); DARK_ORANGE_COLOR = res.getColor(R.color.orange_5); GRAY_50_COLOR = res.getColor(R.color.gray_50); UPVOTE_INDICATOR = res.getDrawable(R.drawable.bg_story_upvote_indicator); UPVOTED = mContext.getString(R.string.upvoted); UPVOTE = mContext.getString(R.string.upvote); } @Override public View getView(Story story, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { // inflate the layout convertView = mInflater.inflate(STORY_LAYOUT, parent, false); // find views for ViewHolder holder = new ViewHolder(); findHolderViews(convertView, holder); // fix background repeats ViewUtils.fixBackgroundRepeat(holder.webButton); ViewUtils.fixBackgroundRepeat(holder.commentsButton); // attach click listeners attachClickListeners(holder); // set holder as tag on views that have click listeners attachTags(convertView, holder); } else { holder = (ViewHolder) convertView.getTag(); } bindView(story, holder); return convertView; } private void attachTags(View convertView, ViewHolder holder) { convertView.setTag(holder); holder.webButton.setTag(holder); holder.commentsButton.setTag(holder); holder.upvoteBtn.setTag(holder); holder.shareBtn.setTag(holder); holder.userBtn.setTag(holder); } private void attachClickListeners(ViewHolder holder) { holder.webButton.setOnClickListener(mClickListener); holder.commentsButton.setOnClickListener(mClickListener); holder.webButton.setOnLongClickListener(mLongListener); holder.commentsButton.setOnLongClickListener(mLongListener); holder.shareBtn.setOnClickListener(mShareListener); holder.upvoteBtn.setOnClickListener(mVoteListener); holder.userBtn.setOnClickListener(mUserListener); } private void findHolderViews(View convertView, ViewHolder holder) { holder.container = convertView; holder.title = (FontText) convertView.findViewById(R.id.txt_title); holder.numPoints = (FontText) convertView.findViewById(R.id.txt_num_pts); holder.numComments = (FontText) convertView.findViewById(R.id.txt_num_comments); holder.domain = (FontText) convertView.findViewById(R.id.txt_domain); holder.commentsButton = convertView.findViewById(R.id.btn_comments); holder.webButton = convertView.findViewById(R.id.btn_web); holder.ctrlContainer = convertView.findViewById(R.id.cnt_ctrl); holder.shareBtn = (FrameLayout) convertView.findViewById(R.id.btn_share); holder.userBtn = (FrameLayout) convertView.findViewById(R.id.btn_user); holder.upvoteBtn = (FrameLayout) convertView.findViewById(R.id.btn_upvote); holder.upvoteIcon = (IconView) convertView.findViewById(R.id.icv_upvote); holder.upvoteLabel = (FontText) convertView.findViewById(R.id.txt_upvote); holder.pointsLabel = (FontText) convertView.findViewById(R.id.txt_pts); holder.commentIcon = (IconView) convertView.findViewById(R.id.icv_comment); holder.divider = convertView.findViewById(R.id.divider); } public void setActiveStory(Story story) { if (story != null) { mActiveStoryId = story.storyId; notifyDataSetChanged(); } } private void bindView(Story story, ViewHolder holder) { holder.story = story; if (holder.ctrlContainer.getVisibility() != View.GONE) holder.ctrlContainer.setVisibility(View.GONE); bindTextViews(story, holder); bindIsJobsPost(story, holder, Story.isYCombinatorJobPost(story)); // holder.upvotedMode caches whether this view has been configured to show an upvoted story // if upvotedMode != story.isUpvoted, then we need reconfigure the view if (story.isUpvoted != holder.upvotedMode) { bindIsUpvoted(story, holder); } // if this is the active story & the view isn't already in activeMode, // change background & colors, hide divider, and disable click listeners if (mTabletInterface != null && mTabletInterface.isTabletLayout()) { boolean isActiveStory = story.storyId == mActiveStoryId; if (isActiveStory != holder.activeMode) { bindIsActiveStory(holder, isActiveStory); } } } private void bindIsActiveStory(ViewHolder holder, boolean isActiveStory) { int textColorSecondary = isActiveStory ? Color.WHITE : TEXT_COLOR_SECONDARY; holder.numPoints.setTextColor(textColorSecondary); holder.numComments.setTextColor(textColorSecondary); holder.domain.setTextColor(textColorSecondary); holder.pointsLabel.setTextColor(textColorSecondary); holder.container.setBackgroundColor(isActiveStory ? DARK_ORANGE_COLOR : Color.TRANSPARENT); holder.title.setTextColor(isActiveStory ? Color.WHITE : TEXT_COLOR_PRIMARY); holder.commentIcon.iconColor(isActiveStory ? Color.WHITE : GRAY_50_COLOR); holder.divider.setVisibility(ViewUtils.boolToVis(!isActiveStory)); holder.commentsButton.setOnClickListener(isActiveStory ? null : mClickListener); holder.commentsButton.setOnLongClickListener(isActiveStory ? null : mLongListener); holder.webButton.setOnClickListener(isActiveStory ? null : mClickListener); holder.webButton.setOnLongClickListener(isActiveStory ? null : mLongListener); holder.commentsButton.setClickable(!isActiveStory); holder.commentsButton.setLongClickable(!isActiveStory); holder.webButton.setClickable(!isActiveStory); holder.webButton.setLongClickable(!isActiveStory); holder.activeMode = isActiveStory; } @SuppressWarnings("deprecation") private void bindIsUpvoted(Story story, ViewHolder holder) { holder.upvoteIcon.iconColor(story.isUpvoted ? BRIGHT_ACCENT_COLOR : TEXT_COLOR_PRIMARY); holder.upvoteLabel.setTextColor(story.isUpvoted ? BRIGHT_ACCENT_COLOR : TEXT_COLOR_PRIMARY); holder.upvoteLabel.setText(story.isUpvoted ? UPVOTED : UPVOTE); holder.upvoteBtn.setClickable(!story.isUpvoted); holder.upvoteBtn.setOnClickListener(story.isUpvoted ? null : mVoteListener); holder.numPoints.setBackgroundDrawable(story.isUpvoted ? UPVOTE_INDICATOR : null); holder.upvotedMode = story.isUpvoted; } /** check for YCombinator job post **/ private void bindIsJobsPost(Story story, ViewHolder holder, boolean isJobPost) { // hide / show comments, points, & webButton holder.webButton.setVisibility(ViewUtils.boolToVis(StringUtils.isNotBlank(story.url) && !isJobPost)); holder.commentIcon.setVisibility(ViewUtils.boolToVis(!isJobPost)); holder.numComments.setVisibility(ViewUtils.boolToVis(!isJobPost)); holder.numPoints.setVisibility(ViewUtils.boolToVis(!isJobPost)); holder.pointsLabel.setVisibility(ViewUtils.boolToVis(!isJobPost)); } private void bindTextViews(Story story, ViewHolder holder) { holder.title.setText(story.title); holder.numPoints.setText(Integer.toString(story.numPoints)); holder.numComments.setText(Integer.toString(story.numComments)); holder.domain.setText((story.domain != null) ? story.domain : ""); } // LISTENERS private View.OnClickListener mUserListener = new View.OnClickListener() { @Override public void onClick(View v) { ViewHolder holder = (ViewHolder) v.getTag(); UserActivity.startUserActivity(mContext, holder.story.username); } }; private View.OnClickListener mShareListener = new View.OnClickListener() { @Override public void onClick(View v) { ViewHolder holder = (ViewHolder) v.getTag(); mShare.shareStory(holder.story); } }; private View.OnClickListener mVoteListener = new View.OnClickListener() { @Override public void onClick(View v) { ViewHolder holder = (ViewHolder) v.getTag(); Story story = holder.story; story.upvote(mContext); hideControls(holder); notifyDataSetChanged(); } }; public View.OnClickListener mClickListener = new View.OnClickListener() { @Override public void onClick(View v) { ViewHolder holder = (ViewHolder) v.getTag(); CommentsTab tab = v.getId() == R.id.btn_comments ? CommentsTab.COMMENTS : CommentsTab.ARTICLE; boolean openInBrowser = new UserPrefs(mContext).getOpenInBrowser(); // check for open in browser if (openInBrowser && tab == CommentsTab.ARTICLE) { mContext.startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse(holder.story.url))); } // if this is in a MainActivity, let mStoryCallbacks handle it if (mStoryCallbacks != null) { setActiveStory(holder.story); mStoryCallbacks.showCommentsForStory(holder.story, tab); } else { // this is from SubmissionsFragment, and should start a new CommentsActivity MainActivity.startCommentsActivity(mContext, null, holder.story, tab); } } }; private View.OnLongClickListener mLongListener = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { ViewHolder holder = (ViewHolder) v.getTag(); if (holder.ctrlContainer.getVisibility() == View.GONE) { // scroll to make controls visible int height = v.getHeight(); int ctrlContainerHeight = mContext.getResources() .getDimensionPixelSize(R.dimen.slideout_ctrl_height); int scrollY = height + Utils.getWindowLocation(mContext, v).y + ctrlContainerHeight - Utils.getScreenSize(mContext).y; if (scrollY > 0) mList.smoothScrollBy(scrollY, 200); showControls(holder); } else { hideControls(holder); } return true; } }; private void showControls(ViewHolder holder) { // animate the showing of controls Animation inAnim = AnimationUtils.loadAnimation(mContext, R.anim.ctrl_scale_down); AnimUtils.startInAnimation(holder.ctrlContainer, inAnim); // hide divider holder.divider.setVisibility(View.GONE); } private void hideControls(ViewHolder holder) { // animate the hiding of controls Animation outAnim = AnimationUtils.loadAnimation(mContext, R.anim.ctrl_scale_up); AnimUtils.startOutAnimation(holder.ctrlContainer, outAnim); // show divider holder.divider.setVisibility(View.VISIBLE); } public Bundle onSaveInstanceState(Bundle bundle) { bundle.putLong(ACTIVE_STORY, mActiveStoryId); bundle.putSerializable(STORIES, (Serializable) this.getArray()); return bundle; } @SuppressWarnings("unchecked") public void onRestoreInstanceState(Bundle savedInstanceState) { mActiveStoryId = savedInstanceState.getLong(ACTIVE_STORY); List<Story> stories = (List<Story>) savedInstanceState.getSerializable(STORIES); clear(); if (stories != null) addAll(stories); } }