package org.qii.weiciyuan.ui.basefragment;
import org.qii.weiciyuan.R;
import org.qii.weiciyuan.bean.ItemBean;
import org.qii.weiciyuan.bean.ListBean;
import org.qii.weiciyuan.bean.android.AsyncTaskLoaderResult;
import org.qii.weiciyuan.support.asyncdrawable.TimeLineBitmapDownloader;
import org.qii.weiciyuan.support.error.WeiboException;
import org.qii.weiciyuan.support.lib.ListViewMiddleMsgLoadingView;
import org.qii.weiciyuan.support.lib.LongClickableLinkMovementMethod;
import org.qii.weiciyuan.support.lib.TopTipBar;
import org.qii.weiciyuan.support.lib.VelocityListView;
import org.qii.weiciyuan.support.lib.pulltorefresh.PullToRefreshBase;
import org.qii.weiciyuan.support.lib.pulltorefresh.PullToRefreshListView;
import org.qii.weiciyuan.support.lib.pulltorefresh.extras.SoundPullEventListener;
import org.qii.weiciyuan.support.settinghelper.SettingUtility;
import org.qii.weiciyuan.support.utils.BundleArgsConstants;
import org.qii.weiciyuan.support.utils.Utility;
import org.qii.weiciyuan.support.utils.ViewUtility;
import org.qii.weiciyuan.ui.adapter.AbstractAppListAdapter;
import org.qii.weiciyuan.ui.interfaces.AbstractAppFragment;
import org.qii.weiciyuan.ui.loader.AbstractAsyncNetRequestTaskLoader;
import org.qii.weiciyuan.ui.loader.DummyLoader;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* User: qii
* Date: 12-8-27
* weiciyuan has two kinds of methods to send/receive network request/response asynchronously,
* one is setRetainInstance(true) + AsyncTask, the other is AsyncTaskLoader
* Because nested fragment(parent fragment has a viewpager, viewpager has many children fragments,
* these children fragments are called nested fragment) can't use setRetainInstance(true), at this
* moment
* you have to use AsyncTaskLoader to solve Android configuration change(for example: change screen
* orientation,
* change system language)
*/
public abstract class AbstractTimeLineFragment<T extends ListBean> extends AbstractAppFragment {
protected PullToRefreshListView pullToRefreshListView;
protected TextView empty;
protected ProgressBar progressBar;
protected TopTipBar newMsgTipBar;
protected View footerView;
protected BaseAdapter timeLineAdapter;
protected static final int DB_CACHE_LOADER_ID = 0;
protected static final int NEW_MSG_LOADER_ID = 1;
protected static final int MIDDLE_MSG_LOADER_ID = 2;
protected static final int OLD_MSG_LOADER_ID = 3;
protected ActionMode actionMode;
public static final int NO_SAVED_CURRENT_LOADING_MSG_VIEW_POSITION = -1;
protected int savedCurrentLoadingMsgViewPositon = NO_SAVED_CURRENT_LOADING_MSG_VIEW_POSITION;
public abstract T getList();
private int listViewScrollState = -1;
private boolean canLoadOldData = true;
public int getListViewScrollState() {
return listViewScrollState;
}
public PullToRefreshListView getPullToRefreshListView() {
return pullToRefreshListView;
}
public ListView getListView() {
return pullToRefreshListView.getRefreshableView();
}
public BaseAdapter getAdapter() {
return timeLineAdapter;
}
public TopTipBar getNewMsgTipBar() {
return newMsgTipBar;
}
protected void refreshLayout(T bean) {
if (bean != null && bean.getSize() > 0) {
// empty.setVisibility(View.INVISIBLE);
progressBar.setVisibility(View.INVISIBLE);
// listView.setVisibility(View.VISIBLE);
} else if (bean == null || bean.getSize() == 0) {
// empty.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.INVISIBLE);
// listView.setVisibility(View.VISIBLE);
} else if (bean.getSize() == bean.getTotal_number()) {
// empty.setVisibility(View.INVISIBLE);
progressBar.setVisibility(View.INVISIBLE);
// listView.setVisibility(View.VISIBLE);
}
}
protected abstract void listViewItemClick(AdapterView parent, View view, int position, long id);
public void loadNewMsg() {
canLoadOldData = true;
getLoaderManager().destroyLoader(MIDDLE_MSG_LOADER_ID);
getLoaderManager().destroyLoader(OLD_MSG_LOADER_ID);
dismissFooterView();
getLoaderManager().restartLoader(NEW_MSG_LOADER_ID, null, msgAsyncTaskLoaderCallback);
}
protected void loadOldMsg(View view) {
if (getLoaderManager().getLoader(OLD_MSG_LOADER_ID) != null || !canLoadOldData) {
return;
}
getLoaderManager().destroyLoader(NEW_MSG_LOADER_ID);
getPullToRefreshListView().onRefreshComplete();
getLoaderManager().destroyLoader(MIDDLE_MSG_LOADER_ID);
getLoaderManager().restartLoader(OLD_MSG_LOADER_ID, null, msgAsyncTaskLoaderCallback);
}
public void loadMiddleMsg(String beginId, String endId, int position) {
getLoaderManager().destroyLoader(NEW_MSG_LOADER_ID);
getLoaderManager().destroyLoader(OLD_MSG_LOADER_ID);
getPullToRefreshListView().onRefreshComplete();
dismissFooterView();
Bundle bundle = new Bundle();
bundle.putString("beginId", beginId);
bundle.putString("endId", endId);
bundle.putInt("position", position);
VelocityListView velocityListView = (VelocityListView) getListView();
bundle.putBoolean("towardsBottom",
velocityListView.getTowardsOrientation() == VelocityListView.TOWARDS_BOTTOM);
getLoaderManager().restartLoader(MIDDLE_MSG_LOADER_ID, bundle, msgAsyncTaskLoaderCallback);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("savedCurrentLoadingMsgViewPositon", savedCurrentLoadingMsgViewPositon);
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.listview_layout, container, false);
buildLayout(inflater, view);
return view;
}
protected void buildLayout(LayoutInflater inflater, View view) {
empty = ViewUtility.findViewById(view, R.id.empty);
progressBar = ViewUtility.findViewById(view, R.id.progressbar);
progressBar.setVisibility(View.GONE);
pullToRefreshListView = ViewUtility.findViewById(view, R.id.listView);
newMsgTipBar = ViewUtility.findViewById(view, R.id.tv_unread_new_message_count_tip_bar);
getListView().setHeaderDividersEnabled(false);
getListView().setScrollingCacheEnabled(false);
footerView = inflater.inflate(R.layout.listview_footer_layout, null);
getListView().addFooterView(footerView);
dismissFooterView();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
pullToRefreshListView.setOnRefreshListener(listViewOnRefreshListener);
pullToRefreshListView.setOnLastItemVisibleListener(listViewOnLastItemVisibleListener);
pullToRefreshListView.setOnScrollListener(listViewOnScrollListener);
pullToRefreshListView.setOnItemClickListener(listViewOnItemClickListener);
pullToRefreshListView.setOnPullEventListener(getPullEventListener());
buildListAdapter();
if (savedInstanceState != null) {
savedCurrentLoadingMsgViewPositon = savedInstanceState
.getInt("savedCurrentLoadingMsgViewPositon",
NO_SAVED_CURRENT_LOADING_MSG_VIEW_POSITION);
}
if (savedCurrentLoadingMsgViewPositon != NO_SAVED_CURRENT_LOADING_MSG_VIEW_POSITION
&& timeLineAdapter instanceof AbstractAppListAdapter) {
((AbstractAppListAdapter) timeLineAdapter)
.setSavedMiddleLoadingViewPosition(savedCurrentLoadingMsgViewPositon);
}
}
private PullToRefreshBase.OnLastItemVisibleListener listViewOnLastItemVisibleListener
= new PullToRefreshBase.OnLastItemVisibleListener() {
@Override
public void onLastItemVisible() {
if (getActivity() == null) {
return;
}
if (getLoaderManager().getLoader(OLD_MSG_LOADER_ID) != null) {
return;
}
loadOldMsg(null);
}
};
private PullToRefreshBase.OnRefreshListener<ListView> listViewOnRefreshListener
= new PullToRefreshBase.OnRefreshListener<ListView>() {
@Override
public void onRefresh(PullToRefreshBase<ListView> refreshView) {
if (getActivity() == null) {
return;
}
if (getLoaderManager().getLoader(NEW_MSG_LOADER_ID) != null) {
return;
}
loadNewMsg();
}
};
private SoundPullEventListener<ListView> getPullEventListener() {
SoundPullEventListener<ListView> listener = new SoundPullEventListener<ListView>(
getActivity());
if (SettingUtility.getEnableSound()) {
listener.addSoundEvent(PullToRefreshBase.State.RELEASE_TO_REFRESH, R.raw.psst1);
// listener.addSoundEvent(PullToRefreshBase.State.GIVE_UP, R.raw.psst2);
listener.addSoundEvent(PullToRefreshBase.State.RESET, R.raw.pop);
}
return listener;
}
private AdapterView.OnItemClickListener listViewOnItemClickListener
= new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (resetActionMode()) {
return;
}
getListView().clearChoices();
int headerViewsCount = getListView().getHeaderViewsCount();
if (isPositionBetweenHeaderViewAndFooterView(position)) {
int indexInDataSource = position - headerViewsCount;
ItemBean msg = getList().getItem(indexInDataSource);
if (!isMiddleUnloadMessage(msg)) {
listViewItemClick(parent, view, indexInDataSource, id);
} else {
String beginId = getList().getItem(indexInDataSource + 1).getId();
String endId = getList().getItem(indexInDataSource - 1).getId();
ListViewMiddleMsgLoadingView loadingView = (ListViewMiddleMsgLoadingView) view;
if (!((ListViewMiddleMsgLoadingView) view).isLoading()
&& savedCurrentLoadingMsgViewPositon
== NO_SAVED_CURRENT_LOADING_MSG_VIEW_POSITION) {
loadingView.load();
loadMiddleMsg(beginId, endId, indexInDataSource);
savedCurrentLoadingMsgViewPositon = indexInDataSource + headerViewsCount;
if (timeLineAdapter instanceof AbstractAppListAdapter) {
((AbstractAppListAdapter) timeLineAdapter)
.setSavedMiddleLoadingViewPosition(
savedCurrentLoadingMsgViewPositon);
}
}
}
} else if (isLastItem(position)) {
loadOldMsg(view);
}
}
boolean isLastItem(int position) {
return position - 1 >= getList().getSize();
}
boolean isMiddleUnloadMessage(ItemBean msg) {
return msg == null || msg.isMiddleUnreadItem();
}
boolean isPositionBetweenHeaderViewAndFooterView(int position) {
return position - getListView().getHeaderViewsCount() < getList().getSize()
&& position - getListView().getHeaderViewsCount() >= 0;
}
boolean resetActionMode() {
if (actionMode != null) {
getListView().clearChoices();
actionMode.finish();
actionMode = null;
return true;
} else {
return false;
}
}
};
private AbsListView.OnScrollListener listViewOnScrollListener
= new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
listViewScrollState = scrollState;
switch (scrollState) {
case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
if (!enableRefreshTime) {
enableRefreshTime = true;
getAdapter().notifyDataSetChanged();
}
onListViewScrollStop();
LongClickableLinkMovementMethod.getInstance().setLongClickable(true);
TimeLineBitmapDownloader.getInstance().setPauseDownloadWork(false);
TimeLineBitmapDownloader.getInstance().setPauseReadWork(false);
break;
case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
enableRefreshTime = false;
LongClickableLinkMovementMethod.getInstance().setLongClickable(false);
TimeLineBitmapDownloader.getInstance().setPauseDownloadWork(true);
TimeLineBitmapDownloader.getInstance().setPauseReadWork(true);
onListViewScrollStateFling();
break;
case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
enableRefreshTime = true;
LongClickableLinkMovementMethod.getInstance().setLongClickable(false);
TimeLineBitmapDownloader.getInstance().setPauseDownloadWork(true);
onListViewScrollStateTouchScroll();
break;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
onListViewScroll();
}
};
@Override
public void onPause() {
super.onPause();
TimeLineBitmapDownloader.getInstance().setPauseDownloadWork(false);
TimeLineBitmapDownloader.getInstance().setPauseReadWork(false);
}
protected void onListViewScrollStop() {
}
protected void onListViewScrollStateTouchScroll() {
}
protected void onListViewScrollStateFling() {
}
protected void onListViewScroll() {
if (hasActionMode()) {
int position = getListView().getCheckedItemPosition();
if (getListView().getFirstVisiblePosition() > position
|| getListView().getLastVisiblePosition() < position) {
clearActionMode();
}
}
if (allowLoadOldMsgBeforeReachListBottom() && getListView().getLastVisiblePosition() > 7
&& getListView().getLastVisiblePosition() > getList().getSize() - 3
&& getListView().getFirstVisiblePosition() != getListView().getHeaderViewsCount()) {
loadOldMsg(null);
}
}
protected boolean allowLoadOldMsgBeforeReachListBottom() {
return true;
}
protected void showFooterView() {
View view = footerView.findViewById(R.id.loading_progressbar);
view.setVisibility(View.VISIBLE);
view.setScaleX(1.0f);
view.setScaleY(1.0f);
view.setAlpha(1.0f);
footerView.findViewById(R.id.laod_failed).setVisibility(View.GONE);
}
protected void dismissFooterView() {
final View progressbar = footerView.findViewById(R.id.loading_progressbar);
progressbar.animate().scaleX(0).scaleY(0).alpha(0.5f).setDuration(300)
.withEndAction(new Runnable() {
@Override
public void run() {
progressbar.setVisibility(View.GONE);
}
});
footerView.findViewById(R.id.laod_failed).setVisibility(View.GONE);
}
protected void showErrorFooterView() {
View view = footerView.findViewById(R.id.loading_progressbar);
view.setVisibility(View.GONE);
TextView tv = ((TextView) footerView.findViewById(R.id.laod_failed));
tv.setVisibility(View.VISIBLE);
}
public void clearActionMode() {
if (actionMode != null) {
actionMode.finish();
actionMode = null;
}
if (pullToRefreshListView != null && getListView().getCheckedItemCount() > 0) {
getListView().clearChoices();
if (getAdapter() != null) {
getAdapter().notifyDataSetChanged();
}
}
}
public boolean clearActionModeIfOpen() {
boolean flag = false;
if (actionMode != null) {
actionMode.finish();
actionMode = null;
flag = true;
}
if (pullToRefreshListView != null && getListView().getCheckedItemCount() > 0) {
getListView().clearChoices();
if (getAdapter() != null) {
getAdapter().notifyDataSetChanged();
}
}
return flag;
}
protected abstract void buildListAdapter();
protected boolean allowRefresh() {
boolean isNewMsgLoaderLoading = getLoaderManager().getLoader(NEW_MSG_LOADER_ID) != null;
return getPullToRefreshListView().getVisibility() == View.VISIBLE && !isNewMsgLoaderLoading;
}
@Override
public void onResume() {
super.onResume();
getListView().setFastScrollEnabled(SettingUtility.allowFastScroll());
getAdapter().notifyDataSetChanged();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Loader<T> loader = getLoaderManager().getLoader(NEW_MSG_LOADER_ID);
if (loader != null) {
getLoaderManager().initLoader(NEW_MSG_LOADER_ID, null, msgAsyncTaskLoaderCallback);
}
loader = getLoaderManager().getLoader(MIDDLE_MSG_LOADER_ID);
if (loader != null) {
getLoaderManager().initLoader(MIDDLE_MSG_LOADER_ID, null, msgAsyncTaskLoaderCallback);
}
loader = getLoaderManager().getLoader(OLD_MSG_LOADER_ID);
if (loader != null) {
getLoaderManager().initLoader(OLD_MSG_LOADER_ID, null, msgAsyncTaskLoaderCallback);
}
}
public void setActionMode(ActionMode mActionMode) {
this.actionMode = mActionMode;
}
public boolean hasActionMode() {
return actionMode != null;
}
protected void showListView() {
progressBar.setVisibility(View.INVISIBLE);
}
private volatile boolean enableRefreshTime = true;
public boolean isListViewFling() {
return !enableRefreshTime;
}
protected abstract void newMsgLoaderSuccessCallback(T newValue, Bundle loaderArgs);
protected abstract void oldMsgLoaderSuccessCallback(T newValue);
protected void middleMsgLoaderSuccessCallback(int position, T newValue, boolean towardsBottom) {
if (newValue == null) {
return;
}
if (newValue.getSize() == 0 || newValue.getSize() == 1) {
getList().getItemList().remove(position);
getAdapter().notifyDataSetChanged();
return;
}
}
protected void newMsgLoaderFailedCallback(WeiboException exception) {
}
protected void oldMsgLoaderFailedCallback(WeiboException exception) {
}
private Loader<AsyncTaskLoaderResult<T>> createNewMsgLoader(int id, Bundle args) {
Loader<AsyncTaskLoaderResult<T>> loader = onCreateNewMsgLoader(id, args);
if (loader == null) {
loader = new DummyLoader<T>(getActivity());
}
if (loader instanceof AbstractAsyncNetRequestTaskLoader) {
((AbstractAsyncNetRequestTaskLoader) loader).setArgs(args);
}
return loader;
}
private Loader<AsyncTaskLoaderResult<T>> createMiddleMsgLoader(int id, Bundle args,
String middleBeginId, String middleEndId, String middleEndTag, int middlePosition) {
Loader<AsyncTaskLoaderResult<T>> loader = onCreateMiddleMsgLoader(id, args, middleBeginId,
middleEndId, middleEndTag, middlePosition);
if (loader == null) {
loader = new DummyLoader<T>(getActivity());
}
return loader;
}
private Loader<AsyncTaskLoaderResult<T>> createOldMsgLoader(int id, Bundle args) {
Loader<AsyncTaskLoaderResult<T>> loader = onCreateOldMsgLoader(id, args);
if (loader == null) {
loader = new DummyLoader<T>(getActivity());
}
return loader;
}
protected Loader<AsyncTaskLoaderResult<T>> onCreateNewMsgLoader(int id, Bundle args) {
return null;
}
protected Loader<AsyncTaskLoaderResult<T>> onCreateMiddleMsgLoader(int id, Bundle args,
String middleBeginId, String middleEndId, String middleEndTag, int middlePosition) {
return null;
}
protected Loader<AsyncTaskLoaderResult<T>> onCreateOldMsgLoader(int id, Bundle args) {
return null;
}
protected LoaderManager.LoaderCallbacks<AsyncTaskLoaderResult<T>> msgAsyncTaskLoaderCallback
= new LoaderManager.LoaderCallbacks<AsyncTaskLoaderResult<T>>() {
private String middleBeginId = "";
private String middleEndId = "";
private int middlePosition = -1;
private boolean towardsBottom = false;
@Override
public Loader<AsyncTaskLoaderResult<T>> onCreateLoader(int id, Bundle args) {
clearActionMode();
showListView();
switch (id) {
case NEW_MSG_LOADER_ID:
if (args == null || args.getBoolean(BundleArgsConstants.SCROLL_TO_TOP)) {
Utility.stopListViewScrollingAndScrollToTop(getListView());
}
return createNewMsgLoader(id, args);
case MIDDLE_MSG_LOADER_ID:
middleBeginId = args.getString("beginId");
middleEndId = args.getString("endId");
middlePosition = args.getInt("position");
towardsBottom = args.getBoolean("towardsBottom");
return createMiddleMsgLoader(id, args, middleBeginId, middleEndId, null,
middlePosition);
case OLD_MSG_LOADER_ID:
showFooterView();
return createOldMsgLoader(id, args);
}
return null;
}
@Override
public void onLoadFinished(Loader<AsyncTaskLoaderResult<T>> loader,
AsyncTaskLoaderResult<T> result) {
T data = result != null ? result.data : null;
WeiboException exception = result != null ? result.exception : null;
Bundle args = result != null ? result.args : null;
switch (loader.getId()) {
case NEW_MSG_LOADER_ID:
getPullToRefreshListView().onRefreshComplete();
refreshLayout(getList());
if (Utility.isAllNotNull(exception)) {
newMsgTipBar.setError(exception.getError());
newMsgLoaderFailedCallback(exception);
} else {
newMsgLoaderSuccessCallback(data, args);
}
break;
case MIDDLE_MSG_LOADER_ID:
if (exception != null) {
View view = Utility.getListViewItemViewFromPosition(getListView(),
savedCurrentLoadingMsgViewPositon);
ListViewMiddleMsgLoadingView loadingView
= (ListViewMiddleMsgLoadingView) view;
if (loadingView != null) {
loadingView.setErrorMessage(exception.getError());
}
} else {
middleMsgLoaderSuccessCallback(middlePosition, data, towardsBottom);
// getAdapter().notifyDataSetChanged();
}
savedCurrentLoadingMsgViewPositon = NO_SAVED_CURRENT_LOADING_MSG_VIEW_POSITION;
if (timeLineAdapter instanceof AbstractAppListAdapter) {
((AbstractAppListAdapter) timeLineAdapter)
.setSavedMiddleLoadingViewPosition(
savedCurrentLoadingMsgViewPositon);
}
break;
case OLD_MSG_LOADER_ID:
refreshLayout(getList());
if (exception != null) {
showErrorFooterView();
oldMsgLoaderFailedCallback(exception);
} else if (data != null) {
canLoadOldData = data.getSize() > 1;
oldMsgLoaderSuccessCallback(data);
getAdapter().notifyDataSetChanged();
dismissFooterView();
} else {
canLoadOldData = false;
dismissFooterView();
}
break;
}
getLoaderManager().destroyLoader(loader.getId());
}
@Override
public void onLoaderReset(Loader<AsyncTaskLoaderResult<T>> loader) {
}
};
}