public class Handler4CommentNumber extends BaseJsonResponseHandler {

    public Handler4CommentNumber(@NonNull OnHttpResponseCallBackImpl<ArrayList<CommentNumber>> onHttpResponseCallBack) {
        super(onHttpResponseCallBack);
    }

    @Override
    protected void onSuccess(int statusCode, String rawJsonResponse) {


        try {
            String url = getRequestURI().toURL().toString();
            JSONObject jsonObject = new JSONObject(rawJsonResponse).getJSONObject("response");
            String[] comment_IDs = url.split("\\=")[1].split("\\,");
            ArrayList<CommentNumber> commentNumbers = new ArrayList<>();

            for (String comment_ID : comment_IDs) {

                if (!jsonObject.isNull(comment_ID)) {
                    CommentNumber commentNumber = new CommentNumber();
                    commentNumber.setComments(jsonObject.getJSONObject(comment_ID).getInt(CommentNumber.COMMENTS));
                    commentNumber.setThread_id(jsonObject.getJSONObject(comment_ID).getString(CommentNumber.THREAD_ID));
                    commentNumber.setThread_key(jsonObject.getJSONObject(comment_ID).getString(CommentNumber.THREAD_KEY));
                    commentNumbers.add(commentNumber);
                } else {
                    //可能会出现没有对应id的数据的情况，为了保证条数一致，添加默认数据
                    commentNumbers.add(new CommentNumber("0", "0", 0));
                }
            }
            mHttpResponseCallBack.onSuccess(SUCCESS, commentNumbers);
        } catch (JSONException e) {
            e.printStackTrace();
            onFailure(FAILED_JSON_PARSE, e);
        } catch (MalformedURLException e) {
            onFailure(FAILED_MALFORMED_URL_EXCEPTION, e);
            e.printStackTrace();
        }
    }

    @Override
    protected void onFailure(int statusCode, Throwable throwable) {
        mHttpResponseCallBack.onFailure(statusCode, throwable);
    }
} public class Handler4Joke extends BaseJsonResponseHandler {

    public Handler4Joke(@NonNull OnHttpResponseCallBackImpl<ArrayList<Joke>> onHttpResponseCallBack) {
        super(onHttpResponseCallBack);
    }

    @Override
    protected void onSuccess(int statusCode, String rawJsonResponse) {

        try {
            String jsonStr = new JSONObject(rawJsonResponse).getJSONArray("comments").toString();

            ArrayList<Joke> jokes = (ArrayList<Joke>) JSONParser.toObject(jsonStr,
                    new TypeToken<ArrayList<Joke>>() {
                    }.getType());
            mHttpResponseCallBack.onSuccess(SUCCESS, jokes);
        } catch (JSONException e) {
            e.printStackTrace();
            onFailure(FAILED_JSON_PARSE, e);
        }
    }

    @Override
    protected void onFailure(int statusCode, Throwable throwable) {
        mHttpResponseCallBack.onFailure(statusCode, throwable);
    }
} public class HttpClientProxy implements ResponseCode {

    public static final int METHOD_GET = 0;
    public static final int METHOD_POST = 1;

    private static AsyncHttpClient mHttpClient;

    public static void init() {
        synchronized (HttpClientProxy.class) {
            if (mHttpClient == null) {
                mHttpClient = new AsyncHttpClient();
                mHttpClient.setTimeout(15 * 1000);
                mHttpClient.setMaxRetriesAndTimeout(1, 1000);

                HttpClient client = mHttpClient.getHttpClient();
                if (client instanceof DefaultHttpClient) {
                    mHttpClient.setEnableRedirects(true, true, true);
                }
            }
        }
    }

    public static AsyncHttpClient getInstance() {
        if (mHttpClient == null) {
            init();
        }

        return mHttpClient;
    }

    @Nullable
    public static RequestHandle get(Context context, String url, ResponseHandlerInterface responseHandler) {
        return call(METHOD_GET, context, url, null, responseHandler);
    }

    @Nullable
    public static RequestHandle get(Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) {
        return call(METHOD_GET, context, url, params, responseHandler);
    }

    @Nullable
    public static RequestHandle post(Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) {
        return call(METHOD_POST, context, url, params, responseHandler);
    }

    @Nullable
    private static RequestHandle call(int method, Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) {


        if (NetWorkUtil.isNetWorkConnected(context)) {
            switch (method) {
                case METHOD_GET:
                    if (params == null) {
                        return getInstance().get(context, url, responseHandler);
                    } else {
                        return getInstance().get(context, url, params, responseHandler);
                    }
                case METHOD_POST:
                    return getInstance().post(context, url, params, responseHandler);
                default:
                    return null;
            }
        } else {
            responseHandler.sendFailureMessage(FAILED_NO_NETWORK, null, null, new NetworkErrorException(MESSAGE_NO_NETWORK));
            return null;
        }
    }

    public static void cancelRequest(RequestHandle... handles) {
        if (handles != null && (handles.length > 0)) {
            for (RequestHandle requestHandle : handles) {
                if ((requestHandle != null) && (!requestHandle.isFinished()) && (!requestHandle.isCancelled())) {
                    requestHandle.cancel(true);
                }
            }
        }
    }

    public static void cancelRequest(List<RequestHandle> handles) {
        if (handles != null && (handles.size() > 0)) {
            for (RequestHandle requestHandle : handles) {
                if ((requestHandle != null) && (!requestHandle.isFinished()) && (!requestHandle.isCancelled())) {
                    requestHandle.cancel(true);
                }
            }
        }
    }

    public static void cancelRequest(Object tag) {
        getInstance().cancelRequestsByTAG(tag, true);
    }

} public interface ConstantString {

    String COMMENT_FAILED = "神秘力量导致评论失败 ⊙︿⊙";
    String LOAD_FAILED = "神秘力量导致加载失败 ⊙︿⊙";
    String SAVE_FAILED = "神秘力量导致保存失败 ⊙︿⊙";
    String SHARE_FAILED = "神秘力量导致分享失败 ⊙︿⊙";
    String REPLY_FAILED = "神秘力量导致回复失败 ⊙︿⊙";

    String INPUT_TOO_SHORT = "你的..太短..";
    String COPY_SUCCESS = "复制成功";
    String SAVE_SUCCESS = "保存成功";
    String REPLY_SUCCESS = "回复成功";
    String SAVE_SMALL_SUCCESS = "保存缩略图成功";
    String LOAD_NO_NETWORK = "无网络，当前为缓存数据";
    String FORBID_COMMENTS = "禁止评论";
    String NO_COMMENTS = "暂无评论";

    String DATA_FRESH_NEWS = "FreshNews";
    String DATA_POSITION = "position";
    String DATA_THREAD_ID = "thread_id";
    String DATA_THREAD_KEY = "thread_key";
    String DATA_IS_FROM_FRESH_NEWS = "is_from_fresh_news";

    String DATA_IMAGE_URL = "img_url";
    String DATA_IMAGE_ID = "img_id";
    String DATA_IMAGE_AUTHOR = "img_author";
    String DATA_IS_NEED_WEBVIEW = "is_need_webview";

    String DATA_IS_SIAMLL_PIC =  "isSmallPic";
    String DATA_FILE_PATH =  "file_path";

} public class JDApplication extends Application {

    public static int COLOR_OF_DIALOG = R.color.primary;
    public static int COLOR_OF_DIALOG_CONTENT = Color.WHITE;

    private static Context mContext;
    private static DaoMaster daoMaster;
    private static DaoSession daoSession;

    private RefWatcher refWatcher;

    @Override
    public void onCreate() {
        StrictModeUtil.init();
        super.onCreate();
        refWatcher = LeakCanary.install(this);
        mContext = this;
        HttpClientProxy.init();
        ImageLoadProxy.initImageLoader(this);

        if (BuildConfig.DEBUG) {
            Logger.init().hideThreadInfo().setMethodCount(1).setLogLevel(LogLevel.FULL);
        }
    }

    public static Context getContext() {
        return mContext;
    }

    public static RefWatcher getRefWatcher(Context context) {
        JDApplication application = (JDApplication) context.getApplicationContext();
        return application.refWatcher;
    }


    public static DaoMaster getDaoMaster(Context context) {
        if (daoMaster == null) {
            DaoMaster.OpenHelper helper = new DaoMaster.DevOpenHelper(context, BaseCache.DB_NAME, null);
            daoMaster = new DaoMaster(helper.getWritableDatabase());
        }
        return daoMaster;
    }

    public static DaoSession getDaoSession(Context context) {
        if (daoSession == null) {
            if (daoMaster == null) {
                daoMaster = getDaoMaster(context);
            }
            daoSession = daoMaster.newSession();
        }
        return daoSession;
    }

} public class JokeCache extends BaseCache {

    private static JokeCache instance;
    private static JokeCacheDao mJokeCacheDao;

    private JokeCache() {
    }

    public static JokeCache getInstance(Context context) {

        if (instance == null) {

            synchronized (JokeCache.class) {
                if (instance == null) {
                    instance = new JokeCache();
                }
            }

            mDaoSession = JDApplication.getDaoSession(context);
            mJokeCacheDao = mDaoSession.getJokeCacheDao();
        }
        return instance;
    }

    public void clearAllCache() {
        mJokeCacheDao.deleteAll();
    }


    @Override
    public ArrayList<Joke> getCacheByPage(int page) {
        QueryBuilder<com.socks.greendao.JokeCache> query = mJokeCacheDao.queryBuilder().where(JokeCacheDao.Properties.Page.eq("" + page));

        if (query.list().size() > 0) {
            return (ArrayList<Joke>) JSONParser.toObject(query.list().get(0).getResult(),
                    new TypeToken<ArrayList<Joke>>() {
                    }.getType());
        } else {
            return new ArrayList<>();
        }

    }

    @Override
    public void addResultCache(String result, int page) {
        com.socks.greendao.JokeCache jokeCache = new com.socks.greendao.JokeCache();
        jokeCache.setResult(result);
        jokeCache.setPage(page);
        jokeCache.setTime(System.currentTimeMillis());

        mJokeCacheDao.insert(jokeCache);
    }

} public interface LoadResultCallBack {

    int SUCCESS_OK = 1001;
    int SUCCESS_NONE = 1002;
    int ERROR_NET = 1003;

    void onSuccess(int result, Object object);

    void onError(int code, String msg);
} public abstract class OnHttpResponseCallBackImpl<T> implements OnHttpResponseCallBack<T> {


    @Override
    public void onSuccess(int statusCode,T t) {

    }

    @Override
    public void onStart() {

    }

    @Override
    public void onFinish() {

    }

    @Override
    public void onFailure(int statusCode, Throwable throwable) {

    }

    @Override
    public void onCancel() {

    }
} public class CommentNumber {

    //用于获取评论数量
    public static final String URL_COMMENT_COUNTS = "http://jandan.duoshuo.com/api/threads/counts.json?threads=";
    public static final String COMMENTS = "comments";

    public static final String THREAD_ID = "thread_id";
    public static final String THREAD_KEY = "thread_key";

    private int comments;
    private String thread_id;
    private String thread_key;

    public CommentNumber() {
    }

    public CommentNumber(String thread_id, String thread_key, int comments) {
        this.thread_id = thread_id;
        this.thread_key = thread_key;
        this.comments = comments;
    }

    public String getThread_id() {
        return thread_id;
    }

    public void setThread_id(String thread_id) {
        this.thread_id = thread_id;
    }

    public String getThread_key() {
        return thread_key;
    }

    public void setThread_key(String thread_key) {
        this.thread_key = thread_key;
    }


    public static String getCommentCountsURL(String params) {
        return URL_COMMENT_COUNTS + params;
    }

    public int getComments() {
        return comments;
    }

    public void setComments(int comments) {
        this.comments = comments;
    }

} public class JSONParser {

    private static Gson gson = new Gson();

    public static String toString(Object obj) {
        return gson.toJson(obj);
    }

    /**
     * @param type 类型反射(Class<?>)或反射令牌(TypeToken)
     * @return Object
     * @throws
     * @Description: 将标准JSON字符串反序列化为对象
     */
    public static Object toObject(String jsonString, Object type) {
        jsonString = jsonString.replace("&nbsp", "");
        jsonString = jsonString.replace("﹠nbsp", "");
        jsonString = jsonString.replace("nbsp", "");
        jsonString = jsonString.replace("&amp;", "");
        jsonString = jsonString.replace("&amp", "");
        jsonString = jsonString.replace("amp", "");
        if (type instanceof Type) {
            try {
                return gson.fromJson(jsonString, (Type) type);
            } catch (JsonSyntaxException e) {
                e.printStackTrace();
                return null;
            }
        } else if (type instanceof Class<?>) {
            try {
                return gson.fromJson(jsonString, (Class<?>) type);
            } catch (JsonSyntaxException e) {
                e.printStackTrace();
                return null;
            }
        } else {
            throw new RuntimeException("只能是Class<?>或者通过TypeToken获取的Type类型");
        }
    }
}
 public class CommentListActivity extends BaseActivity implements LoadResultCallBack {

    @InjectView(R.id.swipe_refresh)
    SwipeRefreshLayout mSwipeRefreshLayout;
    @InjectView(R.id.recycler_view)
    RecyclerView mRecyclerView;
    @InjectView(R.id.toolbar)
    Toolbar mToolbar;
    @InjectView(R.id.loading)
    RotateLoading loading;

    private String thread_key;
    private String thread_id;
    private boolean isFromFreshNews;
    private CommentAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_comment_list);
        initView();
        initData();
    }

    @Override
    public void initView() {
        ButterKnife.inject(this);

        mToolbar.setTitleTextColor(Color.WHITE);
        setSupportActionBar(mToolbar);
        mToolbar.setTitle("评论");
        mToolbar.setNavigationIcon(R.drawable.ic_actionbar_back);

        mRecyclerView.setHasFixedSize(false);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

        mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
                android.R.color.holo_green_light,
                android.R.color.holo_orange_light,
                android.R.color.holo_red_light);

        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                if (isFromFreshNews) {
                    mAdapter.loadData4FreshNews();
                } else {
                    mAdapter.loadData();
                }
            }
        });
    }

    @Override
    public void initData() {
        thread_key = getIntent().getStringExtra(DATA_THREAD_KEY);
        thread_id = getIntent().getStringExtra(DATA_THREAD_ID);
        isFromFreshNews = getIntent().getBooleanExtra(DATA_IS_FROM_FRESH_NEWS, false);

        if (isFromFreshNews) {
            mAdapter = new CommentAdapter(this, thread_id, isFromFreshNews, this);
            if (TextUtils.isEmpty(thread_id) || thread_id.equals("0")) {
                ShowToast.Short(FORBID_COMMENTS);
                finish();
            }
        } else {
            mAdapter = new CommentAdapter(this, thread_key, isFromFreshNews, this);
            if (TextUtils.isEmpty(thread_key) || thread_key.equals("0")) {
                ShowToast.Short(FORBID_COMMENTS);
                finish();
            }
        }
        mRecyclerView.setAdapter(mAdapter);
        if (isFromFreshNews) {
            mAdapter.loadData4FreshNews();
        } else {
            mAdapter.loadData();
        }
        loading.start();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            if (isFromFreshNews) {
                mAdapter.loadData4FreshNews();
            } else {
                mAdapter.loadData();
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_comment_list, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                finish();
                return true;
            case R.id.action_edit:
                Intent intent = new Intent(this, PushCommentActivity.class);
                intent.putExtra(DATA_THREAD_ID, mAdapter.getThreadId());
                startActivityForResult(intent, 100);
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onSuccess(int result, Object object) {
        if (result == LoadResultCallBack.SUCCESS_NONE) {
            ShowToast.Short(NO_COMMENTS);
        }
        loading.stop();
        mSwipeRefreshLayout.setRefreshing(false);
    }

    @Override
    public void onError(int code, String msg) {
        mSwipeRefreshLayout.setRefreshing(false);
       loading.stop();
        ShowToast.Short(LOAD_FAILED);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mAdapter.cancelAllRequest();
    }
} public class ShareUtil {

    public static void sharePicture(Activity activity, String url) {

        String[] urlSplits = url.split("\\.");

        File cacheFile = ImageLoader.getInstance().getDiskCache().get(url);

        //如果不存在，则使用缩略图进行分享
        if (!cacheFile.exists()) {
            String picUrl = url;
            picUrl = picUrl.replace("mw600", "small").replace("mw1200", "small").replace
                    ("large", "small");
            cacheFile = ImageLoader.getInstance().getDiskCache().get(picUrl);
        }

        File newFile = new File(CacheUtil.getSharePicName
                (cacheFile, urlSplits));

        if (FileUtil.copyTo(cacheFile, newFile)) {
            ShareUtil.sharePicture(activity, newFile.getAbsolutePath(),
                    "分享自煎蛋 " + url);
        } else {
            ShowToast.Short(ConstantString.SHARE_FAILED);
        }
    }


    public static void shareText(Activity activity, String shareText) {

        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType("text/plain");
        intent.putExtra(Intent.EXTRA_TEXT,
                shareText);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        activity.startActivity(Intent.createChooser(intent, activity.getResources().getString(R
                .string.app_name)));
    }

    public static void sharePicture(Activity activity, String imgPath, String shareText) {

        Intent intent = new Intent(Intent.ACTION_SEND);
        File f = new File(imgPath);
        if (f != null && f.exists() && f.isFile()) {
            intent.setType("image/*");
            intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
        } else {
            ShowToast.Short("分享图片不存在哦");
            return;
        }

        //GIF图片指明出处url，其他图片指向项目地址
        if (imgPath.endsWith(".gif")) {
            intent.putExtra(Intent.EXTRA_TEXT, shareText);
        }

        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        activity.startActivity(Intent.createChooser(intent, activity.getResources().getString(R
                .string.app_name)));
    }


} public class ShowToast {

    public static void Short(@NonNull CharSequence sequence) {
        Toast.makeText(JDApplication.getContext(), sequence, Toast.LENGTH_SHORT).show();
    }

    public static void Long(@NonNull CharSequence sequence) {
        Toast.makeText(JDApplication.getContext(), sequence, Toast.LENGTH_SHORT).show();
    }

}

import android.app.Activity;
import android.content.Intent;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
import com.loopj.android.http.RequestHandle;
import com.socks.jiandan.R;
import com.socks.jiandan.async.Handler4CommentNumber;
import com.socks.jiandan.async.Handler4Joke;
import com.socks.jiandan.async.HttpClientProxy;
import com.socks.jiandan.base.ConstantString;
import com.socks.jiandan.base.JDApplication;
import com.socks.jiandan.cache.JokeCache;
import com.socks.jiandan.callback.LoadFinishCallBack;
import com.socks.jiandan.callback.LoadResultCallBack;
import com.socks.jiandan.callback.OnHttpResponseCallBackImpl;
import com.socks.jiandan.model.CommentNumber;
import com.socks.jiandan.model.Joke;
import com.socks.jiandan.utils.JSONParser;
import com.socks.jiandan.ui.CommentListActivity;
import com.socks.jiandan.utils.NetWorkUtil;
import com.socks.jiandan.utils.ShareUtil;
import com.socks.jiandan.utils.ShowToast;
import com.socks.jiandan.utils.String2TimeUtil;
import com.socks.jiandan.utils.TextUtil;
import java.util.ArrayList;
                        .items(R.array.joke_dialog)
                        .backgroundColor(mActivity.getResources().getColor(JDApplication.COLOR_OF_DIALOG))
                        .contentColor(JDApplication.COLOR_OF_DIALOG_CONTENT)
                        .itemsCallback(new MaterialDialog.ListCallback() {
                            @Override
                            public void onSelection(MaterialDialog dialog, View view, int which, CharSequence text) {

                                switch (which) {
                                    //分享
                                    case 0:
                                        ShareUtil.shareText(mActivity, joke.getComment_content().trim());
                                        break;
                                    //复制
                                    case 1:
                                        TextUtil.copy(mActivity, joke.getComment_content());
                                        break;
                                }

                            }
                        }).show();
            }
        });

        holder.ll_comment.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(mActivity, CommentListActivity.class);
                intent.putExtra("thread_key", "comment-" + joke.getComment_ID());
                mActivity.startActivity(intent);
            }
        });

        setAnimation(holder.card, position);

    }

    @Override
    public int getItemCount() {
        return mJokes.size();
    }

    public void loadFirst() {
        page = 1;
        loadDataByNetworkType();
    }

    public void loadNextPage() {
        page++;
        loadDataByNetworkType();
    }

    private void loadDataByNetworkType() {

        if (NetWorkUtil.isNetWorkConnected(mActivity)) {
            loadData();
        } else {
            loadCache();
        }

    }

    private void loadData() {

        RequestHandle requestHandle = HttpClientProxy.get(mActivity,Joke.getRequestUrl(page),new Handler4Joke(new OnHttpResponseCallBackImpl<ArrayList<Joke>>() {
            @Override
            public void onSuccess(int statusCode, ArrayList<Joke> jokes) {
                getCommentCounts(jokes);
            }

            @Override
            public void onFailure(int statusCode, Throwable throwable) {
                mLoadFinisCallBack.loadFinish(null);
            }
        }));

        mRequestHandles.add(requestHandle);
    }

    private void loadCache() {
        mLoadFinisCallBack.loadFinish(null);
        mLoadResultCallBack.onSuccess(LoadResultCallBack.SUCCESS_OK, null);
        JokeCache jokeCacheUtil = JokeCache.getInstance(mActivity);
        if (page == 1) {
            mJokes.clear();
            ShowToast.Short(ConstantString.LOAD_NO_NETWORK);
        }
        mJokes.addAll(jokeCacheUtil.getCacheByPage(page));
        notifyDataSetChanged();
    }

    private void getCommentCounts(final ArrayList<Joke> jokes) {

        StringBuilder sb = new StringBuilder();
        for (Joke joke : jokes) {
            sb.append("comment-" + joke.getComment_ID() + ",");
        }

        String url = sb.toString();
        if (url.endsWith(",")) {
            url = url.substring(0, url.length() - 1);
        }


        RequestHandle requestHandle = HttpClientProxy.get(mActivity, CommentNumber.getCommentCountsURL(url), new Handler4CommentNumber(new OnHttpResponseCallBackImpl<ArrayList<CommentNumber>>() {
            @Override
            public void onSuccess(int statusCode, ArrayList<CommentNumber> commentNumbers) {
                for (int i = 0; i < jokes.size(); i++) {
                    jokes.get(i).setComment_counts(commentNumbers.get(i).getComments() + "");
                }

                if (page == 1) {
                    mJokes.clear();
                    //首次正常加载之后，清空之前的缓存
                    JokeCache.getInstance(mActivity).clearAllCache();
                }

                mJokes.addAll(jokes);
                notifyDataSetChanged();

                //加载完毕后缓存
                JokeCache.getInstance(mActivity).addResultCache(JSONParser.toString(jokes),