package org.wordpress.android.ui.posts.adapters; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.content.Context; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.support.annotation.NonNull; import android.support.v7.widget.ListPopupWindow; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.AdapterView; import android.widget.TextView; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.wordpress.android.R; import org.wordpress.android.WordPress; import org.wordpress.android.fluxc.Dispatcher; import org.wordpress.android.fluxc.generated.MediaActionBuilder; import org.wordpress.android.fluxc.model.MediaModel; import org.wordpress.android.fluxc.model.PostModel; import org.wordpress.android.fluxc.model.SiteModel; import org.wordpress.android.fluxc.model.post.PostStatus; import org.wordpress.android.fluxc.store.MediaStore; import org.wordpress.android.fluxc.store.MediaStore.MediaPayload; import org.wordpress.android.fluxc.store.PostStore; import org.wordpress.android.ui.posts.PostUtils; import org.wordpress.android.ui.posts.PostsListFragment; import org.wordpress.android.ui.posts.services.PostUploadService; import org.wordpress.android.ui.reader.utils.ReaderImageScanner; import org.wordpress.android.ui.reader.utils.ReaderUtils; import org.wordpress.android.util.AppLog; import org.wordpress.android.util.DateTimeUtils; import org.wordpress.android.util.DisplayUtils; import org.wordpress.android.util.SiteUtils; import org.wordpress.android.widgets.PostListButton; import org.wordpress.android.widgets.WPNetworkImageView; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; /** * Adapter for Posts/Pages list */ public class PostsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final long ROW_ANIM_DURATION = 150; private static final int VIEW_TYPE_POST_OR_PAGE = 0; private static final int VIEW_TYPE_ENDLIST_INDICATOR = 1; public interface OnPostButtonClickListener { void onPostButtonClicked(int buttonId, PostModel post); } public enum LoadMode { IF_CHANGED, FORCED } private OnLoadMoreListener mOnLoadMoreListener; private OnPostsLoadedListener mOnPostsLoadedListener; private OnPostSelectedListener mOnPostSelectedListener; private OnPostButtonClickListener mOnPostButtonClickListener; private final SiteModel mSite; private final int mPhotonWidth; private final int mPhotonHeight; private final int mEndlistIndicatorHeight; private final boolean mIsPage; private final boolean mIsStatsSupported; private final boolean mAlwaysShowAllButtons; private boolean mIsLoadingPosts; private final List<PostModel> mPosts = new ArrayList<>(); private final List<PostModel> mHiddenPosts = new ArrayList<>(); private final Map<Integer, String> mFeaturedImageUrls = new HashMap<>(); private final LayoutInflater mLayoutInflater; @Inject Dispatcher mDispatcher; @Inject protected PostStore mPostStore; @Inject protected MediaStore mMediaStore; public PostsListAdapter(Context context, @NonNull SiteModel site, boolean isPage) { ((WordPress) context.getApplicationContext()).component().inject(this); mIsPage = isPage; mLayoutInflater = LayoutInflater.from(context); mSite = site; mIsStatsSupported = SiteUtils.isAccessedViaWPComRest(site) && site.getHasCapabilityViewStats(); int displayWidth = DisplayUtils.getDisplayPixelWidth(context); int contentSpacing = context.getResources().getDimensionPixelSize(R.dimen.content_margin); mPhotonWidth = displayWidth - (contentSpacing * 2); mPhotonHeight = context.getResources().getDimensionPixelSize(R.dimen.reader_featured_image_height); // endlist indicator height is hard-coded here so that its horz line is in the middle of the fab mEndlistIndicatorHeight = DisplayUtils.dpToPx(context, mIsPage ? 82 : 74); // on larger displays we can always show all buttons mAlwaysShowAllButtons = (displayWidth >= 1080); } public void setOnLoadMoreListener(OnLoadMoreListener listener) { mOnLoadMoreListener = listener; } public void setOnPostsLoadedListener(OnPostsLoadedListener listener) { mOnPostsLoadedListener = listener; } public void setOnPostSelectedListener(OnPostSelectedListener listener) { mOnPostSelectedListener = listener; } public void setOnPostButtonClickListener(OnPostButtonClickListener listener) { mOnPostButtonClickListener = listener; } private PostModel getItem(int position) { if (isValidPostPosition(position)) { return mPosts.get(position); } return null; } private boolean isValidPostPosition(int position) { return (position >= 0 && position < mPosts.size()); } @Override public int getItemViewType(int position) { if (position == mPosts.size()) { return VIEW_TYPE_ENDLIST_INDICATOR; } return VIEW_TYPE_POST_OR_PAGE; } @Override public int getItemCount() { if (mPosts.size() == 0) { return 0; } else { return mPosts.size() + 1; // +1 for the endlist indicator } } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_ENDLIST_INDICATOR) { View view = mLayoutInflater.inflate(R.layout.endlist_indicator, parent, false); view.getLayoutParams().height = mEndlistIndicatorHeight; return new EndListViewHolder(view); } else if (mIsPage) { View view = mLayoutInflater.inflate(R.layout.page_item, parent, false); return new PageViewHolder(view); } else { View view = mLayoutInflater.inflate(R.layout.post_cardview, parent, false); return new PostViewHolder(view); } } private boolean canShowStatsForPost(PostModel post) { return mIsStatsSupported && PostStatus.fromPost(post) == PostStatus.PUBLISHED && !post.isLocalDraft() && !post.isLocallyChanged(); } private boolean canPublishPost(PostModel post) { return post != null && !PostUploadService.isPostUploading(post) && (post.isLocallyChanged() || post.isLocalDraft() || PostStatus.fromPost(post) == PostStatus.DRAFT); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { // nothing to do if this is the static endlist indicator if (getItemViewType(position) == VIEW_TYPE_ENDLIST_INDICATOR) { return; } final PostModel post = mPosts.get(position); Context context = holder.itemView.getContext(); if (holder instanceof PostViewHolder) { PostViewHolder postHolder = (PostViewHolder) holder; if (StringUtils.isNotEmpty(post.getTitle())) { // Unescape HTML String cleanPostTitle = StringEscapeUtils.unescapeHtml4(post.getTitle()); postHolder.txtTitle.setText(cleanPostTitle); } else { postHolder.txtTitle.setText("(" + context.getResources().getText(R.string.untitled) + ")"); } String cleanPostExcerpt = PostUtils.getPostListExcerptFromPost(post); if (StringUtils.isNotEmpty(cleanPostExcerpt)) { postHolder.txtExcerpt.setVisibility(View.VISIBLE); // Unescape HTML cleanPostExcerpt = StringEscapeUtils.unescapeHtml4(cleanPostExcerpt); // Collapse shortcodes: [gallery ids="1206,1205,1191"] -> [gallery] cleanPostExcerpt = PostUtils.collapseShortcodes(cleanPostExcerpt); postHolder.txtExcerpt.setText(cleanPostExcerpt); } else { postHolder.txtExcerpt.setVisibility(View.GONE); } if (post.getFeaturedImageId() > 0 || mFeaturedImageUrls.containsKey(post.getId())) { postHolder.imgFeatured.setVisibility(View.VISIBLE); postHolder.imgFeatured.setImageUrl(mFeaturedImageUrls.get(post.getId()), WPNetworkImageView.ImageType.PHOTO); } else { postHolder.imgFeatured.setVisibility(View.GONE); } // local drafts say "delete" instead of "trash" if (post.isLocalDraft()) { postHolder.txtDate.setVisibility(View.GONE); postHolder.btnTrash.setButtonType(PostListButton.BUTTON_DELETE); } else { postHolder.txtDate.setText(PostUtils.getFormattedDate(post)); postHolder.txtDate.setVisibility(View.VISIBLE); postHolder.btnTrash.setButtonType(PostListButton.BUTTON_TRASH); } if (PostUploadService.isPostUploading(post)) { postHolder.disabledOverlay.setVisibility(View.VISIBLE); } else { postHolder.disabledOverlay.setVisibility(View.GONE); } updateStatusText(postHolder.txtStatus, post); configurePostButtons(postHolder, post); } else if (holder instanceof PageViewHolder) { PageViewHolder pageHolder = (PageViewHolder) holder; if (StringUtils.isNotEmpty(post.getTitle())) { pageHolder.txtTitle.setText(post.getTitle()); } else { pageHolder.txtTitle.setText("(" + context.getResources().getText(R.string.untitled) + ")"); } String dateStr = getPageDateHeaderText(context, post); pageHolder.txtDate.setText(dateStr); updateStatusText(pageHolder.txtStatus, post); // don't show date header if same as previous boolean showDate; if (position > 0) { String prevDateStr = getPageDateHeaderText(context, mPosts.get(position - 1)); showDate = !prevDateStr.equals(dateStr); } else { showDate = true; } pageHolder.dateHeader.setVisibility(showDate ? View.VISIBLE : View.GONE); // no "..." more button when uploading pageHolder.btnMore.setVisibility(PostUploadService.isPostUploading(post) ? View.GONE : View.VISIBLE); pageHolder.btnMore.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showPagePopupMenu(v, post); } }); // only show the top divider for the first item pageHolder.dividerTop.setVisibility(position == 0 ? View.VISIBLE : View.GONE); if (PostUploadService.isPostUploading(post)) { pageHolder.disabledOverlay.setVisibility(View.VISIBLE); } else { pageHolder.disabledOverlay.setVisibility(View.GONE); } } // load more posts when we near the end if (mOnLoadMoreListener != null && position >= mPosts.size() - 1 && position >= PostsListFragment.POSTS_REQUEST_COUNT - 1) { mOnLoadMoreListener.onLoadMore(); } holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mOnPostSelectedListener != null) { mOnPostSelectedListener.onPostSelected(post); } } }); } /* * returns the caption to show in the date header for the passed page - pages with the same * caption will be grouped together * - if page is local draft, returns "Local draft" * - if page is scheduled, returns formatted date w/o time * - if created today or yesterday, returns "Today" or "Yesterday" * - if created this month, returns the number of days ago * - if created this year, returns the month name * - if created before this year, returns the month name with year */ private static String getPageDateHeaderText(Context context, PostModel page) { if (page.isLocalDraft()) { return context.getString(R.string.local_draft); } else if (PostStatus.fromPost(page) == PostStatus.SCHEDULED) { return DateUtils.formatDateTime(context, DateTimeUtils.timestampFromIso8601Millis(page.getDateCreated()), DateUtils.FORMAT_ABBREV_ALL); } else { Date dtCreated = DateTimeUtils.dateUTCFromIso8601(page.getDateCreated()); Date dtNow = DateTimeUtils.nowUTC(); int daysBetween = DateTimeUtils.daysBetween(dtCreated, dtNow); if (daysBetween == 0) { return context.getString(R.string.today); } else if (daysBetween == 1) { return context.getString(R.string.yesterday); } else if (DateTimeUtils.isSameMonthAndYear(dtCreated, dtNow)) { return String.format(context.getString(R.string.days_ago), daysBetween); } else if (DateTimeUtils.isSameYear(dtCreated, dtNow)) { return new SimpleDateFormat("MMMM").format(dtCreated); } else { return new SimpleDateFormat("MMMM yyyy").format(dtCreated); } } } /* * user tapped "..." next to a page, show a popup menu of choices */ private void showPagePopupMenu(View view, final PostModel page) { Context context = view.getContext(); final ListPopupWindow listPopup = new ListPopupWindow(context); listPopup.setAnchorView(view); listPopup.setWidth(context.getResources().getDimensionPixelSize(R.dimen.menu_item_width)); listPopup.setModal(true); listPopup.setAdapter(new PageMenuAdapter(context, page)); listPopup.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { listPopup.dismiss(); if (mOnPostButtonClickListener != null) { int buttonId = (int) id; mOnPostButtonClickListener.onPostButtonClicked(buttonId, page); } } }); listPopup.show(); } private void updateStatusText(TextView txtStatus, PostModel post) { if ((PostStatus.fromPost(post) == PostStatus.PUBLISHED) && !post.isLocalDraft() && !post.isLocallyChanged()) { txtStatus.setVisibility(View.GONE); } else { int statusTextResId = 0; int statusIconResId = 0; int statusColorResId = R.color.grey_darken_10; if (PostUploadService.isPostUploading(post)) { statusTextResId = R.string.post_uploading; statusColorResId = R.color.alert_yellow; } else if (post.isLocalDraft()) { statusTextResId = R.string.local_draft; statusIconResId = R.drawable.noticon_scheduled_alert_yellow_16dp; statusColorResId = R.color.alert_yellow; } else if (post.isLocallyChanged()) { statusTextResId = R.string.local_changes; statusIconResId = R.drawable.noticon_scheduled_alert_yellow_16dp; statusColorResId = R.color.alert_yellow; } else { switch (PostStatus.fromPost(post)) { case DRAFT: statusTextResId = R.string.draft; statusIconResId = R.drawable.noticon_scheduled_alert_yellow_16dp; statusColorResId = R.color.alert_yellow; break; case PRIVATE: statusTextResId = R.string.post_private; break; case PENDING: statusTextResId = R.string.pending_review; statusIconResId = R.drawable.noticon_scheduled_alert_yellow_16dp; statusColorResId = R.color.alert_yellow; break; case SCHEDULED: statusTextResId = R.string.scheduled; statusIconResId = R.drawable.noticon_scheduled_alert_yellow_16dp; statusColorResId = R.color.alert_yellow; break; case TRASHED: statusTextResId = R.string.trashed; statusIconResId = R.drawable.ic_pages_alert_red_16dp; statusColorResId = R.color.alert_red; break; } } Resources resources = txtStatus.getContext().getResources(); txtStatus.setTextColor(resources.getColor(statusColorResId)); txtStatus.setText(statusTextResId != 0 ? resources.getString(statusTextResId) : ""); Drawable drawable = (statusIconResId != 0 ? resources.getDrawable(statusIconResId) : null); txtStatus.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); txtStatus.setVisibility(View.VISIBLE); } } private void configurePostButtons(final PostViewHolder holder, final PostModel post) { // posts with local changes have preview rather than view button if (post.isLocalDraft() || post.isLocallyChanged()) { holder.btnView.setButtonType(PostListButton.BUTTON_PREVIEW); } else { holder.btnView.setButtonType(PostListButton.BUTTON_VIEW); } boolean canShowStatsButton = canShowStatsForPost(post); boolean canShowPublishButton = canPublishPost(post); int numVisibleButtons = 3; if (canShowPublishButton) numVisibleButtons++; if (canShowStatsButton) numVisibleButtons++; // edit / view are always visible holder.btnEdit.setVisibility(View.VISIBLE); holder.btnView.setVisibility(View.VISIBLE); // if we have enough room to show all buttons, hide the back/more buttons and show stats/trash/publish if (mAlwaysShowAllButtons || numVisibleButtons <= 3) { holder.btnMore.setVisibility(View.GONE); holder.btnBack.setVisibility(View.GONE); holder.btnTrash.setVisibility(View.VISIBLE); holder.btnStats.setVisibility(canShowStatsButton ? View.VISIBLE : View.GONE); holder.btnPublish.setVisibility(canShowPublishButton ? View.VISIBLE : View.GONE); if (!mSite.getHasCapabilityPublishPosts()) { // Users with roles that lack permission to publish show Submit holder.btnPublish.setButtonType(PostListButton.BUTTON_SUBMIT); } } else { holder.btnMore.setVisibility(View.VISIBLE); holder.btnBack.setVisibility(View.GONE); holder.btnTrash.setVisibility(View.GONE); holder.btnStats.setVisibility(View.GONE); holder.btnPublish.setVisibility(View.GONE); } View.OnClickListener btnClickListener = new View.OnClickListener() { @Override public void onClick(View view) { // handle back/more here, pass other actions to activity/fragment int buttonType = ((PostListButton) view).getButtonType(); switch (buttonType) { case PostListButton.BUTTON_MORE: animateButtonRows(holder, post, false); break; case PostListButton.BUTTON_BACK: animateButtonRows(holder, post, true); break; default: if (mOnPostButtonClickListener != null) { mOnPostButtonClickListener.onPostButtonClicked(buttonType, post); } break; } } }; holder.btnEdit.setOnClickListener(btnClickListener); holder.btnView.setOnClickListener(btnClickListener); holder.btnStats.setOnClickListener(btnClickListener); holder.btnTrash.setOnClickListener(btnClickListener); holder.btnMore.setOnClickListener(btnClickListener); holder.btnBack.setOnClickListener(btnClickListener); holder.btnPublish.setOnClickListener(btnClickListener); } /* * buttons may appear in two rows depending on display size and number of visible * buttons - these rows are toggled through the "more" and "back" buttons - this * routine is used to animate the new row in and the old row out */ private void animateButtonRows(final PostViewHolder holder, final PostModel post, final boolean showRow1) { // first animate out the button row, then show/hide the appropriate buttons, // then animate the row layout back in PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1f, 0f); PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1f, 0f); ObjectAnimator animOut = ObjectAnimator.ofPropertyValuesHolder(holder.layoutButtons, scaleX, scaleY); animOut.setDuration(ROW_ANIM_DURATION); animOut.setInterpolator(new AccelerateInterpolator()); animOut.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // row 1 holder.btnEdit.setVisibility(showRow1 ? View.VISIBLE : View.GONE); holder.btnView.setVisibility(showRow1 ? View.VISIBLE : View.GONE); holder.btnMore.setVisibility(showRow1 ? View.VISIBLE : View.GONE); // row 2 holder.btnStats.setVisibility(!showRow1 && canShowStatsForPost(post) ? View.VISIBLE : View.GONE); holder.btnPublish.setVisibility(!showRow1 && canPublishPost(post) ? View.VISIBLE : View.GONE); holder.btnTrash.setVisibility(!showRow1 ? View.VISIBLE : View.GONE); holder.btnBack.setVisibility(!showRow1 ? View.VISIBLE : View.GONE); PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 0f, 1f); PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0f, 1f); ObjectAnimator animIn = ObjectAnimator.ofPropertyValuesHolder(holder.layoutButtons, scaleX, scaleY); animIn.setDuration(ROW_ANIM_DURATION); animIn.setInterpolator(new DecelerateInterpolator()); animIn.start(); } }); animOut.start(); } public void loadPosts(LoadMode mode) { if (mIsLoadingPosts) { AppLog.d(AppLog.T.POSTS, "post adapter > already loading posts"); } else { new LoadPostsTask(mode).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } /* * hides the post - used when the post is trashed by the user but the network request * to delete the post hasn't completed yet */ public void hidePost(PostModel post) { mHiddenPosts.add(post); int position = PostUtils.indexOfPostInList(post, mPosts); if (position > -1) { mPosts.remove(position); if (mPosts.size() > 0) { notifyItemRemoved(position); //when page is removed update the next one in case we need to show a header if (mIsPage) { notifyItemChanged(position); } } else { // we must call notifyDataSetChanged when the only post has been deleted - if we // call notifyItemRemoved the recycler will throw an IndexOutOfBoundsException // because removing the last post also removes the end list indicator notifyDataSetChanged(); } } } public void unhidePost(PostModel post) { if (mHiddenPosts.remove(post)) { loadPosts(LoadMode.IF_CHANGED); } } public interface OnLoadMoreListener { void onLoadMore(); } public interface OnPostSelectedListener { void onPostSelected(PostModel post); } public interface OnPostsLoadedListener { void onPostsLoaded(int postCount); } private class PostViewHolder extends RecyclerView.ViewHolder { private final TextView txtTitle; private final TextView txtExcerpt; private final TextView txtDate; private final TextView txtStatus; private final PostListButton btnEdit; private final PostListButton btnView; private final PostListButton btnPublish; private final PostListButton btnMore; private final PostListButton btnStats; private final PostListButton btnTrash; private final PostListButton btnBack; private final WPNetworkImageView imgFeatured; private final ViewGroup layoutButtons; private final View disabledOverlay; PostViewHolder(View view) { super(view); txtTitle = (TextView) view.findViewById(R.id.text_title); txtExcerpt = (TextView) view.findViewById(R.id.text_excerpt); txtDate = (TextView) view.findViewById(R.id.text_date); txtStatus = (TextView) view.findViewById(R.id.text_status); btnEdit = (PostListButton) view.findViewById(R.id.btn_edit); btnView = (PostListButton) view.findViewById(R.id.btn_view); btnPublish = (PostListButton) view.findViewById(R.id.btn_publish); btnMore = (PostListButton) view.findViewById(R.id.btn_more); btnStats = (PostListButton) view.findViewById(R.id.btn_stats); btnTrash = (PostListButton) view.findViewById(R.id.btn_trash); btnBack = (PostListButton) view.findViewById(R.id.btn_back); imgFeatured = (WPNetworkImageView) view.findViewById(R.id.image_featured); layoutButtons = (ViewGroup) view.findViewById(R.id.layout_buttons); disabledOverlay = view.findViewById(R.id.disabled_overlay); } } private class PageViewHolder extends RecyclerView.ViewHolder { private final TextView txtTitle; private final TextView txtDate; private final TextView txtStatus; private final ViewGroup dateHeader; private final View btnMore; private final View dividerTop; private final View disabledOverlay; PageViewHolder(View view) { super(view); txtTitle = (TextView) view.findViewById(R.id.text_title); txtStatus = (TextView) view.findViewById(R.id.text_status); btnMore = view.findViewById(R.id.btn_more); dividerTop = view.findViewById(R.id.divider_top); dateHeader = (ViewGroup) view.findViewById(R.id.header_date); txtDate = (TextView) dateHeader.findViewById(R.id.text_date); disabledOverlay = view.findViewById(R.id.disabled_overlay); } } private class EndListViewHolder extends RecyclerView.ViewHolder { EndListViewHolder(View view) { super(view); } } /* * called after the media (featured image) for a post has been downloaded - locate the post * and set its featured image url to the passed url */ public void mediaChanged(MediaModel mediaModel) { // Multiple posts could have the same featured image List<Integer> indexList = PostUtils.indexesOfFeaturedMediaIdInList(mediaModel.getMediaId(), mPosts); for (int position : indexList) { PostModel post = getItem(position); if (post != null) { mFeaturedImageUrls.put(post.getId(), mediaModel.getUrl()); notifyItemChanged(position); } } } private class LoadPostsTask extends AsyncTask<Void, Void, Boolean> { private List<PostModel> tmpPosts; private final ArrayList<Long> mediaIdsToUpdate = new ArrayList<>(); private LoadMode mLoadMode; LoadPostsTask(LoadMode loadMode) { mLoadMode = loadMode; } @Override protected void onPreExecute() { super.onPreExecute(); mIsLoadingPosts = true; } @Override protected void onCancelled() { super.onCancelled(); mIsLoadingPosts = false; } @Override protected Boolean doInBackground(Void... nada) { if (mIsPage) { tmpPosts = mPostStore.getPagesForSite(mSite); } else { tmpPosts = mPostStore.getPostsForSite(mSite); } // Make sure we don't return any hidden posts for (PostModel hiddenPost : mHiddenPosts) { tmpPosts.remove(hiddenPost); } // Go no further if existing post list is the same if (mLoadMode == LoadMode.IF_CHANGED && PostUtils.postListsAreEqual(mPosts, tmpPosts)) { // Always update the list if there are uploading posts boolean postsAreUploading = false; for (PostModel post : tmpPosts) { if (PostUploadService.isPostUploading(post)) { postsAreUploading = true; break; } } if (!postsAreUploading) { return false; } } // Generate the featured image url for each post String imageUrl = null; for (PostModel post : tmpPosts) { if (post.isLocalDraft()) { imageUrl = null; } else if (post.getFeaturedImageId() != 0) { MediaModel media = mMediaStore.getSiteMediaWithId(mSite, post.getFeaturedImageId()); if (media != null) { imageUrl = media.getUrl(); } else { // Reset the current `imageUrl` so it doesn't contain the previous post's image imageUrl = null; } // If the imageUrl isn't found it means the featured image info hasn't been added to // the local media library yet, so add to the list of media IDs to request info for if (TextUtils.isEmpty(imageUrl)) { mediaIdsToUpdate.add(post.getFeaturedImageId()); } } else if (StringUtils.isNotEmpty(post.getContent())) { ReaderImageScanner scanner = new ReaderImageScanner(post.getContent(), mSite.isPrivate()); imageUrl = scanner.getLargestImage(); } else { imageUrl = null; } if (!TextUtils.isEmpty(imageUrl)) { mFeaturedImageUrls.put(post.getId(), ReaderUtils.getResizedImageUrl(imageUrl, mPhotonWidth, mPhotonHeight, mSite.isPrivate())); } } return true; } @Override protected void onPostExecute(Boolean result) { if (result) { mPosts.clear(); mPosts.addAll(tmpPosts); notifyDataSetChanged(); if (mediaIdsToUpdate.size() > 0) { for (Long mediaId : mediaIdsToUpdate) { MediaModel mediaToDownload = new MediaModel(); mediaToDownload.setMediaId(mediaId); mediaToDownload.setLocalSiteId(mSite.getId()); MediaPayload payload = new MediaPayload(mSite, mediaToDownload); mDispatcher.dispatch(MediaActionBuilder.newFetchMediaAction(payload)); } } } mIsLoadingPosts = false; if (mOnPostsLoadedListener != null) { mOnPostsLoadedListener.onPostsLoaded(mPosts.size()); } } } }