package org.goodev.discourse.ui;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.TextView;
import com.manuelpeinado.refreshactionitem.ProgressIndicatorType;
import com.manuelpeinado.refreshactionitem.RefreshActionItem;
import com.manuelpeinado.refreshactionitem.RefreshActionItem.RefreshActionListener;
import org.goodev.discourse.ActivityUtils;
import org.goodev.discourse.App;
import org.goodev.discourse.R;
import org.goodev.discourse.api.Api;
import org.goodev.discourse.api.LatestTopics;
import org.goodev.discourse.api.LatestTopics.TopicList;
import org.goodev.discourse.api.StarTask;
import org.goodev.discourse.api.data.Topic;
import org.goodev.discourse.utils.ImageLoader;
import org.goodev.discourse.utils.Utils;
public class TopicsListFragment extends ListFragment implements OnScrollListener, LoaderCallbacks<LatestTopics>, RefreshActionListener {
public static final String EXTRA_ADD_VERTICAL_MARGINS = "extra_ADD_VERTICAL_MARGINS";
private static final String STATE_POSITION = "extra_postion";
private static final String STATE_TOP = "extra_top";
private static final int TOPICS_LOADER_ID = 1;
private static final int HOLDER_TAG_KEY = 2;
private final TopicAdapter mAdapter = new TopicAdapter();
protected String mSiteUrl;
private LatestTopics mLatestTopics = new LatestTopics();
private int mListViewStatePosition;
private int mListViewStateTop;
private ImageLoader mImageLoader;
private RefreshActionItem mRefreshActionItem;
private StarTask mStarTask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mImageLoader = App.getImageLoader(getActivity(), getResources());
if (savedInstanceState != null) {
mSiteUrl = savedInstanceState.getString(Utils.EXTRA_URL);
} else {
Bundle arg = getArguments();
if (arg != null) {
mSiteUrl = arg.getString(Utils.EXTRA_URL);
}
}
setHasOptionsMenu(true);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// onCreate 在该函数后调用
}
public CharSequence getTitle() {
return getResources().getString(R.string.title_latest);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
getActivity().setTitle(getTitle());
if (savedInstanceState != null) {
mListViewStatePosition = savedInstanceState.getInt(STATE_POSITION, -1);
mListViewStateTop = savedInstanceState.getInt(STATE_TOP, 0);
} else {
mListViewStatePosition = -1;
mListViewStateTop = 0;
}
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_topics_list, menu);
MenuItem item = menu.findItem(R.id.action_refresh);
mRefreshActionItem = (RefreshActionItem) item.getActionView();
mRefreshActionItem.setMenuItem(item);
mRefreshActionItem.setProgressIndicatorType(ProgressIndicatorType.INDETERMINATE);
// mRefreshActionItem.setMax(100);
mRefreshActionItem.setRefreshActionListener(this);
if (isStreamLoading()) {
mRefreshActionItem.showProgress(true);
}
}
@Override
public void onRefreshButtonClick(RefreshActionItem sender) {
mListViewStatePosition = -1;
refresh();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// case R.id.action_refresh:
// return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText(getString(R.string.empty_topics));
// In support library r8, calling initLoader for a fragment in a FragmentPagerAdapter
// in the fragment's onCreate may cause the same LoaderManager to be dealt to multiple
// fragments because their mIndex is -1 (haven't been added to the activity yet). Thus,
// we do this in onActivityCreated.
getLoaderManager().initLoader(TOPICS_LOADER_ID, null, this);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final ListView listView = getListView();
if (!Utils.isTablet(getActivity())) {
// TODO ...
// view.setBackgroundColor(getResources().getColor(R.color.plus_stream_spacer_color));
}
if (getArguments() != null && getArguments().getBoolean(EXTRA_ADD_VERTICAL_MARGINS, false)) {
int verticalMargin = getResources().getDimensionPixelSize(R.dimen.topics_list_padding_vertical);
if (verticalMargin > 0) {
listView.setClipToPadding(false);
listView.setPadding(0, verticalMargin, 0, verticalMargin);
}
}
listView.setOnScrollListener(this);
listView.setDrawSelectorOnTop(true);
// listView.setDivider(getResources().getDrawable(android.R.color.transparent));
// listView.setDividerHeight(getResources()
// .getDimensionPixelSize(R.dimen.page_margin_width));
TypedValue v = new TypedValue();
getActivity().getTheme().resolveAttribute(R.attr.clickableItemBackground, v, true);
listView.setSelector(v.resourceId);
setListAdapter(mAdapter);
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(Utils.EXTRA_URL, mSiteUrl);
if (isAdded()) {
View v = getListView().getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
outState.putInt(STATE_POSITION, getListView().getFirstVisiblePosition());
outState.putInt(STATE_TOP, top);
}
super.onSaveInstanceState(outState);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// Pause disk cache access to ensure smoother scrolling
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
mImageLoader.stopProcessingQueue();
} else {
mImageLoader.startProcessingQueue();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (!isStreamLoading() && streamHasMoreResults() && visibleItemCount != 0 && firstVisibleItem + visibleItemCount >= totalItemCount - 1) {
loadMoreResults();
}
}
public void refresh() {
if (mRefreshActionItem != null)
mRefreshActionItem.showProgress(true);
refresh(false);
}
public void refresh(boolean forceRefresh) {
if (isStreamLoading() && !forceRefresh) {
return;
}
// clear current items
mLatestTopics.clear();
mAdapter.notifyDataSetInvalidated();
if (isAdded()) {
Loader loader = getLoaderManager().getLoader(TOPICS_LOADER_ID);
((LatestTopicsLoader) loader).init(mSiteUrl, getInitUrl());
}
loadMoreResults();
}
public void loadMoreResults() {
if (isAdded()) {
Loader loader = getLoaderManager().getLoader(TOPICS_LOADER_ID);
if (loader != null) {
loader.forceLoad();
}
}
}
@Override
public Loader<LatestTopics> onCreateLoader(int id, Bundle args) {
return new LatestTopicsLoader(getActivity(), mSiteUrl, getInitUrl());
}
@Override
public void onLoadFinished(Loader<LatestTopics> loader, LatestTopics data) {
if (mRefreshActionItem != null) {
mRefreshActionItem.showProgress(false);
}
if (data != null) {
mLatestTopics = data;
}
mAdapter.notifyDataSetChanged();
if (mListViewStatePosition != -1 && isAdded()) {
getListView().setSelectionFromTop(mListViewStatePosition, mListViewStateTop);
mListViewStatePosition = -1;
}
}
@Override
public void onLoaderReset(Loader<LatestTopics> loader) {
}
private boolean isStreamLoading() {
if (isAdded()) {
final Loader loader = getLoaderManager().getLoader(TOPICS_LOADER_ID);
if (loader != null) {
return ((LatestTopicsLoader) loader).isLoading();
}
}
return true;
}
private boolean streamHasMoreResults() {
if (isAdded()) {
final Loader loader = getLoaderManager().getLoader(TOPICS_LOADER_ID);
if (loader != null) {
return ((LatestTopicsLoader) loader).hasMoreResults();
}
}
return false;
}
private boolean streamHasError() {
if (isAdded()) {
final Loader loader = getLoaderManager().getLoader(TOPICS_LOADER_ID);
if (loader != null) {
return ((LatestTopicsLoader) loader).hasError();
}
}
return false;
}
public String getInitUrl() {
return mSiteUrl + Api.LATEST;
}
public String getPopularUrl() {
return mSiteUrl + Api.POPULAR;
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Topic t = (Topic) l.getItemAtPosition(position);
if (t == null) {
return;
}
ActivityUtils.startTopicActivity(getActivity(), t.slug, t.id);
}
private static class LatestTopicsLoader extends AsyncTaskLoader<LatestTopics> {
LatestTopics mLatestTopics;
private boolean mIsLoading;
private boolean mHasError;
private String mSiteUrl;
private String mInitUrl;
public LatestTopicsLoader(Context context, String siteUrl, String initUrl) {
super(context);
init(siteUrl, initUrl);
}
private void init(String siteUrl, String initUrl) {
mHasError = false;
mIsLoading = true;
mSiteUrl = siteUrl;
mInitUrl = initUrl;
mLatestTopics = null;
}
@Override
public LatestTopics loadInBackground() {
mIsLoading = true;
if (mLatestTopics == null) {
mLatestTopics = new LatestTopics();
}
TopicList tl = mLatestTopics.getTopicList();
String url;
if (tl != null && !TextUtils.isEmpty(tl.more_topics_url)) {
url = Utils.removeLastSlash(mSiteUrl) + tl.more_topics_url;
} else {
url = mInitUrl;
}
boolean result = Api.getLatestTopics(mLatestTopics, url);
// if(!result && first) {
// result = Api.getLatestTopics(mLatestTopics, mSiteUrl+Api.POPULAR);
// }
mHasError = !result;
return result ? mLatestTopics : null;
}
@Override
public void deliverResult(LatestTopics data) {
mIsLoading = false;
if (data != null) {
if (mLatestTopics == null) {
mLatestTopics = data;
} else {
// mLatestTopics 在 loadInBackground 函数中重用了 里面已经设置好数据了, 不用调用addAll()函数了
// mLatestTopics.addAll(data);
}
}
if (isStarted()) {
// Need to return new ArrayList for some reason or onLoadFinished() is not called
super.deliverResult(mLatestTopics == null ? null : new LatestTopics(mLatestTopics));
}
}
@Override
protected void onStartLoading() {
if (mLatestTopics != null) {
// If we already have results and are starting up, deliver what we already have.
deliverResult(null);
} else {
forceLoad();
}
}
@Override
protected void onStopLoading() {
mIsLoading = false;
cancelLoad();
}
@Override
protected void onReset() {
super.onReset();
onStopLoading();
mLatestTopics = null;
}
public boolean isLoading() {
return mIsLoading;
}
public boolean hasMoreResults() {
return mLatestTopics != null && mLatestTopics.getTopicList() != null && mLatestTopics.getTopicList().more_topics_url != null;
}
public boolean hasError() {
return mHasError;
}
public void refresh() {
reset();
startLoading();
}
}
private class TopicAdapter extends BaseAdapter {
private static final int VIEW_TYPE_ITEM = 0;
private static final int VIEW_TYPE_LOADING = 1;
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int position) {
return getItemViewType(position) == VIEW_TYPE_ITEM;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public int getCount() {
return mLatestTopics.getTopicSize() + (
// show the status list row if...
((isStreamLoading() && mLatestTopics.getTopicSize() == 0) // ...this is the first load
|| streamHasMoreResults() // ...or there's another page
|| streamHasError()) // ...or there's an error
? 1 : 0);
}
@Override
public int getItemViewType(int position) {
return (position >= mLatestTopics.getTopicSize()) ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
}
@Override
public Object getItem(int position) {
return (getItemViewType(position) == VIEW_TYPE_ITEM) ? mLatestTopics.getTopic(position) : null;
}
@Override
public long getItemId(int position) {
// TODO: better unique ID heuristic
return (getItemViewType(position) == VIEW_TYPE_ITEM) ? mLatestTopics.getTopic(position).id.hashCode() : -1;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (getItemViewType(position) == VIEW_TYPE_LOADING) {
if (convertView == null) {
convertView = getLayoutInflater(null).inflate(R.layout.list_item_stream_status, parent, false);
}
if (streamHasError()) {
convertView.findViewById(android.R.id.progress).setVisibility(View.GONE);
((TextView) convertView.findViewById(android.R.id.text1)).setText(R.string.stream_error);
} else {
convertView.findViewById(android.R.id.progress).setVisibility(View.VISIBLE);
((TextView) convertView.findViewById(android.R.id.text1)).setText(R.string.loading);
}
return convertView;
} else {
Topic topic = mLatestTopics.getTopic(position);
if (convertView == null) {
convertView = getLayoutInflater(null).inflate(R.layout.topic_list_item, parent, false);
}
TopicRowViewBinder.bindItemView(convertView, topic, mImageLoader, false, mLatestTopics, mStarListener);
return convertView;
}
}
}
private final OnCheckedChangeListener mStarListener = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Topic t = (Topic) buttonView.getTag();
if (t == null || !App.isLogin()) {
return;
}
if (mStarTask != null && !mStarTask.isCancelled()) {
mStarTask.cancel(true);
}
mStarTask = new StarTask(buttonView, mStarListener, mSiteUrl, t.slug, t.id, isChecked);
mStarTask.execute();
}
};
}