package im.zico.wingtwitter.ui.fragment;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.AlertDialog;
import android.app.LoaderManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.TextView;
import android.widget.Toast;
import com.nineoldandroids.view.ViewPropertyAnimator;
import com.tjerkw.slideexpandable.library.AbstractSlideExpandableListAdapter;
import com.tjerkw.slideexpandable.library.ActionSlideExpandableListView;
import com.tjerkw.slideexpandable.library.SlideExpandableListAdapter;
import com.tjerkw.slideexpandable.library.WrapperListAdapterImpl;
import org.w3c.dom.Text;
import java.util.ArrayList;
import im.zico.wingtwitter.R;
import im.zico.wingtwitter.WingApp;
import im.zico.wingtwitter.adapter.TimeLineAdapter;
import im.zico.wingtwitter.dao.WingDataHelper;
import im.zico.wingtwitter.dao.WingStore;
import im.zico.wingtwitter.type.WingTweet;
import im.zico.wingtwitter.ui.TweetComposeActivity;
import im.zico.wingtwitter.ui.view.LoadingFooter;
import im.zico.wingtwitter.ui.view.TweetListView;
import im.zico.wingtwitter.utils.PreferencesManager;
import im.zico.wingtwitter.utils.TweetUtils;
import im.zico.wingtwitter.utils.WConfig;
import twitter4j.AsyncTwitter;
import twitter4j.ResponseList;
import twitter4j.Status;
import twitter4j.TwitterAdapter;
import twitter4j.TwitterException;
import twitter4j.TwitterListener;
import twitter4j.TwitterMethod;
/**
* Created by tinyao on 12/4/14.
*/
public abstract class BaseStatusesListFragment extends BaseFragment implements LoaderManager.LoaderCallbacks<Cursor>,
SwipeRefreshLayout.OnRefreshListener, TweetListView.ScrollDetectCallback {
private static final int TASK_TIMELINE = 1;
private static final int TASK_FAVORITE = 2;
private static final int TASK_UNFAVORITE = 3;
private static final int TASK_FAVORITE_TIMELINE = 4;
private static final int TASK_EXCPTION = -1;
public SwipeRefreshLayout mSwipeRefresh;
TweetListView mListView;
TimeLineAdapter mAdapter;
int mPageId = 0;
private AsyncTwitter mAsyncTwitter;
private LoadingFooter mLoadingFooter;
private WingDataHelper DBHelper;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_blank, container, false);
mListView = (TweetListView) rootView.findViewById(R.id.list);
mLoadingFooter = new LoadingFooter(getActivity());
mListView.addFooterView(mLoadingFooter.getView());
return rootView;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getAsyncTwitter().addListener(listener);
bindSwipeToRefresh((ViewGroup) view);
DBHelper = new WingDataHelper(getActivity());
mAdapter = new TimeLineAdapter(getActivity(), mListView);
mListView.setAdapter(mAdapter);
getLoaderManager().initLoader(0, null, this);
mListView.setScrollCallback(this);
mListView.getSlideAdapter().setItemExpandCollapseListener(new AbstractSlideExpandableListAdapter.OnItemExpandCollapseListener() {
@Override
public void onExpand(View itemView, int position) {
View childView = mListView.getChildAt(position - mListView.getFirstVisiblePosition());
childView.findViewById(R.id.main_card_content).setBackgroundResource(R.drawable.tweet_bg_dark);
((TextView) childView.findViewById(R.id.user_name)).setTextColor(Color.LTGRAY);
((TextView) childView.findViewById(R.id.tweet_content)).setTextColor(Color.LTGRAY);
Log.d("DEBUG", "onExpand");
}
@Override
public void onCollapse(View itemView, int position) {
View childView = mListView.getChildAt(position - mListView.getFirstVisiblePosition());
childView.findViewById(R.id.main_card_content).setBackgroundResource(R.drawable.flat_ripple_selector);
((TextView) childView.findViewById(R.id.user_name)).setTextColor(Color.BLACK);
((TextView) childView.findViewById(R.id.tweet_content)).setTextColor(Color.BLACK);
Log.d("DEBUG", "onCollapse");
}
});
mListView.setItemActionListener(new ActionSlideExpandableListView.OnActionClickListener() {
@Override
public void onClick(final View itemView, View clickedView, final int position) {
final WingTweet tweet = mAdapter.getItem(position);
switch (clickedView.getId()) {
case R.id.expand_action_reply:
Bundle bundle = new Bundle();
bundle.putString("user", "@" + tweet.screen_name);
bundle.putLong("inReplyId", tweet.tweet_id);
TweetComposeActivity.showDialog(getActivity(), bundle);
break;
case R.id.expand_action_share:
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT,
"@" + tweet.screen_name + ": " + tweet.content);
sendIntent.setType("text/plain");
startActivity(sendIntent);
break;
case R.id.expand_action_favorite:
WingTweet favTweet = mAdapter.getItem(position);
if (!favTweet.favorited) {
getAsyncTwitter().createFavorite(tweet.tweet_id);
} else {
if (getType() == WingStore.TYPE_FAVORITE) {
ViewPropertyAnimator.animate(itemView).setInterpolator(new AccelerateDecelerateInterpolator())
.setDuration(200)
.translationXBy(0-getResources().getDisplayMetrics().widthPixels)
.setListener(new com.nineoldandroids.animation.AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(com.nineoldandroids.animation.Animator animation) {
super.onAnimationEnd(animation);
getDBHelper().delete(mAdapter.getItem(position), WingStore.TYPE_FAVORITE);
getAsyncTwitter().destroyFavorite(tweet.tweet_id);
performDismiss(itemView);
}
});
} else {
getAsyncTwitter().destroyFavorite(tweet.tweet_id);
}
}
break;
case R.id.expand_action_retweet:
new AlertDialog.Builder(getActivity())
.setMessage("Retweet or Quote ?")
.setNeutralButton(R.string.cancel, null)
.setPositiveButton(R.string.retweet, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
getAsyncTwitter().retweetStatus(tweet.tweet_id);
}
})
.setNegativeButton(R.string.quote, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Bundle bnd = new Bundle();
bnd.putString("quote", "RT " + tweet.content);
TweetComposeActivity.showDialog(getActivity(), bnd);
}
}).create().show();
break;
case R.id.expand_action_copy:
ClipboardManager clipboard = (ClipboardManager)
getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("wing", tweet.content);
clipboard.setPrimaryClip(clip);
Toast.makeText(getActivity(), "Copyed to clipboard !", Toast.LENGTH_SHORT).show();
break;
case R.id.expand_action_delete:
new AlertDialog.Builder(getActivity())
.setTitle("Delete")
.setMessage("Do you want to delete this Tweet?")
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
getDBHelper().delete(tweet);
getAsyncTwitter().destroyStatus(tweet.tweet_id);
}
})
.setNegativeButton(R.string.no, null)
.create().show();
break;
case R.id.expand_action_filter:
// ToDo: Filter user or source
new AlertDialog.Builder(getActivity())
.setTitle("Add to filter")
.setMessage("User: " + tweet.user_name + "\nSoure: " + tweet.source)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setNegativeButton(R.string.no, null)
.create().show();
break;
}
mListView.collapse();
}
});
}
@Override
public void onScrollFooter() {
if (mAdapter.getCount() == 0){
return;
}
if (mLoadingFooter.getState() != LoadingFooter.State.Loading
&& mLoadingFooter.getState() != LoadingFooter.State.TheEnd) {
mLoadingFooter.setState(LoadingFooter.State.Loading);
loadNext();
}
}
@Override
public void onScrollDown() {
}
@Override
public void onScrollUp() {
}
public void scrollTop() {
mListView.smoothScrollToPositionFromTop(0, 0);
}
/**
* Bind a SwipeRefreshLayout to the contentView
* @param v
*/
protected void bindSwipeToRefresh(ViewGroup v) {
mSwipeRefresh = new SwipeRefreshLayout(getActivity());
// Move child to SwipeRefreshLayout, and add SwipeRefreshLayout to root view
v.removeViewInLayout(mListView);
v.addView(mSwipeRefresh, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mSwipeRefresh.addView(mListView, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mSwipeRefresh.setColorSchemeResources(
R.color.refresh_progress_1,
R.color.refresh_progress_2,
R.color.refresh_progress_3);
mSwipeRefresh.setOnRefreshListener(this);
}
public WingDataHelper getDBHelper() {
return DBHelper;
}
/**
* Impliment to provide the main status type in fragment,
* such as Tweet, Mentioned, Favorite
* @return
*/
public abstract int getType();
/**
* Create a AsyncTwitter Instance for each fragment instance
* @return AsyncTwitter
*/
public AsyncTwitter getAsyncTwitter(){
if (mAsyncTwitter == null) {
mAsyncTwitter = WingApp.newTwitterInstance();
}
return mAsyncTwitter;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return DBHelper.getCursorLoader(getType());
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (isListEmpty() && data.getCount() > WConfig.TIMELINE_MAX_CACHE) {
// There are over 200 tweets cached in database, clear at each launch time
// isListEmpty means just launched
Log.d("DEBUG", "remove old");
DBHelper.clearOldTweets(getType());
}
if (mAdapter.getCount() > 0 && mLoadingFooter.getState() != LoadingFooter.State.Loading) {
int firstVisPos = mListView.getFirstVisiblePosition();
View firstVisView = mListView.getChildAt(0);
int top = firstVisView != null ? firstVisView.getTop() : 0;
mListView.setBlockLayoutChildren(true);
int itemsAddedBeforeFirstVisible = data.getCount() - mAdapter.getCount();
mAdapter.changeCursor(data);
mListView.setBlockLayoutChildren(false);
mListView.setSelectionFromTop(firstVisPos + itemsAddedBeforeFirstVisible, top);
} else {
mAdapter.changeCursor(data);
}
// If there isn't any cache, load from the server
if (data != null && data.getCount() == 0) {
loadLatest();
}
}
private void performDismiss(final View mDismissView) {
// Animate the dismissed view to zero-height and then fire the dismiss callback.
// This triggers layout on each animation frame; in the future we may want to do something
// smarter and more performant.
final ViewGroup.LayoutParams lp = mDismissView.getLayoutParams();
final int originalHeight = mDismissView.getHeight();
ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(200);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Reset view presentation
mDismissView.setAlpha(1f);
mDismissView.setTranslationX(0);
lp.height = originalHeight;
mDismissView.setLayoutParams(lp);
}
});
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
lp.height = (Integer) valueAnimator.getAnimatedValue();
mDismissView.setLayoutParams(lp);
}
});
animator.start();
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.changeCursor(null);
}
@Override
public void onRefresh() {
loadLatest();
}
/**
* When pull to refresh, request for latest tweets
*/
public abstract void loadLatest();
/**
* When arrive at the footer, request for older tweets
*/
public abstract void loadNext();
public boolean isListEmpty() {
return mAdapter == null || mAdapter.getCount() == 0;
}
/**
* Listener for TwitterMethod Callback
*/
private TwitterListener listener = new TwitterAdapter() {
@Override
public void gotHomeTimeline(ResponseList<Status> statuses) {
super.gotHomeTimeline(statuses);
onTimelineResult(statuses);
}
@Override
public void gotMentions(ResponseList<Status> statuses) {
super.gotMentions(statuses);
onTimelineResult(statuses);
}
@Override
public void gotFavorites(ResponseList<Status> statuses) {
super.gotFavorites(statuses);
onTimelineResult(statuses);
}
@Override
public void createdFavorite(Status status) {
WingTweet favTweet = getDBHelper().getTweet(status.getId(), getType());
favTweet.favorited = true;
getDBHelper().update(favTweet);
getDBHelper().save(favTweet, WingStore.TYPE_FAVORITE);
}
@Override
public void destroyedFavorite(Status status) {
WingTweet unfavTweet = getDBHelper().getTweet(status.getId(), getType());
unfavTweet.favorited = false;
getDBHelper().update(unfavTweet);
getDBHelper().delete(unfavTweet, WingStore.TYPE_FAVORITE);
}
@Override
public void onException(TwitterException te, TwitterMethod method) {
super.onException(te, method);
onTwitterError(te, method);
}
};
@Override
public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
Log.d("DEBUG", "onViewStateRestored");
if(savedInstanceState!=null) {
mListView.setSelectionFromTop(savedInstanceState.getInt("index"), savedInstanceState.getInt("top"));
}
}
public void onTimelineResult(ResponseList<Status> statuses) {
Message msg = mHandler.obtainMessage();
msg.obj = statuses;
msg.what = TASK_TIMELINE;
mHandler.sendMessage(msg);
}
public void onTwitterError(TwitterException te, TwitterMethod method) {
String error2Show = TweetUtils.parseException(te, method);
Message msg = new Message();
msg.obj = error2Show;
msg.arg1 = method.ordinal();
msg.what = TASK_EXCPTION;
mHandler.sendMessage(msg);
}
/**
*
*/
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TASK_EXCPTION:
if (msg.arg1 == TwitterMethod.HOME_TIMELINE.ordinal()) {
mSwipeRefresh.setRefreshing(false);
Toast.makeText(getActivity(), msg.obj + "", Toast.LENGTH_SHORT).show();
} else if (msg.arg1 == TwitterMethod.CREATE_FAVORITE.ordinal()) {
Toast.makeText(getActivity(), "Fail to favorite", Toast.LENGTH_SHORT).show();
} else if (msg.arg1 == TwitterMethod.DESTROY_FAVORITE.ordinal()) {
Toast.makeText(getActivity(), "Fail to delete favorite", Toast.LENGTH_SHORT).show();
}
break;
case TASK_TIMELINE:
ResponseList<Status> statuses = (ResponseList<Status>) msg.obj;
if (!mSwipeRefresh.isRefreshing()) {
// load old
if (statuses == null || statuses.size() < 10) {
Log.d("DEBUG", "the end");
mLoadingFooter.setState(LoadingFooter.State.TheEnd);
} else {
mLoadingFooter.setState(LoadingFooter.State.Idle, 3000);
}
} else {
// load latest
if (statuses.size() > 0) {
Toast.makeText(getActivity(), statuses.size() + " New Tweets",
Toast.LENGTH_SHORT).show();
}
mSwipeRefresh.setRefreshing(false);
if (statuses.size() >= 17) {
DBHelper.deleteAllTweets();
}
}
ArrayList<WingTweet> wingTweets = new ArrayList<>();
for (Status ss : statuses) {
WingTweet tweet = new WingTweet(ss);
if (!getDBHelper().isTweetExisted(tweet.tweet_id, getType())) {
wingTweets.add(tweet);
}
}
getDBHelper().saveAll(wingTweets, getType());
break;
}
super.handleMessage(msg);
}
};
}