org.catnut.fragment.TweetFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.catnut.fragment.TweetFragment.java

Source

/*
 * The MIT License (MIT)
 * Copyright (c) 2014 longkai
 * The software shall be used for good, not evil.
 */
package org.catnut.fragment;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.LoaderManager;
import android.content.AsyncQueryHandler;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Typeface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.BaseColumns;
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.SwipeRefreshLayout;
import android.text.Editable;
import android.text.Html;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.ShareActionProvider;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.squareup.picasso.Picasso;
import com.viewpagerindicator.LinePageIndicator;
import org.catnut.R;
import org.catnut.adapter.CommentsAdapter;
import org.catnut.adapter.EmotionsAdapter;
import org.catnut.api.CommentsAPI;
import org.catnut.api.FavoritesAPI;
import org.catnut.api.TweetAPI;
import org.catnut.core.CatnutAPI;
import org.catnut.core.CatnutApp;
import org.catnut.core.CatnutProvider;
import org.catnut.core.CatnutRequest;
import org.catnut.metadata.Status;
import org.catnut.metadata.User;
import org.catnut.metadata.WeiboAPIError;
import org.catnut.processor.StatusProcessor;
import org.catnut.support.OnFragmentBackPressedListener;
import org.catnut.support.PageTransformer;
import org.catnut.support.TouchImageView;
import org.catnut.support.TweetImageSpan;
import org.catnut.support.TweetTextView;
import org.catnut.ui.ComposeTweetActivity;
import org.catnut.ui.HelloActivity;
import org.catnut.ui.ProfileActivity;
import org.catnut.ui.SingleFragmentActivity;
import org.catnut.ui.TweetActivity;
import org.catnut.util.CatnutUtils;
import org.catnut.util.ColorSwicher;
import org.catnut.util.Constants;
import org.catnut.util.DateTime;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;

/**
 * ??
 *
 * @author longkai
 */
public class TweetFragment extends Fragment implements TextWatcher, OnFragmentBackPressedListener,
        PopupMenu.OnMenuItemClickListener, LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener,
        AbsListView.OnScrollListener, AdapterView.OnItemLongClickListener, SwipeRefreshLayout.OnRefreshListener {

    private static final String TAG = "TweetFragment";
    private static final String RETWEET_INDICATOR = ">"; // ?
    private static final int COMMENT = 0;
    private static final int REPLY = 1;
    private static final int RETWEET = 2;

    //   private static final int MAX_SHOW_TOAST_TIME = 2;

    private Handler mHandler = new Handler();

    /** ? */
    private static final String[] PROJECTION = new String[] { "s." + BaseColumns._ID, Status.uid, Status.columnText,
            "s." + Status.created_at, User.screen_name, User.profile_image_url, User.remark };

    private RequestQueue mRequestQueue;
    private TweetImageSpan mImageSpan;
    private SharedPreferences mPreferences;
    private ConnectivityManager mConnectivityManager;
    private SwipeRefreshLayout mSwipeRefreshLayout;

    private String mSelection;

    private ListView mListView;
    private CommentsAdapter mAdapter;
    private EditText mSendText;
    private ImageView mSend;
    private TextView mTextCounter;
    private ImageView mOverflow;
    private PopupMenu mPopupMenu;

    // tweet id
    private long mId;
    private JSONObject mJson;
    // ?
    private int mTotal; // ????total?...?????
    private int mLastTotalCount; // ...
    // ????
    private boolean mFavorited = false;
    // ?id
    private long mReplyTo = 0L;
    // ?
    private int mRetweetOption = 0;
    // ??
    private int mShowToastTimes = 0;

    private String mPlainText; // ?

    // widgets
    private View mTweetLayout;
    private ImageView mAvatar;
    private TextView mRemark;
    private TextView mScreenName;
    private TweetTextView mText;
    private TextView mReplayCount;
    private TextView mReteetCount;
    private TextView mFavoriteCount;
    private TextView mSource;
    private TextView mCreateAt;
    private ImageView mThumbs;
    private View mPicsOverflow;
    //   private View mRetweetLayout;
    private ViewStub mRetweetLayout;

    private Typeface mTypeface;
    private boolean mStayInLatest;
    private float mLineSpacing = 1.0f;

    // others
    private ShareActionProvider mShareActionProvider;
    private Intent mShareIntent;

    protected Response.ErrorListener errorListener = new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.d(TAG, "error loading data from cloud!", error);
            WeiboAPIError weiboAPIError = WeiboAPIError.fromVolleyError(error);
            Toast.makeText(getActivity(), weiboAPIError.error, Toast.LENGTH_LONG).show();
            mSwipeRefreshLayout.setRefreshing(false);
        }
    };

    public static TweetFragment getFragment(long id) {
        Bundle args = new Bundle();
        args.putLong(Constants.ID, id);
        TweetFragment fragment = new TweetFragment();
        fragment.setArguments(args);
        return fragment;
    }

    public static TweetFragment getFragment(String tweet) {
        Bundle args = new Bundle();
        args.putString(Constants.JSON, tweet);
        TweetFragment fragment = new TweetFragment();
        fragment.setArguments(args);
        return fragment;
    }

    public int getFetchSize() {
        return CatnutUtils.resolveListPrefInt(mPreferences, getString(R.string.pref_default_fetch_size),
                getResources().getInteger(R.integer.default_fetch_size));
    }

    protected void refresh() {
        // ???
        if (!isNetworkAvailable()) {
            Toast.makeText(getActivity(), getString(R.string.network_unavailable), Toast.LENGTH_SHORT).show();
            initFromLocal();
            return;
        }
        // refresh!
        final int size = getFetchSize();
        (new Thread(new Runnable() {
            @Override
            public void run() {
                // ??????(?-)???Orz...
                String query = CatnutUtils.buildQuery(new String[] { BaseColumns._ID }, mSelection, Status.TABLE,
                        null, BaseColumns._ID + " desc", size + ", 1" // limit x, y
                );
                Cursor cursor = getActivity().getContentResolver().query(CatnutProvider.parse(Status.MULTIPLE),
                        null, query, null, null);
                // the cursor never null?
                final long since_id;
                if (cursor.moveToNext()) {
                    since_id = cursor.getLong(0);
                } else {
                    since_id = 0;
                }
                cursor.close();
                final CatnutAPI api = CommentsAPI.show(mId, since_id, 0, size, 0, 0);
                // refresh...
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mRequestQueue.add(new CatnutRequest(getActivity(), api,
                                new StatusProcessor.CommentTweetsProcessor(mId),
                                new Response.Listener<JSONObject>() {
                                    @Override
                                    public void onResponse(JSONObject response) {
                                        Log.d(TAG, "refresh done...");
                                        mTotal = response.optInt(Status.total_number);
                                        // ???
                                        mLastTotalCount = 0;
                                        mShowToastTimes = 0;
                                        JSONArray jsonArray = response.optJSONArray(Status.COMMENTS);
                                        int newSize = jsonArray.length(); // ...
                                        Bundle args = new Bundle();
                                        args.putInt(TAG, newSize);
                                        getLoaderManager().restartLoader(0, args, TweetFragment.this);
                                    }
                                }, errorListener)).setTag(TAG);
                    }
                });
            }
        })).start();
    }

    private void initFromLocal() {
        Bundle args = new Bundle();
        args.putInt(TAG, getFetchSize());
        getLoaderManager().initLoader(0, args, this);
        new Thread(updateLocalCount).start();
    }

    private Runnable updateLocalCount = new Runnable() {
        @Override
        public void run() {
            String query = CatnutUtils.buildQuery(new String[] { "count(0)" }, mSelection, Status.TABLE, null, null,
                    null);
            Cursor cursor = getActivity().getContentResolver().query(CatnutProvider.parse(Status.MULTIPLE), null,
                    query, null, null);
            if (cursor.moveToNext()) {
                mTotal = cursor.getInt(0);
            }
            cursor.close();
        }
    };

    protected void loadMore(long max_id) {
        // 
        // ?(??||?)
        boolean fromCloud = mPreferences.getBoolean(getString(R.string.pref_keep_latest),
                getResources().getBoolean(R.bool.pref_load_more_from_cloud));
        if (fromCloud && isNetworkAvailable()) {
            // ??ok?
            loadFromCloud(max_id);
        } else {
            // 
            loadFromLocal();
            // ?
            new Thread(updateLocalCount).start();
        }
    }

    private void loadFromLocal() {
        Bundle args = new Bundle();
        mLastTotalCount = mAdapter.getCount(); // 
        args.putInt(TAG, mAdapter.getCount() + getFetchSize());
        getLoaderManager().restartLoader(0, args, this);
        mSwipeRefreshLayout.setRefreshing(true);
    }

    private void loadFromCloud(long max_id) {
        mSwipeRefreshLayout.setRefreshing(true);
        CatnutAPI api = CommentsAPI.show(mId, 0, max_id, getFetchSize(), 0, 0);
        mRequestQueue.add(new CatnutRequest(getActivity(), api, new StatusProcessor.CommentTweetsProcessor(mId),
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.d(TAG, "load more from cloud done...");
                        mTotal = response.optInt(Status.total_number);
                        mLastTotalCount = mAdapter.getCount();
                        int newSize = response.optJSONArray(Status.COMMENTS).length() + mAdapter.getCount();
                        Bundle args = new Bundle();
                        args.putInt(TAG, newSize);
                        getLoaderManager().restartLoader(0, args, TweetFragment.this);
                    }
                }, errorListener)).setTag(TAG);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        Bundle args = getArguments();
        if (args.containsKey(Constants.JSON)) {
            try {
                mJson = new JSONObject(args.getString(Constants.JSON));
                mId = mJson.optLong(Constants.ID);
            } catch (JSONException e) {
                Log.e(TAG, "malformed json!", e);
                Toast.makeText(activity, activity.getString(R.string.malformed_json), Toast.LENGTH_LONG).show();
                // getActivity().onBackPressed();
            }
        } else {
            mId = args.getLong(Constants.ID);
        }
        mSelection = new StringBuilder(Status.TYPE).append("=").append(Status.COMMENT).append(" and ")
                .append(Status.TO_WHICH_TWEET).append("=").append(mId).toString();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        mConnectivityManager = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
        CatnutApp app = CatnutApp.getTingtingApp();
        mRequestQueue = app.getRequestQueue();
        mPreferences = app.getPreferences();
        mTypeface = CatnutUtils.getTypeface(mPreferences, getString(R.string.pref_customize_tweet_font),
                getString(R.string.default_typeface));
        mStayInLatest = mPreferences.getBoolean(getString(R.string.pref_keep_latest), true);
        mLineSpacing = CatnutUtils.getLineSpacing(mPreferences, getString(R.string.pref_line_spacing),
                getString(R.string.default_line_spacing));
        mImageSpan = new TweetImageSpan(getActivity());
        mAdapter = new CommentsAdapter(getActivity());
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.comments, null, false);
        mListView = (ListView) view.findViewById(android.R.id.list);
        mSendText = (EditText) view.findViewById(R.id.action_reply);
        mSend = (ImageView) view.findViewById(R.id.action_send);
        mTextCounter = (TextView) view.findViewById(R.id.text_counter);
        mOverflow = (ImageView) view.findViewById(R.id.action_overflow);
        mPopupMenu = new PopupMenu(getActivity(), mOverflow);
        // for our headers
        mTweetLayout = inflater.inflate(R.layout.tweet, null);
        mAvatar = (ImageView) mTweetLayout.findViewById(R.id.avatar);
        mRemark = (TextView) mTweetLayout.findViewById(R.id.remark);
        mScreenName = (TextView) mTweetLayout.findViewById(R.id.screen_name);
        mText = (TweetTextView) mTweetLayout.findViewById(R.id.text);
        mReplayCount = (TextView) mTweetLayout.findViewById(R.id.reply_count);
        mReteetCount = (TextView) mTweetLayout.findViewById(R.id.reteet_count);
        mFavoriteCount = (TextView) mTweetLayout.findViewById(R.id.like_count);
        mSource = (TextView) mTweetLayout.findViewById(R.id.source);
        mCreateAt = (TextView) mTweetLayout.findViewById(R.id.create_at);
        mThumbs = (ImageView) mTweetLayout.findViewById(R.id.thumbs);
        mPicsOverflow = mTweetLayout.findViewById(R.id.pics_overflow);
        mRetweetLayout = (ViewStub) mTweetLayout.findViewById(R.id.view_stub);
        // just return the list
        return view;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refresh);
        mSwipeRefreshLayout.setOnRefreshListener(this);
        ColorSwicher.injectColor(mSwipeRefreshLayout);
        mSendText.addTextChangedListener(this);
        mSendText.setTextColor(getResources().getColor(android.R.color.primary_text_light));
        mSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // ???
                send();
            }
        });
        mPopupMenu.inflate(R.menu.comment_overflow);
        mPopupMenu.setOnMenuItemClickListener(this);
        mOverflow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPopupMenu.show();
            }
        });
        // 
        mSwipeRefreshLayout.setRefreshing(true);
        if (savedInstanceState == null) {
            if (mPreferences.getBoolean(getString(R.string.pref_keep_latest), true)) {
                refresh();
            } else {
                initFromLocal();
            }
        } else {
            initFromLocal();
        }
        // ?/??
        if (mJson == null) {
            loadTweet();
        } else {
            loadRetweet();
        }
    }

    // ??
    private void loadTweet() {
        // load tweet from local...
        String query = CatnutUtils.buildQuery(
                new String[] { Status.uid, Status.columnText, Status.bmiddle_pic, Status.original_pic,
                        Status.comments_count, Status.reposts_count, Status.attitudes_count, Status.source,
                        Status.favorited, Status.retweeted_status, Status.pic_urls, "s." + Status.created_at,
                        User.screen_name, User.avatar_large, User.remark, User.verified },
                "s._id=" + mId, Status.TABLE + " as s", "inner join " + User.TABLE + " as u on s.uid=u._id", null,
                null);
        new AsyncQueryHandler(getActivity().getContentResolver()) {
            @Override
            protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
                if (cursor.moveToNext()) {
                    Picasso.with(getActivity()).load(cursor.getString(cursor.getColumnIndex(User.avatar_large)))
                            .placeholder(R.drawable.error).error(R.drawable.error).into(mAvatar);
                    final long uid = cursor.getLong(cursor.getColumnIndex(Status.uid));
                    final String screenName = cursor.getString(cursor.getColumnIndex(User.screen_name));
                    mAvatar.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Intent intent = new Intent(getActivity(), ProfileActivity.class);
                            intent.putExtra(Constants.ID, uid);
                            intent.putExtra(User.screen_name, screenName);
                            startActivity(intent);
                        }
                    });
                    String remark = cursor.getString(cursor.getColumnIndex(User.remark));
                    mRemark.setText(TextUtils.isEmpty(remark) ? screenName : remark);
                    mScreenName.setText(getString(R.string.mention_text, screenName));
                    mPlainText = cursor.getString(cursor.getColumnIndex(Status.columnText));
                    mText.setText(mPlainText);
                    CatnutUtils.vividTweet(mText, mImageSpan);
                    CatnutUtils.setTypeface(mText, mTypeface);
                    mText.setLineSpacing(0, mLineSpacing);
                    int replyCount = cursor.getInt(cursor.getColumnIndex(Status.comments_count));
                    mReplayCount.setText(CatnutUtils.approximate(replyCount));
                    int retweetCount = cursor.getInt(cursor.getColumnIndex(Status.reposts_count));
                    mReteetCount.setText(CatnutUtils.approximate(retweetCount));
                    int favoriteCount = cursor.getInt(cursor.getColumnIndex(Status.attitudes_count));
                    mFavoriteCount.setText(CatnutUtils.approximate(favoriteCount));
                    String source = cursor.getString(cursor.getColumnIndex(Status.source));
                    mSource.setText(Html.fromHtml(source).toString());
                    mCreateAt.setText(DateUtils.getRelativeTimeSpanString(
                            DateTime.getTimeMills(cursor.getString(cursor.getColumnIndex(Status.created_at)))));
                    if (CatnutUtils.getBoolean(cursor, User.verified)) {
                        mTweetLayout.findViewById(R.id.verified).setVisibility(View.VISIBLE);
                    }
                    String thumb = cursor.getString(cursor.getColumnIndex(Status.bmiddle_pic));
                    String url = cursor.getString(cursor.getColumnIndex(Status.original_pic));
                    loadThumbs(thumb, url, mThumbs,
                            CatnutUtils.optPics(cursor.getString(cursor.getColumnIndex(Status.pic_urls))),
                            mPicsOverflow);
                    // retweet
                    final String jsonString = cursor.getString(cursor.getColumnIndex(Status.retweeted_status));
                    if (!TextUtils.isEmpty(jsonString)) {
                        View retweet = mRetweetLayout.inflate();
                        try {
                            JSONObject json = new JSONObject(jsonString);
                            JSONObject user = json.optJSONObject(User.SINGLE);
                            String _remark = user.optString(User.remark);
                            if (TextUtils.isEmpty(_remark)) {
                                _remark = user.optString(User.screen_name);
                            }
                            CatnutUtils.setText(retweet, R.id.retweet_nick,
                                    getString(R.string.mention_text, _remark));
                            long mills = DateTime.getTimeMills(json.optString(Status.created_at));
                            CatnutUtils.setText(retweet, R.id.retweet_create_at,
                                    DateUtils.getRelativeTimeSpanString(mills));
                            TweetTextView retweetText = (TweetTextView) CatnutUtils.setText(retweet,
                                    R.id.retweet_text, json.optString(Status.text));
                            CatnutUtils.vividTweet(retweetText, mImageSpan);
                            CatnutUtils.setTypeface(retweetText, mTypeface);
                            retweetText.setLineSpacing(0, mLineSpacing);
                            retweet.setOnClickListener(new View.OnClickListener() {
                                @Override
                                public void onClick(View v) {
                                    Intent intent = new Intent(getActivity(), TweetActivity.class);
                                    intent.putExtra(Constants.JSON, jsonString);
                                    startActivity(intent);
                                }
                            });
                            retweet.findViewById(R.id.verified)
                                    .setVisibility(user.optBoolean(User.verified) ? View.VISIBLE : View.GONE);
                            if (json.has(Status.thumbnail_pic)) {
                                loadThumbs(json.optString(Status.bmiddle_pic), json.optString(Status.bmiddle_pic),
                                        (ImageView) retweet.findViewById(R.id.thumbs),
                                        json.optJSONArray(Status.pic_urls),
                                        retweet.findViewById(R.id.pics_overflow));
                            }
                        } catch (JSONException e) {
                            Log.e(TAG, "convert text to string error!", e);
                            retweet.setVisibility(View.GONE);
                        }
                    }
                    // shareAndFavorite&favorite
                    shareAndFavorite(CatnutUtils.getBoolean(cursor, Status.favorited), mPlainText);
                }
                cursor.close();
            }
        }.startQuery(0, null, CatnutProvider.parse(Status.MULTIPLE), null, query, null, null);
    }

    // ??
    private void loadRetweet() {
        JSONObject user = mJson.optJSONObject(User.SINGLE);
        Picasso.with(getActivity()).load(user == null ? Constants.NULL : user.optString(User.avatar_large))
                .placeholder(R.drawable.error).error(R.drawable.error).into(mAvatar);
        final long uid = mJson.optLong(Status.uid);
        final String screenName = user == null ? getString(R.string.unknown_user)
                : user.optString(User.screen_name);
        mAvatar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getActivity(), ProfileActivity.class);
                intent.putExtra(Constants.ID, uid);
                intent.putExtra(User.screen_name, screenName);
                startActivity(intent);
            }
        });
        String remark = user.optString(User.remark);
        mRemark.setText(TextUtils.isEmpty(remark) ? screenName : remark);
        mScreenName.setText(getString(R.string.mention_text, screenName));
        mPlainText = mJson.optString(Status.text);
        mText.setText(mPlainText);
        CatnutUtils.vividTweet(mText, mImageSpan);
        CatnutUtils.setTypeface(mText, mTypeface);
        mText.setLineSpacing(0, mLineSpacing);
        int replyCount = mJson.optInt(Status.comments_count);
        mReplayCount.setText(CatnutUtils.approximate(replyCount));
        int retweetCount = mJson.optInt(Status.reposts_count);
        mReteetCount.setText(CatnutUtils.approximate(retweetCount));
        int favoriteCount = mJson.optInt(Status.attitudes_count);
        mFavoriteCount.setText(CatnutUtils.approximate(favoriteCount));
        String source = mJson.optString(Status.source);
        mSource.setText(Html.fromHtml(source).toString());
        mCreateAt.setText(
                DateUtils.getRelativeTimeSpanString(DateTime.getTimeMills(mJson.optString(Status.created_at))));
        if (user.optBoolean(User.verified)) {
            mTweetLayout.findViewById(R.id.verified).setVisibility(View.VISIBLE);
        }

        loadThumbs(mJson.optString(Status.bmiddle_pic), mJson.optString(Status.original_pic), mThumbs,
                mJson.optJSONArray(Status.pic_urls), mPicsOverflow);
        shareAndFavorite(mJson.optBoolean(Status.favorited), mJson.optString(Status.text));

        if (!mJson.has(Status.retweeted_status)) {
            //todo: ?????
        }
    }

    /**
     * ??
     *
     * @param thumb url
     * @param originalUrl url
     */
    private void loadThumbs(String thumb, final String originalUrl, final ImageView thumbs,
            final JSONArray jsonArray, View overflow) {
        if (!TextUtils.isEmpty(thumb)) {
            if (mStayInLatest) {
                int screenWidth = CatnutUtils.getScreenWidth(getActivity());
                Picasso.with(getActivity()).load(thumb).into(thumbs);
                thumbs.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        return CatnutUtils.imageOverlay(v, event);
                    }
                });
                thumbs.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        thumbs.getDrawable().clearColorFilter();
                        mText.invalidate();
                        Intent intent = SingleFragmentActivity.getIntent(getActivity(),
                                SingleFragmentActivity.PHOTO_VIEWER);
                        intent.putExtra(Constants.PIC, originalUrl);
                        startActivity(intent);
                    }
                });
            } else {
                thumbs.setImageResource(R.drawable.error);
            }
            thumbs.setVisibility(View.VISIBLE);
        } else {
            thumbs.setVisibility(View.GONE);
            overflow.setVisibility(View.GONE);
        }
        if (jsonArray != null && jsonArray.length() > 1) {
            overflow.setVisibility(View.VISIBLE);
            overflow.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    View view = LayoutInflater.from(getActivity()).inflate(R.layout.tweet_pics, null);
                    LinePageIndicator indicator = (LinePageIndicator) view.findViewById(R.id.indicator);
                    final ViewPager pager = (ViewPager) view.findViewById(R.id.pager);
                    pager.setPageTransformer(true, new PageTransformer.DepthPageTransformer());
                    pager.setAdapter(new PagerAdapter() {
                        @Override
                        public int getCount() {
                            return jsonArray.length();
                        }

                        @Override
                        public boolean isViewFromObject(View view, Object object) {
                            return view == object;
                        }

                        @Override
                        public void destroyItem(ViewGroup container, int position, Object object) {
                            container.removeView((View) object);
                        }

                        @Override
                        public Object instantiateItem(ViewGroup container, int position) {
                            View v = LayoutInflater.from(getActivity()).inflate(R.layout.photo, null);
                            TouchImageView iv = (TouchImageView) v.findViewById(R.id.image);
                            String url = jsonArray.optJSONObject(position).optString(Status.thumbnail_pic);
                            String replace = url.replace(Constants.THUMBNAIL, Status.MEDIUM_THUMBNAIL);
                            Picasso.with(getActivity()).load(replace).into(iv);
                            container.addView(v);
                            return v;
                        }
                    });
                    indicator.setViewPager(pager);
                    new AlertDialog.Builder(
                            new ContextThemeWrapper(getActivity(), android.R.style.Theme_Holo_Dialog)).setView(view)
                                    .setPositiveButton(getString(R.string.original_pics),
                                            new DialogInterface.OnClickListener() {
                                                @Override
                                                public void onClick(DialogInterface dialog, int which) {
                                                    ArrayList<Uri> urls = new ArrayList<Uri>(jsonArray.length());
                                                    for (int i = 0; i < jsonArray.length(); i++) {
                                                        String s = jsonArray.optJSONObject(i)
                                                                .optString(Status.thumbnail_pic)
                                                                .replace(Constants.THUMBNAIL,
                                                                        Status.LARGE_THUMBNAIL);
                                                        urls.add(Uri.parse(s));
                                                    }
                                                    Intent intent = SingleFragmentActivity.getIntent(getActivity(),
                                                            SingleFragmentActivity.GALLERY);
                                                    intent.putExtra(GalleryPagerFragment.CUR_INDEX,
                                                            pager.getCurrentItem());
                                                    intent.putExtra(GalleryPagerFragment.URLS, urls);
                                                    intent.putExtra(GalleryPagerFragment.TITLE,
                                                            getString(R.string.tweet_pics));
                                                    startActivity(intent);
                                                }
                                            })
                                    .setNegativeButton(getString(R.string.close), null).show();
                }
            });
        } else {
            overflow.setVisibility(View.GONE);
        }
    }

    /**
     * ?/??
     *
     * @param favorited ???/???
     * @param text ?
     */
    private void shareAndFavorite(boolean favorited, String text) {
        mShareIntent = new Intent(Intent.ACTION_SEND).setType(getString(R.string.mime_text_plain));
        mShareIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.tweet_share_subject));
        mShareIntent.putExtra(Intent.EXTRA_TEXT, text);
        if (mShareActionProvider != null) {
            mShareActionProvider.setShareIntent(mShareIntent);
        }
        mFavorited = favorited;
    }

    @Override
    public void onStart() {
        super.onStart();
        getActivity().getActionBar().setTitle(getString(R.string.tweet));
    }

    @Override
    public void onStop() {
        super.onStop();
        mRequestQueue.cancelAll(TAG);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mListView.addHeaderView(mTweetLayout);
        mListView.setAdapter(mAdapter);
        mListView.setOnItemClickListener(this);
        mListView.setOnItemLongClickListener(this);
        mListView.setOnScrollListener(this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        int limit = args.getInt(TAG, getFetchSize());
        return CatnutUtils.getCursorLoader(getActivity(), CatnutProvider.parse(Status.MULTIPLE), PROJECTION,
                mSelection, null, Status.TABLE + " as s", "inner join " + User.TABLE + " as u on s.uid=u._id",
                "s." + BaseColumns._ID + " desc", String.valueOf(limit));
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        if (mSwipeRefreshLayout.isRefreshing()) {
            mSwipeRefreshLayout.setRefreshing(false);
        }
        mAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }

    @Override
    public void onRefresh() {
        refresh();
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        boolean canLoading = SCROLL_STATE_IDLE == scrollState // ???
                && (mListView.getLastVisiblePosition() - 1) == (mAdapter.getCount() - 1) // header?
                && !mSwipeRefreshLayout.isRefreshing(); // ??
        //            && mAdapter.getCount() > 0; // ?
        if (canLoading) {
            // ???
            if (mAdapter.getCount() >= mTotal || mLastTotalCount == mAdapter.getCount()) {
                Log.d(TAG, "load all done...");
                //            if (mShowToastTimes < MAX_SHOW_TOAST_TIME) {
                //               Toast.makeText(getActivity(), R.string.no_more, Toast.LENGTH_SHORT).show();
                //               mShowToastTimes++;
                //            }
            } else {
                Log.d(TAG, "load...");
                loadMore(mAdapter.getItemId(mAdapter.getCount() - 1));
            }
        } else {
            Log.d(TAG, "cannot load more!");
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // no-op
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, final int position, final long id) {
        if (CatnutUtils.hasLength(mSendText)) {
            confirmAbortEdit(new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    mSendText.setText(null);
                    intentToReply(position, id);
                }
            });
        } else {
            intentToReply(position, id);
        }
    }

    // ??
    private void confirmAbortEdit(DialogInterface.OnClickListener abortListener) {
        new AlertDialog.Builder(getActivity()).setMessage(R.string.abort_existing_reply_alert)
                .setPositiveButton(android.R.string.ok, abortListener).setNegativeButton(android.R.string.no, null)
                .show();
    }

    /**
     * ????
     *
     * @param position
     */
    private void intentToReply(int position, long id) {
        // text?
        mSendText.setText(null);

        Cursor cursor = (Cursor) mAdapter.getItem(position - 1); // a header view in top...
        String screenName = cursor.getString(cursor.getColumnIndex(User.screen_name));
        // 
        mReplyTo = id;
        mSendText.requestFocus();
        mSendText.setHint(getString(R.string.reply_comment_hint, screenName));
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.tweet, menu);
        MenuItem share = menu.findItem(R.id.share);
        mShareActionProvider = (ShareActionProvider) share.getActionProvider();
        mShareActionProvider.setShareIntent(mShareIntent);
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        MenuItem fav = menu.findItem(R.id.action_toggle_favorite);
        if (mFavorited) {
            fav.setTitle(getString(R.string.cancle_favorite)).setIcon(R.drawable.ic_title_decline);
        } else {
            fav.setTitle(getString(R.string.favorite)).setIcon(R.drawable.ic_title_favorite);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.action_toggle_favorite:
            toggleFavorite();
            break;
        case R.id.pref:
            startActivity(SingleFragmentActivity.getIntent(getActivity(), SingleFragmentActivity.PREF));
            break;
        case R.id.action_compose:
            startActivity(new Intent(getActivity(), ComposeTweetActivity.class));
            break;
        case R.id.action_comment:
            // 
            // never be null but empty
            if (CatnutUtils.hasLength(mSendText)) {
                confirmAbortEdit(new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // ?
                        mReplyTo = 0L;
                        resetEditor(getString(R.string.comment_tweet), false);
                    }
                });
            } else {
                mReplyTo = 0L; // ?
                resetEditor(getString(R.string.comment_tweet), false);
            }
            break;
        case R.id.action_reteet:
            if (CatnutUtils.hasLength(mSendText)) {
                confirmAbortEdit(new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        resetEditor(RETWEET_INDICATOR + getString(R.string.retweet), true);
                    }
                });
            } else {
                resetEditor(RETWEET_INDICATOR + getString(R.string.retweet), true);
            }
            break;
        case R.id.fantasy:
            startActivity(
                    new Intent(getActivity(), HelloActivity.class).putExtra(HelloActivity.TAG, HelloActivity.TAG));
            break;
        case android.R.id.copy:
            CatnutUtils.copy2ClipBoard(getActivity(), getString(R.string.tweet), mPlainText,
                    getString(R.string.tweet_text_copied));
            break;
        default:
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    private void resetEditor(String hint, boolean sendEnabled) {
        mSendText.setText(null);
        mSendText.setHint(hint);
        mSendText.requestFocus();
        mSend.setImageResource(sendEnabled ? R.drawable.ic_dm_send_default : R.drawable.ic_dm_send_disabled);
        mSend.setClickable(sendEnabled);
    }

    private void toggleFavorite() {
        mRequestQueue.add(
                new CatnutRequest(getActivity(), mFavorited ? FavoritesAPI.destroy(mId) : FavoritesAPI.create(mId),
                        new StatusProcessor.FavoriteTweetProcessor(), new Response.Listener<JSONObject>() {
                            @Override
                            public void onResponse(JSONObject response) {
                                Toast.makeText(getActivity(),
                                        mFavorited ? R.string.cancle_favorite_success : R.string.favorite_success,
                                        Toast.LENGTH_SHORT).show();
                                mFavorited = !mFavorited;
                                // ?ui
                                JSONObject status = response.optJSONObject(Status.SINGLE);
                                mReplayCount.setText(CatnutUtils.approximate(status.optInt(Status.comments_count)));
                                mReteetCount.setText(CatnutUtils.approximate(status.optInt(Status.reposts_count)));
                                mFavoriteCount
                                        .setText(CatnutUtils.approximate(status.optInt(Status.attitudes_count)));
                            }
                        }, new Response.ErrorListener() {
                            @Override
                            public void onErrorResponse(VolleyError error) {
                                WeiboAPIError weiboAPIError = WeiboAPIError.fromVolleyError(error);
                                Toast.makeText(getActivity(), weiboAPIError.error, Toast.LENGTH_SHORT).show();
                            }
                        }))
                .setTag(TAG);
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // no-op
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // no-op
    }

    @Override
    public void afterTextChanged(Editable s) {
        int count = 140 - mSendText.length();
        mTextCounter.setText(String.valueOf(count));
        mTextCounter.setTextColor(getResources().getColor(android.R.color.holo_green_dark));
        // never lt 140, 'cause the edit text' s max length is 140
        if (count != 140) {
            mSend.setClickable(true);
            mSend.setFocusable(true);
            mSend.setImageResource(R.drawable.ic_dm_send_default);
        } else {
            mSend.setClickable(false);
            mSend.setFocusable(false);
            mSend.setImageResource(R.drawable.ic_dm_send_disabled);
        }
    }

    // ??
    private void send() {
        // ?hint?
        final int type;
        String hint = mSendText.getHint().toString();
        if (hint.startsWith(RETWEET_INDICATOR)) {
            type = RETWEET;
        } else {
            type = hint.contains("@") ? REPLY : COMMENT;
        }
        if (type != RETWEET) { // ???...
            if (!CatnutUtils.hasLength(mSendText)) {
                Toast.makeText(getActivity(), getString(R.string.require_not_empty), Toast.LENGTH_SHORT).show();
                return; // ????
            }
        }
        mSend.setBackgroundResource(R.drawable.ic_dm_send_disabled);
        mSend.setClickable(false);
        String text = mSendText.getText().toString();
        CatnutAPI api;
        switch (type) {
        default:
        case COMMENT:
            api = CommentsAPI.create(text, mId, 0, null);
            break;
        case REPLY:
            api = CommentsAPI.reply(mReplyTo, mId, text, 0, 0, null);
            break;
        case RETWEET:
            api = TweetAPI.repost(mId, text, mRetweetOption, null);
            break;
        }
        mRequestQueue.add(new CatnutRequest(getActivity(), api,
                type == RETWEET ? new StatusProcessor.SingleTweetProcessor(Status.RETWEET)
                        : new StatusProcessor.CommentTweetProcessor(mId), // 
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        // ?
                        mSendText.setText(null);
                        mTotal++;
                        String msg;
                        switch (type) {
                        default:
                        case COMMENT:
                            msg = getString(R.string.comment_success);
                            break;
                        case REPLY:
                            msg = getString(R.string.reply_success);
                            break;
                        case RETWEET:
                            msg = getString(R.string.retweet_success);
                            mSend.setImageResource(R.drawable.ic_dm_send_default);
                            mSend.setClickable(true);
                            break;
                        }
                        Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
                        // ui
                        TextView which = type == RETWEET ? mReteetCount : mReplayCount;
                        String before = which.getText().toString();
                        if (TextUtils.isEmpty(before)) {
                            which.setText("1"); // ???R.string.xx
                        } else {
                            which.setText(String.valueOf(Integer.parseInt(before) + 1));
                        }
                        // listlimit??list...
                        int pos = mListView.getFirstVisiblePosition();
                        mLastTotalCount = mAdapter.getCount();
                        Bundle args = new Bundle();
                        args.putInt(TAG, mLastTotalCount + 1);
                        getLoaderManager().restartLoader(0, args, TweetFragment.this);
                        mListView.setSelection(pos);
                    }
                }, sendErrorListener)).setTag(TAG);
    }

    // ????
    private Response.ErrorListener sendErrorListener = new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            WeiboAPIError weiboAPIError = WeiboAPIError.fromVolleyError(error);
            Toast.makeText(getActivity(), weiboAPIError.error, Toast.LENGTH_SHORT).show();
        }
    };

    @Override
    public void onBackPressed() {
        if (CatnutUtils.hasLength(mSendText)) {
            confirmAbortEdit(new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    getActivity().onBackPressed();
                }
            });
        } else {
            getActivity().onBackPressed();
        }
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        // first check emotions
        if (item.getItemId() == R.id.action_emotions) {
            GridView emotions = (GridView) LayoutInflater.from(getActivity()).inflate(R.layout.emotions, null);
            emotions.setAdapter(new EmotionsAdapter(getActivity()));
            emotions.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    int cursor = mSendText.getSelectionStart();
                    mSendText.getText().insert(cursor,
                            CatnutUtils.text2Emotion(getActivity(), TweetImageSpan.EMOTION_KEYS[position],
                                    getResources().getInteger(R.integer.icon_bound_px)));
                    mSendText.requestFocus();
                }
            });
            AlertDialog alertDialog = new AlertDialog.Builder(getActivity()).setView(emotions).create();
            alertDialog.show();
            alertDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
                    getResources().getDimensionPixelSize(R.dimen.emotion_window_height));
            return true;
        }
        switch (item.getItemId()) {
        case R.id.action_reply_none:
            mRetweetOption = 0;
            break;
        case R.id.action_reply_current:
            mRetweetOption = 1;
            break;
        case R.id.action_reply_original:
            mRetweetOption = 2;
            break;
        case R.id.action_reply_both:
            mRetweetOption = 3;
            break;
        default:
            break;
        }
        if (!item.isChecked()) {
            item.setChecked(true);
        }
        return true;
    }

    public boolean isNetworkAvailable() {
        NetworkInfo activeNetwork = mConnectivityManager.getActiveNetworkInfo();
        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
    }

    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
        Cursor cursor = (Cursor) mAdapter.getItem(position - 1);
        String text = cursor.getString(cursor.getColumnIndex(Status.columnText));
        CatnutUtils.copy2ClipBoard(getActivity(), getString(R.string.tweet), text,
                getString(R.string.tweet_text_copied));
        return true;
    }
}