package org.goodev.discourse.ui; 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.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; 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.data.UserActions; import org.goodev.discourse.utils.HttpRequest; import org.goodev.discourse.utils.HttpRequest.HttpRequestException; import org.goodev.discourse.utils.ImageLoader; import org.goodev.discourse.utils.L; import org.goodev.discourse.utils.Utils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.List; public class UserActionsFragment extends ListFragment implements LoaderCallbacks<List<UserActions>>, OnScrollListener { // 1:"Likes Given",2:"Likes Received", // 3:"Bookmarks",4:"Topics",5:"Posts",6:"Replies", // 7:"Mentions",9:"Quotes", // 10:"Favorites",11:"Edits", // 12:"Sent Items",13:"Inbox"} public static final int TYPE_ALL = 0; public static final int TYPE_LIKES_GIVEN = 1; public static final int TYPE_LIKES_RECEIVED = 2; public static final int TYPE_BOOKMARK = 3; public static final int TYPE_TOPIC = 4; public static final int TYPE_POST = 5; public static final int TYPE_REPLY = 6; /** * 喜爱 */ public static final int TYPE_LIKES = 10; public static final int TYPE_EDIT = 11; /** * Mentions 提到我的 */ public static final int TYPE_MENTIONS = 7; /** * Quotes */ public static final int TYPE_QUOTES = 9; public static final int TYPE_SENT_ITEMSS = 12; public static final int TYPE_INBOX = 13; public static final int TYPE_ALL_PRIVATE_MSG = 20; public static final int TYPE_MIN_PRIVATE_MSG = 21; public static final int TYPE_UNREAD_PRIVATE_MSG = 22; public static final String REPLY_FILTER = "6,7,9"; public static final String EXTRA_ADD_VERTICAL_MARGINS = "extra.ADD_VERTICAL_MARGINS"; private static final int LOADER_ID = 11; private static final String STATE_POSITION = "position"; private static final String STATE_TOP = "top"; private final ActionsAdapter mAdapter = new ActionsAdapter(); private String mUsername; private int mType; private List<UserActions> mActions = new ArrayList<UserActions>(); private int mListViewStatePosition; private int mListViewStateTop; private ImageLoader mImageLoader; public static UserActionsFragment newInstance(String username, int type) { UserActionsFragment f = new UserActionsFragment(); Bundle args = new Bundle(); args.putString(Utils.EXTRA_NAME, username); args.putInt(Utils.EXTRA_TYPE, type); f.setArguments(args); return f; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); mImageLoader = App.getImageLoader(getActivity(), getResources()); mUsername = args.getString(Utils.EXTRA_NAME); mType = args.getInt(Utils.EXTRA_TYPE); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 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 onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); final ListView listView = getListView(); // TODO fix this // if (!UIUtils.isTablet(getActivity())) { // 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.plus_stream_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.darker_gray)); // listView.setDividerHeight(getResources() // .getDimensionPixelSize(R.dimen.divider_height)); TypedValue v = new TypedValue(); getActivity().getTheme().resolveAttribute(R.attr.clickableItemBackground, v, true); listView.setSelector(v.resourceId); setListAdapter(mAdapter); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // 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(LOADER_ID, null, this); } @Override public Loader<List<UserActions>> onCreateLoader(int id, Bundle args) { return new ActionsLoader(getActivity(), mUsername, mType); } @Override public void onLoadFinished(Loader<List<UserActions>> loader, List<UserActions> data) { L.i("data: " + (data == null ? null : data.size())); if (data != null) { mActions = data; } mAdapter.notifyDataSetChanged(); if (mListViewStatePosition != -1 && isAdded()) { getListView().setSelectionFromTop(mListViewStatePosition, mListViewStateTop); mListViewStatePosition = -1; } } @Override public void onLoaderReset(Loader<List<UserActions>> loader) { } @Override public void onSaveInstanceState(Bundle outState) { 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); } private boolean isStreamLoading() { if (isAdded()) { final Loader loader = getLoaderManager().getLoader(LOADER_ID); if (loader != null) { return ((ActionsLoader) loader).isLoading(); } } return true; } private boolean streamHasMoreResults() { if (isAdded()) { final Loader loader = getLoaderManager().getLoader(LOADER_ID); if (loader != null) { return ((ActionsLoader) loader).hasMoreResults(); } } return false; } private boolean streamHasError() { if (isAdded()) { final Loader loader = getLoaderManager().getLoader(LOADER_ID); if (loader != null) { return ((ActionsLoader) loader).hasError(); } } return false; } @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 loadMoreResults() { if (isAdded()) { Loader loader = getLoaderManager().getLoader(LOADER_ID); if (loader != null) { loader.forceLoad(); } } } @Override public void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); UserActions action = (UserActions) l.getItemAtPosition(position - l.getHeaderViewsCount()); if (action == null) { return; } String slug = action.slug; if (TextUtils.isEmpty(slug)) { slug = "topic"; } ActivityUtils.startTopicActivity(getActivity(), slug, action.topic_id); } private static class ActionsLoader extends AsyncTaskLoader<List<UserActions>> { public static final int NO_MORE_DATA = -1; List<UserActions> mActions; /** * 如果offset为-1 则说明没有下一页了 */ private long mOffset; private int mType; private String mUsername; private boolean mIsLoading; private boolean mHasError; public ActionsLoader(Context context, String name, int type) { super(context); init(name, type); } private void init(String name, int type) { mActions = null; mIsLoading = true; mHasError = false; mOffset = 0; mType = type; mUsername = name; } @Override public List<UserActions> loadInBackground() { try { mIsLoading = true; String url = getUrl(); L.i(url); HttpRequest hr = HttpRequest.get(url); HttpURLConnection conn = hr.getConnection(); if (App.isLogin()) { App.getCookieManager().setCookies(conn); } String body = hr.body(); L.i("%s", body); JSONObject obj = new JSONObject(body); if (obj.has(Api.K_user_actions)) { mHasError = false; JSONArray data = obj.getJSONArray(Api.K_user_actions); if (data.length() == 0) { mOffset = NO_MORE_DATA; return null; } List<UserActions> result = Api.getUserActions(data); mOffset += result.size(); return result; } } catch (HttpRequestException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } mHasError = true; return null; } private String getUrl() { if (mType == TYPE_ALL) { return App.getSiteUrl() + String.format(Api.GET_USER_ACTIONS, mOffset, mUsername); } else if (mType == TYPE_REPLY) { return App.getSiteUrl() + String.format(Api.GET_USER_ACTIONS_FILTER, mOffset, mUsername, REPLY_FILTER); } return App.getSiteUrl() + String.format(Api.GET_USER_ACTIONS_FILTER, mOffset, mUsername, mType + ""); } @Override public void deliverResult(List<UserActions> activities) { mIsLoading = false; if (activities != null) { if (mActions == null) { mActions = activities; } else { mActions.addAll(activities); } } if (isStarted()) { // Need to return new ArrayList for some reason or onLoadFinished() is not called super.deliverResult(mActions == null ? null : new ArrayList<UserActions>(mActions)); } } @Override protected void onStartLoading() { if (mActions != 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(); mActions = null; } public boolean isLoading() { return mIsLoading; } public boolean hasMoreResults() { return mOffset != NO_MORE_DATA; } public boolean hasError() { return mHasError; } public void refresh() { reset(); startLoading(); } } private class ActionsAdapter extends BaseAdapter { private static final int VIEW_TYPE_ACTIVITY = 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_ACTIVITY; } @Override public int getViewTypeCount() { return 2; } @Override public boolean hasStableIds() { return true; } @Override public int getCount() { return mActions.size() + ( // show the status list row if... ((isStreamLoading() && mActions.size() == 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 >= mActions.size()) ? VIEW_TYPE_LOADING : VIEW_TYPE_ACTIVITY; } @Override public Object getItem(int position) { return (getItemViewType(position) == VIEW_TYPE_ACTIVITY) ? mActions.get(position) : null; } @Override public long getItemId(int position) { // TODO: better unique ID heuristic return (getItemViewType(position) == VIEW_TYPE_ACTIVITY) ? mActions.get(position).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 { UserActions activity = (UserActions) getItem(position); if (convertView == null) { convertView = getLayoutInflater(null).inflate(R.layout.list_item_user_action, parent, false); } UserActionViewBinder.bindActivityView(convertView, activity, mImageLoader, false); return convertView; } } } }