/*
* Copyright 2009 Andrew Shu
*
* This file is part of "reddit is fun".
*
* "reddit is fun" 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.
*
* "reddit is fun" 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 "reddit is fun". If not, see <http://www.gnu.org/licenses/>.
*/
package com.andrewshu.android.reddit.threads;
import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.http.client.HttpClient;
import org.codehaus.jackson.map.ObjectMapper;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.TextAppearanceSpan;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.webkit.CookieSyncManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.andrewshu.android.reddit.R;
import com.andrewshu.android.reddit.comments.CommentsListActivity;
import com.andrewshu.android.reddit.common.CacheInfo;
import com.andrewshu.android.reddit.common.Common;
import com.andrewshu.android.reddit.common.Constants;
import com.andrewshu.android.reddit.common.RedditIsFunHttpClientFactory;
import com.andrewshu.android.reddit.common.tasks.HideTask;
import com.andrewshu.android.reddit.common.tasks.SaveTask;
import com.andrewshu.android.reddit.common.tasks.VoteTask;
import com.andrewshu.android.reddit.common.util.StringUtils;
import com.andrewshu.android.reddit.common.util.Util;
import com.andrewshu.android.reddit.login.LoginDialog;
import com.andrewshu.android.reddit.login.LoginTask;
import com.andrewshu.android.reddit.mail.InboxActivity;
import com.andrewshu.android.reddit.mail.PeekEnvelopeTask;
import com.andrewshu.android.reddit.reddits.PickSubredditActivity;
import com.andrewshu.android.reddit.reddits.SubscribeTask;
import com.andrewshu.android.reddit.reddits.UnsubscribeTask;
import com.andrewshu.android.reddit.settings.RedditPreferencesPage;
import com.andrewshu.android.reddit.settings.RedditSettings;
import com.andrewshu.android.reddit.submit.SubmitLinkActivity;
import com.andrewshu.android.reddit.things.ThingInfo;
import com.andrewshu.android.reddit.threads.ShowThumbnailsTask.ThumbnailLoadAction;
import com.andrewshu.android.reddit.user.ProfileActivity;
/**
* Main Activity class representing a Subreddit, i.e., a ThreadsList.
*
* @author TalkLittle
*
*/
public final class ThreadsListActivity extends ListActivity {
private static final String TAG = "ThreadsListActivity";
private final Pattern REDDIT_PATH_PATTERN = Pattern.compile(Constants.REDDIT_PATH_PATTERN_STRING);
private final ObjectMapper mObjectMapper = Common.getObjectMapper();
/** Custom list adapter that fits our threads data into the list. */
private ThreadsListAdapter mThreadsAdapter = null;
private ArrayList<ThingInfo> mThreadsList = null;
private static final Object THREAD_ADAPTER_LOCK = new Object();
private final HttpClient mClient = RedditIsFunHttpClientFactory.getGzipHttpClient();
private final RedditSettings mSettings = new RedditSettings();
// UI State
private ThingInfo mVoteTargetThing = null;
private DownloadThreadsTask mCurrentDownloadThreadsTask = null;
private final Object mCurrentDownloadThreadsTaskLock = new Object();
private View mNextPreviousView = null;
private ShowThumbnailsTask mCurrentShowThumbnailsTask = null;
private final Object mCurrentShowThumbnailsTaskLock = new Object();
// Navigation that can be cached
private String mSubreddit = Constants.FRONTPAGE_STRING;
// The after, before, and count to navigate away from current page of results
private String mAfter = null;
private String mBefore = null;
private volatile int mCount = Constants.DEFAULT_THREAD_DOWNLOAD_LIMIT;
// The after, before, and count to navigate to current page
private String mLastAfter = null;
private String mLastBefore = null;
private volatile int mLastCount = 0;
private String mSortByUrl = Constants.ThreadsSort.SORT_BY_HOT_URL;
private String mSortByUrlExtra = "";
private String mJumpToThreadId = null;
// End navigation variables
// Menu
private boolean mCanChord = false;
/**
* Called when the activity starts up. Do activity initialization
* here, not in a constructor.
*
* @see Activity#onCreate
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CookieSyncManager.createInstance(getApplicationContext());
mSettings.loadRedditPreferences(getApplicationContext(), mClient);
setRequestedOrientation(mSettings.getRotation());
setTheme(mSettings.getTheme());
requestWindowFeature(Window.FEATURE_PROGRESS);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.threads_list_content);
registerForContextMenu(getListView());
if (savedInstanceState != null) {
if (Constants.LOGGING) Log.d(TAG, "using savedInstanceState");
mSubreddit = savedInstanceState.getString(Constants.SUBREDDIT_KEY);
if (mSubreddit == null)
mSubreddit = mSettings.getHomepage();
mAfter = savedInstanceState.getString(Constants.AFTER_KEY);
mBefore = savedInstanceState.getString(Constants.BEFORE_KEY);
mCount = savedInstanceState.getInt(Constants.THREAD_COUNT_KEY);
mLastAfter = savedInstanceState.getString(Constants.LAST_AFTER_KEY);
mLastBefore = savedInstanceState.getString(Constants.LAST_BEFORE_KEY);
mLastCount = savedInstanceState.getInt(Constants.THREAD_LAST_COUNT_KEY);
mSortByUrl = savedInstanceState.getString(Constants.ThreadsSort.SORT_BY_KEY);
mJumpToThreadId = savedInstanceState.getString(Constants.JUMP_TO_THREAD_ID_KEY);
mVoteTargetThing = savedInstanceState.getParcelable(Constants.VOTE_TARGET_THING_INFO_KEY);
// try to restore mThreadsList using getLastNonConfigurationInstance()
// (separate function to avoid a compiler warning casting ArrayList<ThingInfo>
restoreLastNonConfigurationInstance();
if (mThreadsList == null) {
// Load previous view of threads
if (mLastAfter != null) {
new MyDownloadThreadsTask(mSubreddit, mLastAfter, null, mLastCount).execute();
} else if (mLastBefore != null) {
new MyDownloadThreadsTask(mSubreddit, null, mLastBefore, mLastCount).execute();
} else {
new MyDownloadThreadsTask(mSubreddit).execute();
}
} else {
// Orientation change. Use prior instance.
resetUI(new ThreadsListAdapter(this, mThreadsList));
if (Constants.FRONTPAGE_STRING.equals(mSubreddit))
setTitle("reddit.com: what's new online!");
else
setTitle("/r/" + mSubreddit.trim());
}
}
// Handle subreddit Uri passed via Intent
else if (getIntent().getData() != null) {
Matcher redditContextMatcher = REDDIT_PATH_PATTERN.matcher(getIntent().getData().getPath());
if (redditContextMatcher.matches()) {
new MyDownloadThreadsTask(redditContextMatcher.group(1)).execute();
} else {
new MyDownloadThreadsTask(mSettings.getHomepage()).execute();
}
}
// No subreddit specified by Intent, so load the user's home reddit
else {
new MyDownloadThreadsTask(mSettings.getHomepage()).execute();
}
}
@Override
protected void onResume() {
super.onResume();
// SOPA blackout: Jan 18, 2012 from 8am-8pm EST (1300-0100 UTC)
long timeMillis = System.currentTimeMillis();
if (timeMillis >= 1326891600000L && timeMillis <= 1326934800000L) {
Toast.makeText(this, "Let's Protest SOPA", Toast.LENGTH_LONG).show();
Common.launchBrowser(this, "http://www.reddit.com", null, false, true, false, false);
finish();
return;
}
int previousTheme = mSettings.getTheme();
mSettings.loadRedditPreferences(this, mClient);
if (mSettings.getTheme() != previousTheme) {
relaunchActivity();
}
else {
CookieSyncManager.getInstance().startSync();
setRequestedOrientation(mSettings.getRotation());
updateNextPreviousButtons();
if (mThreadsAdapter != null)
jumpToThread();
if (mSettings.isLoggedIn())
new PeekEnvelopeTask(this, mClient, mSettings.getMailNotificationStyle()).execute();
}
}
private void relaunchActivity() {
finish();
startActivity(getIntent());
}
@Override
protected void onPause() {
super.onPause();
CookieSyncManager.getInstance().stopSync();
mSettings.saveRedditPreferences(this);
}
@Override
public Object onRetainNonConfigurationInstance() {
// Avoid having to re-download and re-parse the threads list
// when rotating or opening keyboard.
return mThreadsList;
}
@SuppressWarnings("unchecked")
private void restoreLastNonConfigurationInstance() {
mThreadsList = (ArrayList<ThingInfo>) getLastNonConfigurationInstance();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
switch(requestCode) {
case Constants.ACTIVITY_PICK_SUBREDDIT:
if (resultCode == Activity.RESULT_OK) {
Matcher redditContextMatcher = REDDIT_PATH_PATTERN.matcher(intent.getData().getPath());
if (redditContextMatcher.matches()) {
new MyDownloadThreadsTask(redditContextMatcher.group(1)).execute();
}
}
break;
default:
break;
}
}
/**
* http://stackoverflow.com/questions/2257963/android-how-to-show-dialog-to-confirm-user-wishes-to-exit-activity
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//Handle the back button
if(mSettings.isConfirmQuitOrLogout() && keyCode == KeyEvent.KEYCODE_BACK && isTaskRoot()) {
//Ask the user if they want to quit
new AlertDialog.Builder(new ContextThemeWrapper(this, mSettings.getDialogTheme()))
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.quit)
.setMessage(R.string.really_quit)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//Stop the activity
finish();
}
})
.setNegativeButton(R.string.no, null)
.show();
return true;
}
else {
return super.onKeyDown(keyCode, event);
}
}
final class ThreadsListAdapter extends ArrayAdapter<ThingInfo> {
static final int THREAD_ITEM_VIEW_TYPE = 0;
// The number of view types
static final int VIEW_TYPE_COUNT = 1;
public boolean mIsLoading = true;
private LayoutInflater mInflater;
private int mFrequentSeparatorPos = ListView.INVALID_POSITION;
public ThreadsListAdapter(Context context, List<ThingInfo> objects) {
super(context, 0, objects);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getItemViewType(int position) {
if (position == mFrequentSeparatorPos) {
// We don't want the separator view to be recycled.
return IGNORE_ITEM_VIEW_TYPE;
}
return THREAD_ITEM_VIEW_TYPE;
}
@Override
public int getViewTypeCount() {
return VIEW_TYPE_COUNT;
}
@Override
public boolean isEmpty() {
if (mIsLoading)
return false;
return super.isEmpty();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
// Here view may be passed in for re-use, or we make a new one.
if (convertView == null) {
view = mInflater.inflate(R.layout.threads_list_item, null);
} else {
view = convertView;
}
ThingInfo item = this.getItem(position);
// Set the values of the Views for the ThreadsListItem
fillThreadsListItemView(
position, view, item, ThreadsListActivity.this, mClient, mSettings, mThumbnailOnClickListenerFactory
);
return view;
}
}
public static void fillThreadsListItemView(
int position,
View view,
ThingInfo item,
ListActivity activity,
HttpClient client,
RedditSettings settings,
ThumbnailOnClickListenerFactory thumbnailOnClickListenerFactory
) {
Resources res = activity.getResources();
TextView titleView = (TextView) view.findViewById(R.id.title);
TextView votesView = (TextView) view.findViewById(R.id.votes);
TextView numCommentsSubredditView = (TextView) view.findViewById(R.id.numCommentsSubreddit);
TextView nsfwView = (TextView) view.findViewById(R.id.nsfw);
// TextView submissionTimeView = (TextView) view.findViewById(R.id.submissionTime);
ImageView voteUpView = (ImageView) view.findViewById(R.id.vote_up_image);
ImageView voteDownView = (ImageView) view.findViewById(R.id.vote_down_image);
View thumbnailContainer = view.findViewById(R.id.thumbnail_view);
FrameLayout thumbnailFrame = (FrameLayout) view.findViewById(R.id.thumbnail_frame);
ImageView thumbnailImageView = (ImageView) view.findViewById(R.id.thumbnail);
ProgressBar indeterminateProgressBar = (ProgressBar) view.findViewById(R.id.indeterminate_progress);
// Set the title and domain using a SpannableStringBuilder
SpannableStringBuilder builder = new SpannableStringBuilder();
String title = item.getTitle();
if (title == null)
title = "";
SpannableString titleSS = new SpannableString(title);
int titleLen = title.length();
titleSS.setSpan(new TextAppearanceSpan(activity,
Util.getTextAppearanceResource(settings.getTheme(), android.R.style.TextAppearance_Large)),
0, titleLen, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
String domain = item.getDomain();
if (domain == null)
domain = "";
int domainLen = domain.length();
SpannableString domainSS = new SpannableString("("+item.getDomain()+")");
domainSS.setSpan(new TextAppearanceSpan(activity,
Util.getTextAppearanceResource(settings.getTheme(), android.R.style.TextAppearance_Small)),
0, domainLen+2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (Util.isLightTheme(settings.getTheme())) {
if (item.isClicked()) {
ForegroundColorSpan fcs = new ForegroundColorSpan(res.getColor(R.color.purple));
titleSS.setSpan(fcs, 0, titleLen, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
ForegroundColorSpan fcs = new ForegroundColorSpan(res.getColor(R.color.blue));
titleSS.setSpan(fcs, 0, titleLen, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
domainSS.setSpan(new ForegroundColorSpan(res.getColor(R.color.gray_50)),
0, domainLen+2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
if (item.isClicked()) {
ForegroundColorSpan fcs = new ForegroundColorSpan(res.getColor(R.color.gray_50));
titleSS.setSpan(fcs, 0, titleLen, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
domainSS.setSpan(new ForegroundColorSpan(res.getColor(R.color.gray_75)),
0, domainLen+2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
builder.append(titleSS).append(" ").append(domainSS);
titleView.setText(builder);
votesView.setText("" + item.getScore());
numCommentsSubredditView.setText(Util.showNumComments(item.getNum_comments()) + " " + item.getSubreddit());
if(item.isOver_18()){
nsfwView.setVisibility(View.VISIBLE);
} else {
nsfwView.setVisibility(View.GONE);
}
// Set the up and down arrow colors based on whether user likes
if (settings.isLoggedIn()) {
if (item.getLikes() == null) {
voteUpView.setImageResource(R.drawable.vote_up_gray);
voteDownView.setImageResource(R.drawable.vote_down_gray);
votesView.setTextColor(res.getColor(R.color.gray_75));
} else if (item.getLikes() == true) {
voteUpView.setImageResource(R.drawable.vote_up_red);
voteDownView.setImageResource(R.drawable.vote_down_gray);
votesView.setTextColor(res.getColor(R.color.arrow_red));
} else {
voteUpView.setImageResource(R.drawable.vote_up_gray);
voteDownView.setImageResource(R.drawable.vote_down_blue);
votesView.setTextColor(res.getColor(R.color.arrow_blue));
}
} else {
voteUpView.setImageResource(R.drawable.vote_up_gray);
voteDownView.setImageResource(R.drawable.vote_down_gray);
votesView.setTextColor(res.getColor(R.color.gray_75));
}
// Thumbnails open links
if (thumbnailContainer != null) {
if (Common.shouldLoadThumbnails(activity, settings)) {
thumbnailContainer.setVisibility(View.VISIBLE);
if (item.getUrl() != null) {
OnClickListener thumbnailOnClickListener = thumbnailOnClickListenerFactory.getThumbnailOnClickListener(item, activity);
if (thumbnailOnClickListener != null) {
thumbnailFrame.setOnClickListener(thumbnailOnClickListener);
}
}
// Show thumbnail based on ThingInfo
if ("default".equals(item.getThumbnail()) || "self".equals(item.getThumbnail()) || StringUtils.isEmpty(item.getThumbnail())) {
indeterminateProgressBar.setVisibility(View.GONE);
thumbnailImageView.setVisibility(View.VISIBLE);
thumbnailImageView.setImageResource(R.drawable.go_arrow);
}
else {
indeterminateProgressBar.setVisibility(View.GONE);
thumbnailImageView.setVisibility(View.VISIBLE);
if (item.getThumbnailBitmap() != null) {
thumbnailImageView.setImageBitmap(item.getThumbnailBitmap());
}
else {
thumbnailImageView.setImageBitmap(null);
new ShowThumbnailsTask(activity, client, R.drawable.go_arrow).execute(new ThumbnailLoadAction(item, thumbnailImageView, position));
}
}
// Set thumbnail background based on current theme
if (Util.isLightTheme(settings.getTheme()))
thumbnailFrame.setBackgroundResource(R.drawable.thumbnail_background_light);
else
thumbnailFrame.setBackgroundResource(R.drawable.thumbnail_background_dark);
} else {
// if thumbnails disabled, hide thumbnail icon
thumbnailContainer.setVisibility(View.GONE);
}
}
}
public static void fillThreadClickDialog(Dialog dialog, ThingInfo thingInfo, RedditSettings settings,
ThreadClickDialogOnClickListenerFactory threadClickDialogOnClickListenerFactory) {
final CheckBox voteUpButton = (CheckBox) dialog.findViewById(R.id.vote_up_button);
final CheckBox voteDownButton = (CheckBox) dialog.findViewById(R.id.vote_down_button);
final TextView titleView = (TextView) dialog.findViewById(R.id.title);
final TextView urlView = (TextView) dialog.findViewById(R.id.url);
final TextView submissionStuffView = (TextView) dialog.findViewById(R.id.submissionTime_submitter_subreddit);
final Button loginButton = (Button) dialog.findViewById(R.id.login_button);
final Button linkButton = (Button) dialog.findViewById(R.id.thread_link_button);
final Button commentsButton = (Button) dialog.findViewById(R.id.thread_comments_button);
titleView.setText(thingInfo.getTitle());
urlView.setText(thingInfo.getUrl());
StringBuilder sb = new StringBuilder(Util.getTimeAgo(thingInfo.getCreated_utc()))
.append(" by ").append(thingInfo.getAuthor())
.append(" to ").append(thingInfo.getSubreddit());
submissionStuffView.setText(sb);
// Only show upvote/downvote if user is logged in
if (settings.isLoggedIn()) {
loginButton.setVisibility(View.GONE);
voteUpButton.setVisibility(View.VISIBLE);
voteDownButton.setVisibility(View.VISIBLE);
// Remove the OnCheckedChangeListeners because we are about to setChecked(),
// and I think the Buttons are recycled, so old listeners will fire
// for the previous vote target ThingInfo.
voteUpButton.setOnCheckedChangeListener(null);
voteDownButton.setOnCheckedChangeListener(null);
// Set initial states of the vote buttons based on user's past actions
if (thingInfo.getLikes() == null) {
// User is currently neutral
voteUpButton.setChecked(false);
voteDownButton.setChecked(false);
} else if (thingInfo.getLikes() == true) {
// User currenty likes it
voteUpButton.setChecked(true);
voteDownButton.setChecked(false);
} else {
// User currently dislikes it
voteUpButton.setChecked(false);
voteDownButton.setChecked(true);
}
voteUpButton.setOnCheckedChangeListener(
threadClickDialogOnClickListenerFactory.getVoteUpOnCheckedChangeListener(thingInfo));
voteDownButton.setOnCheckedChangeListener(
threadClickDialogOnClickListenerFactory.getVoteDownOnCheckedChangeListener(thingInfo));
} else {
voteUpButton.setVisibility(View.GONE);
voteDownButton.setVisibility(View.GONE);
loginButton.setVisibility(View.VISIBLE);
loginButton.setOnClickListener(
threadClickDialogOnClickListenerFactory.getLoginOnClickListener());
}
// "link" button behaves differently for regular links vs. self posts and links to comments pages (e.g., bestof)
if (thingInfo.isIs_self()) {
// It's a self post. Both buttons do the same thing.
linkButton.setEnabled(false);
} else {
linkButton.setOnClickListener(
threadClickDialogOnClickListenerFactory.getLinkOnClickListener(thingInfo, settings.isUseExternalBrowser()));
linkButton.setEnabled(true);
}
// "comments" button is easy: always does the same thing
commentsButton.setOnClickListener(
threadClickDialogOnClickListenerFactory.getCommentsOnClickListener(thingInfo));
}
/**
* Jump to thread whose id is mJumpToThreadId. Then clear mJumpToThreadId.
*/
private void jumpToThread() {
if (mJumpToThreadId == null || mThreadsAdapter == null)
return;
for (int k = 0; k < mThreadsAdapter.getCount(); k++) {
if (mJumpToThreadId.equals(mThreadsAdapter.getItem(k).getId())) {
getListView().setSelection(k);
mJumpToThreadId = null;
break;
}
}
}
/**
* Called when user clicks an item in the list. Starts an activity to
* open the url for that item.
*/
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
ThingInfo item = mThreadsAdapter.getItem(position);
// Mark the thread as selected
mVoteTargetThing = item;
mJumpToThreadId = item.getId();
showDialog(Constants.DIALOG_THREAD_CLICK);
}
/**
* Resets the output UI list contents, retains session state.
* @param threadsAdapter A ThreadsListAdapter to use. Pass in null if you want a new empty one created.
*/
void resetUI(ThreadsListAdapter threadsAdapter) {
findViewById(R.id.loading_light).setVisibility(View.GONE);
findViewById(R.id.loading_dark).setVisibility(View.GONE);
if (mSettings.isAlwaysShowNextPrevious()) {
if (mNextPreviousView != null) {
getListView().removeFooterView(mNextPreviousView);
mNextPreviousView = null;
}
} else {
findViewById(R.id.next_previous_layout).setVisibility(View.GONE);
if (getListView().getFooterViewsCount() == 0) {
// If we are not using the persistent navbar, then show as ListView footer instead
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mNextPreviousView = inflater.inflate(R.layout.next_previous_list_item, null);
getListView().addFooterView(mNextPreviousView);
}
}
synchronized (THREAD_ADAPTER_LOCK) {
if (threadsAdapter == null) {
// Reset the list to be empty.
mThreadsList = new ArrayList<ThingInfo>();
mThreadsAdapter = new ThreadsListAdapter(this, mThreadsList);
} else {
mThreadsAdapter = threadsAdapter;
}
setListAdapter(mThreadsAdapter);
mThreadsAdapter.mIsLoading = false;
mThreadsAdapter.notifyDataSetChanged(); // Just in case
}
Common.updateListDrawables(this, mSettings.getTheme());
updateNextPreviousButtons();
}
private void enableLoadingScreen() {
if (Util.isLightTheme(mSettings.getTheme())) {
findViewById(R.id.loading_light).setVisibility(View.VISIBLE);
findViewById(R.id.loading_dark).setVisibility(View.GONE);
} else {
findViewById(R.id.loading_light).setVisibility(View.GONE);
findViewById(R.id.loading_dark).setVisibility(View.VISIBLE);
}
synchronized (THREAD_ADAPTER_LOCK) {
if (mThreadsAdapter != null)
mThreadsAdapter.mIsLoading = true;
}
getWindow().setFeatureInt(Window.FEATURE_PROGRESS, Window.PROGRESS_START);
}
private void disableLoadingScreen() {
resetUI(mThreadsAdapter);
getWindow().setFeatureInt(Window.FEATURE_PROGRESS, Window.PROGRESS_END);
}
private void updateNextPreviousButtons() {
Common.updateNextPreviousButtons(this, mNextPreviousView, mAfter, mBefore, mCount, mSettings,
downloadAfterOnClickListener, downloadBeforeOnClickListener);
}
/**
* Given a subreddit name string, starts the threadlist-download-thread going.
*
* @param subreddit The name of a subreddit ("android", "gaming", etc.)
* If the number of elements in subreddit is >= 2, treat 2nd element as "after"
*/
private class MyDownloadThreadsTask extends DownloadThreadsTask {
public MyDownloadThreadsTask(String subreddit) {
super(getApplicationContext(),
ThreadsListActivity.this.mClient,
ThreadsListActivity.this.mObjectMapper,
ThreadsListActivity.this.mSortByUrl,
ThreadsListActivity.this.mSortByUrlExtra,
subreddit);
}
public MyDownloadThreadsTask(String subreddit,
String after, String before, int count) {
super(getApplicationContext(),
ThreadsListActivity.this.mClient,
ThreadsListActivity.this.mObjectMapper,
ThreadsListActivity.this.mSortByUrl,
ThreadsListActivity.this.mSortByUrlExtra,
subreddit, after, before, count);
}
@Override
protected void saveState() {
mSettings.setModhash(mModhash);
ThreadsListActivity.this.mSubreddit = mSubreddit;
ThreadsListActivity.this.mLastAfter = mLastAfter;
ThreadsListActivity.this.mLastBefore = mLastBefore;
ThreadsListActivity.this.mLastCount = mLastCount;
ThreadsListActivity.this.mAfter = mAfter;
ThreadsListActivity.this.mBefore = mBefore;
ThreadsListActivity.this.mCount = mCount;
ThreadsListActivity.this.mSortByUrl = mSortByUrl;
ThreadsListActivity.this.mSortByUrlExtra = mSortByUrlExtra;
}
@Override
public void onPreExecute() {
synchronized (mCurrentDownloadThreadsTaskLock) {
if (mCurrentDownloadThreadsTask != null) {
this.cancel(true);
return;
}
mCurrentDownloadThreadsTask = this;
}
resetUI(null);
enableLoadingScreen();
if (mContentLength == -1) {
getWindow().setFeatureInt(Window.FEATURE_PROGRESS, Window.PROGRESS_INDETERMINATE_ON);
}
else {
getWindow().setFeatureInt(Window.FEATURE_PROGRESS, 0);
}
if (Constants.FRONTPAGE_STRING.equals(mSubreddit))
setTitle("reddit.com: what's new online!");
else
setTitle("/r/" + mSubreddit.trim());
}
@Override
public void onPostExecute(Boolean success) {
synchronized (mCurrentDownloadThreadsTaskLock) {
mCurrentDownloadThreadsTask = null;
}
disableLoadingScreen();
if (mContentLength == -1)
getWindow().setFeatureInt(Window.FEATURE_PROGRESS, Window.PROGRESS_INDETERMINATE_OFF);
else
getWindow().setFeatureInt(Window.FEATURE_PROGRESS, Window.PROGRESS_END);
if (success) {
synchronized (THREAD_ADAPTER_LOCK) {
mThreadsList.addAll(mThingInfos);
mThreadsAdapter.notifyDataSetChanged();
}
showThumbnails(mThingInfos);
updateNextPreviousButtons();
// Point the list to last thread user was looking at, if any
jumpToThread();
} else {
if (!isCancelled())
Common.showErrorToast(mUserError, Toast.LENGTH_LONG, ThreadsListActivity.this);
}
}
@Override
public void onProgressUpdate(Long... progress) {
if (mContentLength == -1) {
getWindow().setFeatureInt(Window.FEATURE_PROGRESS, Window.PROGRESS_INDETERMINATE_ON);
}
else {
getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress[0].intValue() * (Window.PROGRESS_END-1) / (int) mContentLength);
}
}
public void propertyChange(PropertyChangeEvent event) {
publishProgress((Long) event.getNewValue());
}
}
private void showThumbnails(List<ThingInfo> thingInfos) {
if (Common.shouldLoadThumbnails(this, mSettings)) {
int size = thingInfos.size();
ThumbnailLoadAction[] thumbnailLoadActions = new ThumbnailLoadAction[size];
for (int i = 0; i < size; i++) {
thumbnailLoadActions[i] = new ThumbnailLoadAction(thingInfos.get(i), null, i);
}
synchronized (mCurrentShowThumbnailsTaskLock) {
if (mCurrentShowThumbnailsTask != null)
mCurrentShowThumbnailsTask.cancel(true);
mCurrentShowThumbnailsTask = new ShowThumbnailsTask(this, mClient, R.drawable.go_arrow);
}
mCurrentShowThumbnailsTask.execute(thumbnailLoadActions);
}
}
private class MyLoginTask extends LoginTask {
public MyLoginTask(String username, String password) {
super(username, password, mSettings, mClient, getApplicationContext());
}
@Override
protected void onPreExecute() {
showDialog(Constants.DIALOG_LOGGING_IN);
}
@Override
protected void onPostExecute(Boolean success) {
removeDialog(Constants.DIALOG_LOGGING_IN);
if (success) {
Toast.makeText(ThreadsListActivity.this, "Logged in as "+mUsername, Toast.LENGTH_SHORT).show();
// Check mail
new PeekEnvelopeTask(getApplicationContext(), mClient, mSettings.getMailNotificationStyle()).execute();
// Refresh the threads list
new MyDownloadThreadsTask(mSubreddit).execute();
} else {
Common.showErrorToast(mUserError, Toast.LENGTH_LONG, ThreadsListActivity.this);
}
}
}
private class MyVoteTask extends VoteTask {
private int _mPreviousScore;
private Boolean _mPreviousLikes;
private ThingInfo _mTargetThingInfo;
public MyVoteTask(ThingInfo thingInfo, int direction, String subreddit) {
super(thingInfo.getName(), direction, subreddit, getApplicationContext(), mSettings, mClient);
_mTargetThingInfo = thingInfo;
_mPreviousScore = thingInfo.getScore();
_mPreviousLikes = thingInfo.getLikes();
}
@Override
public void onPreExecute() {
if (!_mSettings.isLoggedIn()) {
Common.showErrorToast("You must be logged in to vote.", Toast.LENGTH_LONG, _mContext);
cancel(true);
return;
}
if (_mDirection < -1 || _mDirection > 1) {
if (Constants.LOGGING) Log.e(TAG, "WTF: _mDirection = " + _mDirection);
throw new RuntimeException("How the hell did you vote something besides -1, 0, or 1?");
}
int newScore;
Boolean newLikes;
_mPreviousScore = Integer.valueOf(_mTargetThingInfo.getScore());
_mPreviousLikes = _mTargetThingInfo.getLikes();
if (_mPreviousLikes == null) {
if (_mDirection == 1) {
newScore = _mPreviousScore + 1;
newLikes = true;
} else if (_mDirection == -1) {
newScore = _mPreviousScore - 1;
newLikes = false;
} else {
cancel(true);
return;
}
} else if (_mPreviousLikes == true) {
if (_mDirection == 0) {
newScore = _mPreviousScore - 1;
newLikes = null;
} else if (_mDirection == -1) {
newScore = _mPreviousScore - 2;
newLikes = false;
} else {
cancel(true);
return;
}
} else {
if (_mDirection == 1) {
newScore = _mPreviousScore + 2;
newLikes = true;
} else if (_mDirection == 0) {
newScore = _mPreviousScore + 1;
newLikes = null;
} else {
cancel(true);
return;
}
}
_mTargetThingInfo.setLikes(newLikes);
_mTargetThingInfo.setScore(newScore);
mThreadsAdapter.notifyDataSetChanged();
}
@Override
public void onPostExecute(Boolean success) {
if (success) {
CacheInfo.invalidateCachedSubreddit(_mContext);
} else {
// Vote failed. Undo the score.
_mTargetThingInfo.setLikes(_mPreviousLikes);
_mTargetThingInfo.setScore(_mPreviousScore);
mThreadsAdapter.notifyDataSetChanged();
Common.showErrorToast(_mUserError, Toast.LENGTH_LONG, _mContext);
}
}
}
private final class MyHideTask extends HideTask {
public MyHideTask(boolean hide, ThingInfo mVoteTargetThreadInfo,
RedditSettings mSettings, Context mContext) {
super(hide, mVoteTargetThreadInfo, mSettings, mContext);
}
@Override
public void onPostExecute(Boolean success) {
// super shows error on success==false
super.onPostExecute(success);
if (success) {
synchronized (THREAD_ADAPTER_LOCK) {
// Remove from list even if unhiding--because the only place you can
// unhide from is the list of Hidden threads.
mThreadsAdapter.remove(mTargetThreadInfo);
mThreadsAdapter.notifyDataSetChanged();
}
}
}
}
/**
* Populates the menu.
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.subreddit, menu);
return true;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo){
super.onCreateContextMenu(menu, v, menuInfo);
AdapterView.AdapterContextMenuInfo info;
info = (AdapterView.AdapterContextMenuInfo) menuInfo;
ThingInfo _item = mThreadsAdapter.getItem(info.position);
mVoteTargetThing = _item;
menu.add(0, Constants.VIEW_SUBREDDIT_CONTEXT_ITEM, 0, R.string.view_subreddit);
menu.add(0, Constants.SHARE_CONTEXT_ITEM, 0, R.string.share);
menu.add(0, Constants.OPEN_IN_BROWSER_CONTEXT_ITEM, 0, R.string.open_browser);
if(mSettings.isLoggedIn()){
if(!_item.isSaved()){
menu.add(0, Constants.SAVE_CONTEXT_ITEM, 0, "Save");
} else {
menu.add(0, Constants.UNSAVE_CONTEXT_ITEM, 0, "Unsave");
}
menu.add(0, Constants.HIDE_CONTEXT_ITEM, 0, "Hide");
}
menu.add(0, Constants.DIALOG_VIEW_PROFILE, Menu.NONE,
String.format(getResources().getString(R.string.user_profile), _item.getAuthor()));
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info;
info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
ThingInfo _item = mThreadsAdapter.getItem(info.position);
switch (item.getItemId()) {
case Constants.VIEW_SUBREDDIT_CONTEXT_ITEM:
new MyDownloadThreadsTask(_item.getSubreddit()).execute();
return true;
case Constants.SHARE_CONTEXT_ITEM:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, _item.getUrl());
try {
startActivity(Intent.createChooser(intent, "Share Link"));
} catch (android.content.ActivityNotFoundException ex) {
if (Constants.LOGGING) Log.e(TAG, "Share Link", ex);
}
return true;
case Constants.OPEN_IN_BROWSER_CONTEXT_ITEM:
setLinkClicked(_item);
Common.launchBrowser(this, _item.getUrl(), Util.createThreadUri(_item).toString(), false, true, true, mSettings.isSaveHistory());
return true;
case Constants.SAVE_CONTEXT_ITEM:
new SaveTask(true, _item, mSettings, getApplicationContext()).execute();
return true;
case Constants.UNSAVE_CONTEXT_ITEM:
new SaveTask(false, _item, mSettings, getApplicationContext()).execute();
return true;
case Constants.HIDE_CONTEXT_ITEM:
new MyHideTask(true, _item, mSettings, getApplicationContext()).execute();
return true;
case Constants.UNHIDE_CONTEXT_ITEM:
new MyHideTask(false, _item, mSettings, getApplicationContext()).execute();
case Constants.DIALOG_VIEW_PROFILE:
Intent i = new Intent(this, ProfileActivity.class);
i.setData(Util.createProfileUri(_item.getAuthor()));
startActivity(i);
return true;
default:
return super.onContextItemSelected(item);
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// This happens when the user begins to hold down the menu key, so
// allow them to chord to get a shortcut.
mCanChord = true;
super.onPrepareOptionsMenu(menu);
MenuItem src, dest;
// Login/Logout
if (mSettings.isLoggedIn()) {
menu.findItem(R.id.login_menu_id).setVisible(false);
if(!mSubreddit.equals(Constants.FRONTPAGE_STRING)){
ArrayList<String> mSubredditsList = CacheInfo.getCachedSubredditList(getApplicationContext());
if(mSubredditsList != null && StringUtils.listContainsIgnoreCase(mSubredditsList, mSubreddit)){
menu.findItem(R.id.unsubscribe_menu_id).setVisible(true);
menu.findItem(R.id.subscribe_menu_id).setVisible(false);
}
else {
menu.findItem(R.id.subscribe_menu_id).setVisible(true);
menu.findItem(R.id.unsubscribe_menu_id).setVisible(false);
}
}
menu.findItem(R.id.inbox_menu_id).setVisible(true);
menu.findItem(R.id.user_profile_menu_id).setVisible(true);
menu.findItem(R.id.user_profile_menu_id).setTitle(
String.format(getResources().getString(R.string.user_profile), mSettings.getUsername())
);
menu.findItem(R.id.logout_menu_id).setVisible(true);
menu.findItem(R.id.logout_menu_id).setTitle(
String.format(getResources().getString(R.string.logout), mSettings.getUsername())
);
}
else {
menu.findItem(R.id.login_menu_id).setVisible(true);
menu.findItem(R.id.unsubscribe_menu_id).setVisible(false);
menu.findItem(R.id.subscribe_menu_id).setVisible(false);
menu.findItem(R.id.inbox_menu_id).setVisible(false);
menu.findItem(R.id.user_profile_menu_id).setVisible(false);
menu.findItem(R.id.logout_menu_id).setVisible(false);
}
// Theme: Light/Dark
src = Util.isLightTheme(mSettings.getTheme()) ?
menu.findItem(R.id.dark_menu_id) :
menu.findItem(R.id.light_menu_id);
dest = menu.findItem(R.id.light_dark_menu_id);
dest.setTitle(src.getTitle());
// Sort
if (Constants.ThreadsSort.SORT_BY_HOT_URL.equals(mSortByUrl))
src = menu.findItem(R.id.sort_by_hot_menu_id);
else if (Constants.ThreadsSort.SORT_BY_NEW_URL.equals(mSortByUrl))
src = menu.findItem(R.id.sort_by_new_menu_id);
else if (Constants.ThreadsSort.SORT_BY_CONTROVERSIAL_URL.equals(mSortByUrl))
src = menu.findItem(R.id.sort_by_controversial_menu_id);
else if (Constants.ThreadsSort.SORT_BY_TOP_URL.equals(mSortByUrl))
src = menu.findItem(R.id.sort_by_top_menu_id);
dest = menu.findItem(R.id.sort_by_menu_id);
dest.setTitle(src.getTitle());
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (!mCanChord) {
// The user has already fired a shortcut with this hold down of the
// menu key.
return false;
}
switch (item.getItemId()) {
case R.id.pick_subreddit_menu_id:
Intent pickSubredditIntent = new Intent(getApplicationContext(), PickSubredditActivity.class);
startActivityForResult(pickSubredditIntent, Constants.ACTIVITY_PICK_SUBREDDIT);
break;
case R.id.login_menu_id:
showDialog(Constants.DIALOG_LOGIN);
break;
case R.id.logout_menu_id:
if (mSettings.isConfirmQuitOrLogout()) {
// Ask the user if they want to logout
new AlertDialog.Builder(new ContextThemeWrapper(this, mSettings.getDialogTheme()))
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.confirm_logout_title)
.setMessage(R.string.confirm_logout)
.setPositiveButton(R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
ThreadsListActivity.this.logout();
}
}
)
.setNegativeButton(R.string.no, null)
.show();
} else {
logout();
}
break;
case R.id.refresh_menu_id:
CacheInfo.invalidateCachedSubreddit(getApplicationContext());
new MyDownloadThreadsTask(mSubreddit).execute();
break;
case R.id.submit_link_menu_id:
Intent submitLinkIntent = new Intent(getApplicationContext(), SubmitLinkActivity.class);
submitLinkIntent.setData(Util.createSubmitUri(mSubreddit));
startActivity(submitLinkIntent);
break;
case R.id.sort_by_menu_id:
showDialog(Constants.DIALOG_SORT_BY);
break;
case R.id.open_browser_menu_id:
String url;
if (mSubreddit.equals(Constants.FRONTPAGE_STRING))
url = Constants.REDDIT_BASE_URL;
else
url = new StringBuilder(Constants.REDDIT_BASE_URL + "/r/").append(mSubreddit).toString();
Common.launchBrowser(this, url, null, false, true, true, false);
break;
case R.id.light_dark_menu_id:
mSettings.setTheme(Util.getInvertedTheme(mSettings.getTheme()));
relaunchActivity();
break;
case R.id.inbox_menu_id:
Intent inboxIntent = new Intent(getApplicationContext(), InboxActivity.class);
startActivity(inboxIntent);
break;
case R.id.user_profile_menu_id:
Intent profileIntent = new Intent(getApplicationContext(), ProfileActivity.class);
startActivity(profileIntent);
break;
case R.id.preferences_menu_id:
Intent prefsIntent = new Intent(getApplicationContext(), RedditPreferencesPage.class);
startActivity(prefsIntent);
break;
case R.id.subscribe_menu_id:
CacheInfo.invalidateCachedSubreddit(getApplicationContext());
new SubscribeTask(mSubreddit, getApplicationContext(), mSettings).execute();
break;
case R.id.unsubscribe_menu_id:
CacheInfo.invalidateCachedSubreddit(getApplicationContext());
new UnsubscribeTask(mSubreddit, getApplicationContext(), mSettings).execute();
break;
case android.R.id.home:
Common.goHome(this);
break;
default:
throw new IllegalArgumentException("Unexpected action value "+item.getItemId());
}
return true;
}
private void logout() {
Common.doLogout(mSettings, mClient, getApplicationContext());
Toast.makeText(this, "You have been logged out.", Toast.LENGTH_SHORT)
.show();
new MyDownloadThreadsTask(mSubreddit).execute();
}
@Override
protected Dialog onCreateDialog(int id) {
Dialog dialog;
ProgressDialog pdialog;
AlertDialog.Builder builder;
switch (id) {
case Constants.DIALOG_LOGIN:
dialog = new LoginDialog(this, mSettings, false) {
public void onLoginChosen(String user, String password) {
removeDialog(Constants.DIALOG_LOGIN);
new MyLoginTask(user, password).execute();
}
};
break;
case Constants.DIALOG_THREAD_CLICK:
dialog = new ThreadClickDialog(this, mSettings);
break;
case Constants.DIALOG_SORT_BY:
builder = new AlertDialog.Builder(new ContextThemeWrapper(this, mSettings.getDialogTheme()));
builder.setTitle("Sort by:");
builder.setSingleChoiceItems(Constants.ThreadsSort.SORT_BY_CHOICES,
getSelectedSortBy(), sortByOnClickListener);
dialog = builder.create();
break;
case Constants.DIALOG_SORT_BY_NEW:
builder = new AlertDialog.Builder(new ContextThemeWrapper(this, mSettings.getDialogTheme()));
builder.setTitle("what's new");
builder.setSingleChoiceItems(Constants.ThreadsSort.SORT_BY_NEW_CHOICES,
getSelectedSortByNew(), sortByNewOnClickListener);
dialog = builder.create();
break;
case Constants.DIALOG_SORT_BY_CONTROVERSIAL:
builder = new AlertDialog.Builder(new ContextThemeWrapper(this, mSettings.getDialogTheme()));
builder.setTitle("most controversial");
builder.setSingleChoiceItems(Constants.ThreadsSort.SORT_BY_CONTROVERSIAL_CHOICES,
getSelectedSortByControversial(), sortByControversialOnClickListener);
dialog = builder.create();
break;
case Constants.DIALOG_SORT_BY_TOP:
builder = new AlertDialog.Builder(new ContextThemeWrapper(this, mSettings.getDialogTheme()));
builder.setTitle("top scoring");
builder.setSingleChoiceItems(Constants.ThreadsSort.SORT_BY_TOP_CHOICES,
getSelectedSortByTop(), sortByTopOnClickListener);
dialog = builder.create();
break;
// "Please wait"
case Constants.DIALOG_LOGGING_IN:
pdialog = new ProgressDialog(new ContextThemeWrapper(this, mSettings.getDialogTheme()));
pdialog.setMessage("Logging in...");
pdialog.setIndeterminate(true);
pdialog.setCancelable(true);
dialog = pdialog;
break;
default:
throw new IllegalArgumentException("Unexpected dialog id "+id);
}
return dialog;
}
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
switch (id) {
case Constants.DIALOG_LOGIN:
if (mSettings.getUsername() != null) {
final TextView loginUsernameInput = (TextView) dialog.findViewById(R.id.login_username_input);
loginUsernameInput.setText(mSettings.getUsername());
}
final TextView loginPasswordInput = (TextView) dialog.findViewById(R.id.login_password_input);
loginPasswordInput.setText("");
break;
case Constants.DIALOG_THREAD_CLICK:
if (mVoteTargetThing == null)
break;
fillThreadClickDialog(dialog, mVoteTargetThing, mSettings, mThreadClickDialogOnClickListenerFactory);
break;
case Constants.DIALOG_SORT_BY:
((AlertDialog) dialog).getListView().setItemChecked(getSelectedSortBy(), true);
break;
case Constants.DIALOG_SORT_BY_NEW:
((AlertDialog) dialog).getListView().setItemChecked(getSelectedSortByNew(), true);
break;
case Constants.DIALOG_SORT_BY_CONTROVERSIAL:
((AlertDialog) dialog).getListView().setItemChecked(getSelectedSortByControversial(), true);
break;
case Constants.DIALOG_SORT_BY_TOP:
((AlertDialog) dialog).getListView().setItemChecked(getSelectedSortByTop(), true);
break;
default:
// No preparation based on app state is required.
break;
}
}
private int getSelectedSortBy() {
for (int i = 0; i < Constants.ThreadsSort.SORT_BY_URL_CHOICES.length; i++) {
if (Constants.ThreadsSort.SORT_BY_URL_CHOICES[i].equals(mSortByUrl)) {
return i;
}
}
return -1;
}
private int getSelectedSortByNew() {
for (int i = 0; i < Constants.ThreadsSort.SORT_BY_NEW_URL_CHOICES.length; i++) {
if (Constants.ThreadsSort.SORT_BY_NEW_URL_CHOICES[i].equals(mSortByUrlExtra)) {
return i;
}
}
return -1;
}
private int getSelectedSortByControversial() {
for (int i = 0; i < Constants.ThreadsSort.SORT_BY_CONTROVERSIAL_URL_CHOICES.length; i++) {
if (Constants.ThreadsSort.SORT_BY_CONTROVERSIAL_URL_CHOICES[i].equals(mSortByUrlExtra)) {
return i;
}
}
return -1;
}
private int getSelectedSortByTop() {
for (int i = 0; i < Constants.ThreadsSort.SORT_BY_TOP_URL_CHOICES.length; i++) {
if (Constants.ThreadsSort.SORT_BY_TOP_URL_CHOICES[i].equals(mSortByUrlExtra)) {
return i;
}
}
return -1;
}
private final OnClickListener downloadAfterOnClickListener = new OnClickListener() {
public void onClick(View v) {
new MyDownloadThreadsTask(mSubreddit, mAfter, null, mCount).execute();
}
};
private final OnClickListener downloadBeforeOnClickListener = new OnClickListener() {
public void onClick(View v) {
new MyDownloadThreadsTask(mSubreddit, null, mBefore, mCount).execute();
}
};
private final DialogInterface.OnClickListener sortByOnClickListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
dialog.dismiss();
String itemString = Constants.ThreadsSort.SORT_BY_CHOICES[item];
if (Constants.ThreadsSort.SORT_BY_HOT.equals(itemString)) {
mSortByUrl = Constants.ThreadsSort.SORT_BY_HOT_URL;
mSortByUrlExtra = "";
new MyDownloadThreadsTask(mSubreddit).execute();
} else if (Constants.ThreadsSort.SORT_BY_NEW.equals(itemString)) {
showDialog(Constants.DIALOG_SORT_BY_NEW);
} else if (Constants.ThreadsSort.SORT_BY_CONTROVERSIAL.equals(itemString)) {
showDialog(Constants.DIALOG_SORT_BY_CONTROVERSIAL);
} else if (Constants.ThreadsSort.SORT_BY_TOP.equals(itemString)) {
showDialog(Constants.DIALOG_SORT_BY_TOP);
}
}
};
private final DialogInterface.OnClickListener sortByNewOnClickListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
dialog.dismiss();
mSortByUrl = Constants.ThreadsSort.SORT_BY_NEW_URL;
mSortByUrlExtra = Constants.ThreadsSort.SORT_BY_NEW_URL_CHOICES[item];
new MyDownloadThreadsTask(mSubreddit).execute();
}
};
private final DialogInterface.OnClickListener sortByControversialOnClickListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
dialog.dismiss();
mSortByUrl = Constants.ThreadsSort.SORT_BY_CONTROVERSIAL_URL;
mSortByUrlExtra = Constants.ThreadsSort.SORT_BY_CONTROVERSIAL_URL_CHOICES[item];
new MyDownloadThreadsTask(mSubreddit).execute();
}
};
private final DialogInterface.OnClickListener sortByTopOnClickListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
dialog.dismiss();
mSortByUrl = Constants.ThreadsSort.SORT_BY_TOP_URL;
mSortByUrlExtra = Constants.ThreadsSort.SORT_BY_TOP_URL_CHOICES[item];
new MyDownloadThreadsTask(mSubreddit).execute();
}
};
private final ThumbnailOnClickListenerFactory mThumbnailOnClickListenerFactory
= new ThumbnailOnClickListenerFactory() {
@Override
public OnClickListener getThumbnailOnClickListener(final ThingInfo threadThingInfo, final Activity activity) {
return new OnClickListener() {
public void onClick(View v) {
mJumpToThreadId = threadThingInfo.getId();
setLinkClicked(threadThingInfo);
Common.launchBrowser(
activity,
threadThingInfo.getUrl(),
Util.createThreadUri(threadThingInfo).toString(),
false,
false,
mSettings.isUseExternalBrowser(),
mSettings.isSaveHistory()
);
}
};
}
};
private final ThreadClickDialogOnClickListenerFactory mThreadClickDialogOnClickListenerFactory
= new ThreadClickDialogOnClickListenerFactory() {
@Override
public OnClickListener getLoginOnClickListener() {
return new OnClickListener() {
public void onClick(View v) {
removeDialog(Constants.DIALOG_THREAD_CLICK);
showDialog(Constants.DIALOG_LOGIN);
}
};
}
@Override
public OnClickListener getLinkOnClickListener(final ThingInfo thingInfo, final boolean useExternalBrowser) {
return new OnClickListener() {
public void onClick(View v) {
removeDialog(Constants.DIALOG_THREAD_CLICK);
setLinkClicked(thingInfo);
Common.launchBrowser(ThreadsListActivity.this, thingInfo.getUrl(),
Util.createThreadUri(thingInfo).toString(),
false, false, useExternalBrowser, mSettings.isSaveHistory());
}
};
}
@Override
public OnClickListener getCommentsOnClickListener(final ThingInfo thingInfo) {
return new OnClickListener() {
public void onClick(View v) {
removeDialog(Constants.DIALOG_THREAD_CLICK);
CacheInfo.invalidateCachedThread(ThreadsListActivity.this);
// Launch an Intent for CommentsListActivity
Intent i = new Intent(ThreadsListActivity.this, CommentsListActivity.class);
i.setData(Util.createThreadUri(thingInfo));
i.putExtra(Constants.EXTRA_SUBREDDIT, thingInfo.getSubreddit());
i.putExtra(Constants.EXTRA_TITLE, thingInfo.getTitle());
i.putExtra(Constants.EXTRA_NUM_COMMENTS, Integer.valueOf(thingInfo.getNum_comments()));
startActivity(i);
}
};
}
@Override
public CompoundButton.OnCheckedChangeListener getVoteUpOnCheckedChangeListener(final ThingInfo thingInfo) {
return new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
removeDialog(Constants.DIALOG_THREAD_CLICK);
if (isChecked) {
new MyVoteTask(thingInfo, 1, thingInfo.getSubreddit()).execute();
} else {
new MyVoteTask(thingInfo, 0, thingInfo.getSubreddit()).execute();
}
}
};
}
@Override
public CompoundButton.OnCheckedChangeListener getVoteDownOnCheckedChangeListener(final ThingInfo thingInfo) {
return new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
removeDialog(Constants.DIALOG_THREAD_CLICK);
if (isChecked) {
new MyVoteTask(thingInfo, -1, thingInfo.getSubreddit()).execute();
} else {
new MyVoteTask(thingInfo, 0, thingInfo.getSubreddit()).execute();
}
}
};
}
};
private void setLinkClicked(ThingInfo threadThingInfo) {
threadThingInfo.setClicked(true);
mThreadsAdapter.notifyDataSetChanged();
}
@Override
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
state.putString(Constants.SUBREDDIT_KEY, mSubreddit);
state.putString(Constants.ThreadsSort.SORT_BY_KEY, mSortByUrl);
state.putString(Constants.JUMP_TO_THREAD_ID_KEY, mJumpToThreadId);
state.putString(Constants.AFTER_KEY, mAfter);
state.putString(Constants.BEFORE_KEY, mBefore);
state.putInt(Constants.THREAD_COUNT_KEY, mCount);
state.putString(Constants.LAST_AFTER_KEY, mLastAfter);
state.putString(Constants.LAST_BEFORE_KEY, mLastBefore);
state.putInt(Constants.THREAD_LAST_COUNT_KEY, mLastCount);
state.putParcelable(Constants.VOTE_TARGET_THING_INFO_KEY, mVoteTargetThing);
}
/**
* Called to "thaw" re-animate the app from a previous onSaveInstanceState().
*
* @see android.app.Activity#onRestoreInstanceState
*/
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
final int[] myDialogs = {
Constants.DIALOG_LOGGING_IN,
Constants.DIALOG_LOGIN,
Constants.DIALOG_SORT_BY,
Constants.DIALOG_SORT_BY_CONTROVERSIAL,
Constants.DIALOG_SORT_BY_NEW,
Constants.DIALOG_SORT_BY_TOP,
Constants.DIALOG_THREAD_CLICK,
};
for (int dialog : myDialogs) {
try {
removeDialog(dialog);
} catch (IllegalArgumentException e) {
// Ignore.
}
}
}
}