/*******************************************************************************
* This file is part of RedReader.
*
* RedReader is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RedReader is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RedReader. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package org.quantumbadger.redreader.fragments;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.apache.commons.lang3.StringEscapeUtils;
import org.quantumbadger.redreader.R;
import org.quantumbadger.redreader.account.RedditAccount;
import org.quantumbadger.redreader.account.RedditAccountManager;
import org.quantumbadger.redreader.activities.BugReportActivity;
import org.quantumbadger.redreader.activities.OptionsMenuUtility;
import org.quantumbadger.redreader.activities.SessionChangeListener;
import org.quantumbadger.redreader.adapters.PostListingManager;
import org.quantumbadger.redreader.cache.CacheManager;
import org.quantumbadger.redreader.cache.CacheRequest;
import org.quantumbadger.redreader.cache.downloadstrategy.DownloadStrategy;
import org.quantumbadger.redreader.cache.downloadstrategy.DownloadStrategyAlways;
import org.quantumbadger.redreader.cache.downloadstrategy.DownloadStrategyIfNotCached;
import org.quantumbadger.redreader.cache.downloadstrategy.DownloadStrategyIfTimestampOutsideBounds;
import org.quantumbadger.redreader.cache.downloadstrategy.DownloadStrategyNever;
import org.quantumbadger.redreader.common.AndroidApi;
import org.quantumbadger.redreader.common.Constants;
import org.quantumbadger.redreader.common.General;
import org.quantumbadger.redreader.common.LinkHandler;
import org.quantumbadger.redreader.common.PrefsUtility;
import org.quantumbadger.redreader.common.RRError;
import org.quantumbadger.redreader.common.RRTime;
import org.quantumbadger.redreader.common.TimestampBound;
import org.quantumbadger.redreader.image.GetImageInfoListener;
import org.quantumbadger.redreader.image.ImageInfo;
import org.quantumbadger.redreader.io.RequestResponseHandler;
import org.quantumbadger.redreader.jsonwrap.JsonBufferedArray;
import org.quantumbadger.redreader.jsonwrap.JsonBufferedObject;
import org.quantumbadger.redreader.jsonwrap.JsonValue;
import org.quantumbadger.redreader.listingcontrollers.CommentListingController;
import org.quantumbadger.redreader.reddit.PostSort;
import org.quantumbadger.redreader.reddit.RedditPostListItem;
import org.quantumbadger.redreader.reddit.RedditSubredditManager;
import org.quantumbadger.redreader.reddit.api.RedditSubredditSubscriptionManager;
import org.quantumbadger.redreader.reddit.api.SubredditRequestFailure;
import org.quantumbadger.redreader.reddit.prepared.RedditParsedPost;
import org.quantumbadger.redreader.reddit.prepared.RedditPreparedPost;
import org.quantumbadger.redreader.reddit.things.RedditPost;
import org.quantumbadger.redreader.reddit.things.RedditSubreddit;
import org.quantumbadger.redreader.reddit.things.RedditThing;
import org.quantumbadger.redreader.reddit.url.PostCommentListingURL;
import org.quantumbadger.redreader.reddit.url.PostListingURL;
import org.quantumbadger.redreader.reddit.url.RedditURLParser;
import org.quantumbadger.redreader.reddit.url.SubredditPostListURL;
import org.quantumbadger.redreader.views.PostListingHeader;
import org.quantumbadger.redreader.views.RedditPostView;
import org.quantumbadger.redreader.views.ScrollbarRecyclerViewManager;
import org.quantumbadger.redreader.views.liststatus.ErrorView;
import java.net.URI;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
public class PostListingFragment extends RRFragment
implements RedditPostView.PostSelectionListener {
private static final String TAG = "PostListingFragment";
private static final String SAVEDSTATE_FIRST_VISIBLE_POS = "firstVisiblePosition";
private final PostListingURL mPostListingURL;
private RedditSubreddit mSubreddit;
private UUID mSession;
private final int mPostCountLimit;
private TextView mLoadMoreView;
private final SharedPreferences mSharedPreferences;
private final PostListingManager mPostListingManager;
private final RecyclerView mRecyclerView;
private final View mOuter;
private String mAfter = null, mLastAfter = null;
private CacheRequest mRequest;
private boolean mReadyToDownloadMore = false;
private long mTimestamp;
private int mPostCount = 0;
private final AtomicInteger mPostRefreshCount = new AtomicInteger(0);
private final HashSet<String> mPostIds = new HashSet<>(200);
private Integer mPreviousFirstVisibleItemPosition;
// Session may be null
public PostListingFragment(
final AppCompatActivity parent,
final Bundle savedInstanceState,
final Uri url,
final UUID session,
final boolean forceDownload) {
super(parent, savedInstanceState);
mPostListingManager = new PostListingManager(parent);
if(savedInstanceState != null) {
mPreviousFirstVisibleItemPosition = savedInstanceState.getInt(SAVEDSTATE_FIRST_VISIBLE_POS);
}
try {
mPostListingURL = (PostListingURL) RedditURLParser.parseProbablePostListing(url);
} catch(ClassCastException e) {
Toast.makeText(getActivity(), "Invalid post listing URL.", Toast.LENGTH_LONG).show();
// TODO proper error handling -- show error view
throw new RuntimeException("Invalid post listing URL");
}
mSession = session;
final Context context = getContext();
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
// TODO output failed URL
if(mPostListingURL == null) {
mPostListingManager.addFooterError(
new ErrorView(getActivity(), new RRError("Invalid post listing URL", "Could not navigate to that URL.")));
// TODO proper error handling
throw new RuntimeException("Invalid post listing URL");
}
switch(PrefsUtility.pref_behaviour_post_count(context, mSharedPreferences)) {
case ALL:
mPostCountLimit = -1;
break;
case R25:
mPostCountLimit = 25;
break;
case R50:
mPostCountLimit = 50;
break;
case R100:
mPostCountLimit = 100;
break;
default:
mPostCountLimit = 0;
break;
}
if(mPostCountLimit > 0) {
restackRefreshCount();
}
final ScrollbarRecyclerViewManager recyclerViewManager
= new ScrollbarRecyclerViewManager(context, null, false);
if(parent instanceof OptionsMenuUtility.OptionsMenuPostsListener
&& PrefsUtility.pref_behaviour_enable_swipe_refresh(context, mSharedPreferences)) {
recyclerViewManager.enablePullToRefresh(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
((OptionsMenuUtility.OptionsMenuPostsListener)parent).onRefreshPosts();
}
});
}
mRecyclerView = recyclerViewManager.getRecyclerView();
mPostListingManager.setLayoutManager((LinearLayoutManager) mRecyclerView.getLayoutManager());
mRecyclerView.setAdapter(mPostListingManager.getAdapter());
mOuter = recyclerViewManager.getOuterView();
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(final RecyclerView recyclerView, final int dx, final int dy) {
onLoadMoreItemsCheck();
}
});
mRecyclerView.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
int limit = 50;
if(mPostCountLimit > 0 && limit > mPostCountLimit) {
limit = mPostCountLimit;
}
final DownloadStrategy downloadStrategy;
if(forceDownload) {
downloadStrategy = DownloadStrategyAlways.INSTANCE;
} else if(session == null && savedInstanceState == null && General.isNetworkConnected(context)) {
final long maxAgeMs = PrefsUtility.pref_cache_rerequest_postlist_age_ms(context, mSharedPreferences);
downloadStrategy = new DownloadStrategyIfTimestampOutsideBounds(TimestampBound.notOlderThan(maxAgeMs));
} else {
downloadStrategy = DownloadStrategyIfNotCached.INSTANCE;
}
mRequest = new PostListingRequest(
mPostListingURL.generateJsonUri(),
RedditAccountManager.getInstance(context).getDefaultAccount(),
session,
downloadStrategy,
true);
// The request doesn't go ahead until the header is in place.
switch(mPostListingURL.pathType()) {
case RedditURLParser.USER_POST_LISTING_URL:
case RedditURLParser.SEARCH_POST_LISTING_URL:
case RedditURLParser.MULTIREDDIT_POST_LISTING_URL:
setHeader(mPostListingURL.humanReadableName(getActivity(), true), mPostListingURL.humanReadableUrl());
CacheManager.getInstance(context).makeRequest(mRequest);
break;
case RedditURLParser.SUBREDDIT_POST_LISTING_URL:
SubredditPostListURL subredditPostListURL
= (SubredditPostListURL)mPostListingURL;
switch(subredditPostListURL.type) {
case FRONTPAGE:
case ALL:
case SUBREDDIT_COMBINATION:
case ALL_SUBTRACTION:
case POPULAR:
setHeader(mPostListingURL.humanReadableName(getActivity(), true), mPostListingURL.humanReadableUrl());
CacheManager.getInstance(context).makeRequest(mRequest);
break;
case SUBREDDIT: {
// Request the subreddit data
final RequestResponseHandler<RedditSubreddit, SubredditRequestFailure> subredditHandler
= new RequestResponseHandler<RedditSubreddit, SubredditRequestFailure>() {
@Override
public void onRequestFailed(SubredditRequestFailure failureReason) {
// Ignore
AndroidApi.UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
CacheManager.getInstance(context).makeRequest(mRequest);
}
});
}
@Override
public void onRequestSuccess(final RedditSubreddit result, final long timeCached) {
AndroidApi.UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
mSubreddit = result;
onSubredditReceived();
CacheManager.getInstance(context).makeRequest(mRequest);
}
});
}
};
try {
RedditSubredditManager
.getInstance(getActivity(), RedditAccountManager.getInstance(getActivity()).getDefaultAccount())
.getSubreddit(RedditSubreddit.getCanonicalName(subredditPostListURL.subreddit), TimestampBound.NONE, subredditHandler, null);
} catch(RedditSubreddit.InvalidSubredditNameException e) {
throw new RuntimeException(e);
}
break;
}
}
break;
}
}
private LinearLayout createVerticalLinearLayout(Context context) {
final LinearLayout result = new LinearLayout(context);
result.setOrientation(LinearLayout.VERTICAL);
return result;
}
@Override
public View getView() {
return mOuter;
}
@Override
public Bundle onSaveInstanceState() {
final Bundle bundle = new Bundle();
final LinearLayoutManager layoutManager = (LinearLayoutManager)mRecyclerView.getLayoutManager();
bundle.putInt(SAVEDSTATE_FIRST_VISIBLE_POS, layoutManager.findFirstVisibleItemPosition());
return bundle;
}
public void cancel() {
if(mRequest != null) mRequest.cancel();
}
public synchronized void restackRefreshCount() {
while(mPostRefreshCount.get() <= 0) {
mPostRefreshCount.addAndGet(mPostCountLimit);
}
}
private void onSubredditReceived() {
final String subtitle;
if(mPostListingURL.getOrder() == null || mPostListingURL.getOrder() == PostSort.HOT) {
if(mSubreddit.subscribers == null) {
subtitle = getString(R.string.header_subscriber_count_unknown);
} else {
subtitle = getContext().getString(R.string.header_subscriber_count,
NumberFormat.getNumberInstance(Locale.getDefault()).format(mSubreddit.subscribers));
}
} else {
subtitle = mPostListingURL.humanReadableUrl();
}
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
setHeader(StringEscapeUtils.unescapeHtml4(mSubreddit.title), subtitle);
getActivity().invalidateOptionsMenu();
}
});
}
private void setHeader(final String title, final String subtitle) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
final PostListingHeader postListingHeader = new PostListingHeader(getActivity(), title, subtitle);
mPostListingManager.addPostListingHeader(postListingHeader);
}
});
}
public void onPostSelected(final RedditPreparedPost post) {
((RedditPostView.PostSelectionListener)getActivity()).onPostSelected(post);
new Thread() {
@Override
public void run() {
post.markAsRead(getActivity());
}
}.start();
}
public void onPostCommentsSelected(final RedditPreparedPost post) {
((RedditPostView.PostSelectionListener)getActivity()).onPostCommentsSelected(post);
new Thread() {
@Override
public void run() {
post.markAsRead(getActivity());
}
}.start();
}
private void onLoadMoreItemsCheck() {
General.checkThisIsUIThread();
if(mReadyToDownloadMore && mAfter != null && !mAfter.equals(mLastAfter)) {
final LinearLayoutManager layoutManager = (LinearLayoutManager)mRecyclerView.getLayoutManager();
if(mPostListingManager.getPostCount() > 0
&& (layoutManager.getItemCount() - layoutManager.findLastVisibleItemPosition() < 20
&& (mPostCountLimit <= 0 || mPostRefreshCount.get() > 0)
|| (mPreviousFirstVisibleItemPosition != null
&& layoutManager.getItemCount() <= mPreviousFirstVisibleItemPosition))) {
mLastAfter = mAfter;
mReadyToDownloadMore = false;
final Uri newUri = mPostListingURL.after(mAfter).generateJsonUri();
// TODO customise (currently 3 hrs)
final DownloadStrategy strategy = (RRTime.since(mTimestamp) < 3 * 60 * 60 * 1000)
? DownloadStrategyIfNotCached.INSTANCE
: DownloadStrategyNever.INSTANCE;
int limit = 50;
if(mPostCountLimit > 0 && limit > mPostRefreshCount.get()) {
limit = mPostRefreshCount.get();
}
mRequest = new PostListingRequest(newUri, RedditAccountManager.getInstance(getActivity()).getDefaultAccount(), mSession, strategy, false);
mPostListingManager.setLoadingVisible(true);
CacheManager.getInstance(getActivity()).makeRequest(mRequest);
} else if(mPostCountLimit > 0 && mPostRefreshCount.get() <= 0) {
if(mLoadMoreView == null) {
mLoadMoreView = (TextView)LayoutInflater.from(getContext()).inflate(R.layout.load_more_posts, null);
mLoadMoreView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPostListingManager.removeLoadMoreButton();
mLoadMoreView = null;
restackRefreshCount();
onLoadMoreItemsCheck();
}
});
mPostListingManager.addLoadMoreButton(mLoadMoreView);
}
}
}
}
public void onSubscribe() {
if(mPostListingURL.pathType() != RedditURLParser.SUBREDDIT_POST_LISTING_URL) return;
try {
RedditSubredditSubscriptionManager
.getSingleton(getActivity(), RedditAccountManager.getInstance(getActivity()).getDefaultAccount())
.subscribe(RedditSubreddit.getCanonicalName(mPostListingURL.asSubredditPostListURL().subreddit), getActivity());
} catch(RedditSubreddit.InvalidSubredditNameException e) {
throw new RuntimeException(e);
}
}
public void onUnsubscribe() {
if(mSubreddit == null) return;
try {
RedditSubredditSubscriptionManager
.getSingleton(getActivity(), RedditAccountManager.getInstance(getActivity()).getDefaultAccount())
.unsubscribe(mSubreddit.getCanonicalName(), getActivity());
} catch(RedditSubreddit.InvalidSubredditNameException e) {
throw new RuntimeException(e);
}
}
public RedditSubreddit getSubreddit() {
return mSubreddit;
}
private static Uri setUriDownloadCount(final Uri input, final int count) {
return input.buildUpon().appendQueryParameter("limit", String.valueOf(count)).build();
}
public void onPostsAdded() {
if(mPreviousFirstVisibleItemPosition == null) {
return;
}
final LinearLayoutManager layoutManager = (LinearLayoutManager)mRecyclerView.getLayoutManager();
if(layoutManager.getItemCount() > mPreviousFirstVisibleItemPosition) {
layoutManager.scrollToPositionWithOffset(mPreviousFirstVisibleItemPosition, 0);
mPreviousFirstVisibleItemPosition = null;
} else {
layoutManager.scrollToPosition(layoutManager.getItemCount() - 1);
}
}
private class PostListingRequest extends CacheRequest {
private final boolean firstDownload;
protected PostListingRequest(Uri url, RedditAccount user, UUID requestSession, DownloadStrategy downloadStrategy, boolean firstDownload) {
super(General.uriFromString(url.toString()), user, requestSession, Constants.Priority.API_POST_LIST, 0, downloadStrategy, Constants.FileType.POST_LIST, DOWNLOAD_QUEUE_REDDIT_API, true, false, getActivity());
this.firstDownload = firstDownload;
}
@Override
protected void onDownloadNecessary() {}
@Override
protected void onDownloadStarted() {}
@Override
protected void onCallbackException(final Throwable t) {
BugReportActivity.handleGlobalError(context, t);
}
@Override
protected void onFailure(final @CacheRequest.RequestFailureType int type, final Throwable t, final Integer status, final String readableMessage) {
AndroidApi.UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
mPostListingManager.setLoadingVisible(false);
final RRError error;
if(type == CacheRequest.REQUEST_FAILURE_CACHE_MISS) {
error = new RRError(
context.getString(R.string.error_postlist_cache_title),
context.getString(R.string.error_postlist_cache_message),
t,
status,
url.toString());
} else {
error = General.getGeneralErrorForFailure(context, type, t, status, url.toString());
}
mPostListingManager.addFooterError(new ErrorView(getActivity(), error));
}
});
}
@Override protected void onProgress(final boolean authorizationInProgress, final long bytesRead, final long totalBytes) {}
@Override protected void onSuccess(final CacheManager.ReadableCacheFile cacheFile, final long timestamp, final UUID session, final boolean fromCache, final String mimetype) {}
@Override
public void onJsonParseStarted(final JsonValue value, final long timestamp, final UUID session, final boolean fromCache) {
final AppCompatActivity activity = getActivity();
// TODO pref (currently 10 mins)
if(firstDownload && fromCache && RRTime.since(timestamp) > 10 * 60 * 1000) {
AndroidApi.UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
final TextView cacheNotif = (TextView)LayoutInflater.from(getActivity())
.inflate(R.layout.cached_header, null, false);
cacheNotif.setText(getActivity().getString(
R.string.listing_cached,
RRTime.formatDateTime(timestamp, getActivity())));
mPostListingManager.addNotification(cacheNotif);
}
});
} // TODO resuming a copy
if(firstDownload) {
((SessionChangeListener)activity).onSessionChanged(session, SessionChangeListener.SessionChangeType.POSTS, timestamp);
PostListingFragment.this.mSession = session;
PostListingFragment.this.mTimestamp = timestamp;
}
// TODO {"error": 403} is received for unauthorized subreddits
try {
final JsonBufferedObject thing = value.asObject();
final JsonBufferedObject listing = thing.getObject("data");
final JsonBufferedArray posts = listing.getArray("children");
final boolean isNsfwAllowed = PrefsUtility.pref_behaviour_nsfw(activity, mSharedPreferences);
final boolean isConnectionWifi = General.isConnectionWifi(activity);
final PrefsUtility.AppearanceThumbnailsShow thumbnailsPref = PrefsUtility.appearance_thumbnails_show(
activity, mSharedPreferences);
final boolean downloadThumbnails = thumbnailsPref == PrefsUtility.AppearanceThumbnailsShow.ALWAYS
|| (thumbnailsPref == PrefsUtility.AppearanceThumbnailsShow.WIFIONLY && isConnectionWifi);
final boolean showNsfwThumbnails = PrefsUtility.appearance_thumbnails_nsfw_show(activity, mSharedPreferences);
final PrefsUtility.CachePrecacheImages imagePrecachePref
= PrefsUtility.cache_precache_images(activity, mSharedPreferences);
final PrefsUtility.CachePrecacheComments commentPrecachePref
= PrefsUtility.cache_precache_comments(activity, mSharedPreferences);
final boolean precacheImages = (imagePrecachePref == PrefsUtility.CachePrecacheImages.ALWAYS
|| (imagePrecachePref == PrefsUtility.CachePrecacheImages.WIFIONLY && isConnectionWifi))
&& !General.isCacheDiskFull(activity);
final boolean precacheComments = (commentPrecachePref == PrefsUtility.CachePrecacheComments.ALWAYS
|| (commentPrecachePref == PrefsUtility.CachePrecacheComments.WIFIONLY && isConnectionWifi));
final PrefsUtility.ImageViewMode imageViewMode
= PrefsUtility.pref_behaviour_imageview_mode(activity, mSharedPreferences);
final PrefsUtility.GifViewMode gifViewMode
= PrefsUtility.pref_behaviour_gifview_mode(activity, mSharedPreferences);
final PrefsUtility.VideoViewMode videoViewMode
= PrefsUtility.pref_behaviour_videoview_mode(activity, mSharedPreferences);
final boolean subredditFilteringEnabled =
mPostListingURL.pathType() == RedditURLParser.SUBREDDIT_POST_LISTING_URL
&& (mPostListingURL.asSubredditPostListURL().type == SubredditPostListURL.Type.ALL
|| mPostListingURL.asSubredditPostListURL().type == SubredditPostListURL.Type.ALL_SUBTRACTION
|| mPostListingURL.asSubredditPostListURL().type == SubredditPostListURL.Type.POPULAR);
final List<String> blockedSubreddits = PrefsUtility.pref_blocked_subreddits(activity, mSharedPreferences); // Grab this so we don't have to pull from the prefs every post
Log.i(TAG, "Precaching images: " + (precacheImages ? "ON" : "OFF"));
Log.i(TAG, "Precaching comments: " + (precacheComments ? "ON" : "OFF"));
final CacheManager cm = CacheManager.getInstance(activity);
final boolean showSubredditName
= !(mPostListingURL != null
&& mPostListingURL.pathType() == RedditURLParser.SUBREDDIT_POST_LISTING_URL
&& mPostListingURL.asSubredditPostListURL().type == SubredditPostListURL.Type.SUBREDDIT);
final ArrayList<RedditPostListItem> downloadedPosts = new ArrayList<>(25);
for(final JsonValue postThingValue : posts) {
final RedditThing postThing = postThingValue.asObject(RedditThing.class);
if(!postThing.getKind().equals(RedditThing.Kind.POST)) continue;
final RedditPost post = postThing.asPost();
mAfter = post.name;
final boolean isPostBlocked = subredditFilteringEnabled && getIsPostBlocked(blockedSubreddits, post);
if(!isPostBlocked
&& (!post.over_18 || isNsfwAllowed)
&& mPostIds.add(post.getIdAlone())) {
final boolean downloadThisThumbnail = downloadThumbnails && (!post.over_18 || showNsfwThumbnails);
final int positionInList = mPostCount;
final RedditParsedPost parsedPost = new RedditParsedPost(post, false);
final RedditPreparedPost preparedPost = new RedditPreparedPost(
activity,
cm,
positionInList,
parsedPost,
timestamp,
showSubredditName,
downloadThisThumbnail);
if(precacheComments) {
final CommentListingController controller = new CommentListingController(
PostCommentListingURL.forPostId(preparedPost.src.getIdAlone()),
activity);
CacheManager.getInstance(activity).makeRequest(new CacheRequest(
General.uriFromString(controller.getUri().toString()),
RedditAccountManager.getInstance(activity).getDefaultAccount(),
null,
Constants.Priority.COMMENT_PRECACHE,
positionInList,
DownloadStrategyIfNotCached.INSTANCE,
Constants.FileType.COMMENT_LIST,
DOWNLOAD_QUEUE_REDDIT_API,
false, // Don't parse the JSON
false,
activity) {
@Override
protected void onCallbackException(final Throwable t) {}
@Override
protected void onDownloadNecessary() {}
@Override
protected void onDownloadStarted() {}
@Override
protected void onFailure(final @CacheRequest.RequestFailureType int type, final Throwable t, final Integer status, final String readableMessage) {
Log.e(TAG, "Failed to precache " + url.toString() + "(RequestFailureType code: " + type + ")");
}
@Override
protected void onProgress(final boolean authorizationInProgress, final long bytesRead, final long totalBytes) {}
@Override
protected void onSuccess(final CacheManager.ReadableCacheFile cacheFile, final long timestamp, final UUID session, final boolean fromCache, final String mimetype) {
Log.i(TAG, "Successfully precached " + url.toString());
}
});
}
LinkHandler.getImageInfo(activity, parsedPost.getUrl(), Constants.Priority.IMAGE_PRECACHE, positionInList, new GetImageInfoListener() {
@Override public void onFailure(final @CacheRequest.RequestFailureType int type, final Throwable t, final Integer status, final String readableMessage) {}
@Override public void onNotAnImage() {}
@Override
public void onSuccess(final ImageInfo info) {
if(!precacheImages) return;
// Don't precache huge images
if(info.size != null && info.size > 15 * 1024 * 1024) {
Log.i(TAG, String.format(
"Not precaching '%s': too big (%d kB)", post.url, info.size / 1024));
return;
}
// Don't precache gifs if they're opened externally
if(ImageInfo.MediaType.GIF.equals(info.mediaType)
&& !gifViewMode.downloadInApp) {
Log.i(TAG, String.format(
"Not precaching '%s': GIFs are opened externally", post.url));
return;
}
// Don't precache images if they're opened externally
if(ImageInfo.MediaType.IMAGE.equals(info.mediaType)
&& !imageViewMode.downloadInApp) {
Log.i(TAG, String.format(
"Not precaching '%s': images are opened externally", post.url));
return;
}
// Don't precache videos if they're opened externally
if(ImageInfo.MediaType.VIDEO.equals(info.mediaType)
&& !videoViewMode.downloadInApp) {
Log.i(TAG, String.format(
"Not precaching '%s': videos are opened externally", post.url));
return;
}
final URI uri = General.uriFromString(info.urlOriginal);
if(uri == null) {
Log.i(TAG, String.format(
"Not precaching '%s': failed to parse URL", post.url));
return;
}
CacheManager.getInstance(activity).makeRequest(new CacheRequest(
uri,
RedditAccountManager.getAnon(),
null,
Constants.Priority.IMAGE_PRECACHE,
positionInList,
DownloadStrategyIfNotCached.INSTANCE,
Constants.FileType.IMAGE,
DOWNLOAD_QUEUE_IMAGE_PRECACHE,
false,
false,
activity
) {
@Override protected void onCallbackException(final Throwable t) {}
@Override protected void onDownloadNecessary() {}
@Override protected void onDownloadStarted() {}
@Override protected void onFailure(final @CacheRequest.RequestFailureType int type, final Throwable t, final Integer status, final String readableMessage) {
Log.e(TAG, String.format(
Locale.US,
"Failed to precache %s (RequestFailureType %d, status %s, readable '%s')",
info.urlOriginal,
type,
status == null ? "NULL" : status.toString(),
readableMessage == null ? "NULL" : readableMessage));
}
@Override protected void onProgress(final boolean authorizationInProgress, final long bytesRead, final long totalBytes) {}
@Override protected void onSuccess(final CacheManager.ReadableCacheFile cacheFile, final long timestamp, final UUID session, final boolean fromCache, final String mimetype) {
Log.i(TAG, "Successfully precached " + info.urlOriginal);
}
});
}
});
downloadedPosts.add(new RedditPostListItem(
preparedPost,
PostListingFragment.this,
activity,
mPostListingURL));
mPostCount++;
mPostRefreshCount.decrementAndGet();
}
}
AndroidApi.UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
mPostListingManager.addPosts(downloadedPosts);
mPostListingManager.setLoadingVisible(false);
onPostsAdded();
mRequest = null;
mReadyToDownloadMore = true;
onLoadMoreItemsCheck();
}
});
} catch (Throwable t) {
notifyFailure(CacheRequest.REQUEST_FAILURE_PARSE, t, null, "Parse failure");
}
}
}
private boolean getIsPostBlocked(
@NonNull final List<String> blockedSubreddits,
@NonNull final RedditPost post) throws RedditSubreddit.InvalidSubredditNameException {
for (String blockedSubredditName : blockedSubreddits) {
if (blockedSubredditName.equalsIgnoreCase(RedditSubreddit.getCanonicalName(post.subreddit))) {
return true;
}
}
return false;
}
}