public class AvatarsAdapter extends BaseRcvAdapter<String> {

    private final DisplayImageOptions mDisplayImageOptions;

    private boolean mIsNoPictureMode;

    public AvatarsAdapter(Context context, List<String> data, boolean isNoPictureMode) {
        super(context, data);

        mIsNoPictureMode = isNoPictureMode;

        mDisplayImageOptions = new DisplayImageOptions.Builder()
                .showImageOnLoading(R.drawable.editor_profile_avatar)
                .showImageOnFail(R.drawable.editor_profile_avatar)
                .cacheInMemory(false)
                .cacheOnDisk(true)
                .bitmapConfig(Bitmap.Config.RGB_565)
                .build();
    }

    @Override
    protected RecyclerView.ViewHolder onCreateAdapterViewHolder(LayoutInflater inflater, ViewGroup parent, int viewType) {
        return new AvatarViewHolder(mLayoutInflater.inflate(R.layout.item_image, parent, false));
    }

    @Override
    protected void onBindAdapterViewHolder(RecyclerView.ViewHolder holder, int position, String str) {
        AvatarViewHolder avatarViewHolder = (AvatarViewHolder) holder;
        if (!mIsNoPictureMode) {
            ImageLoader.getInstance().displayImage(str, avatarViewHolder.mAvatarView, mDisplayImageOptions);
        }
    }

    @Override
    protected int getAdapterItemViewType(int position, String s) {
        return 0;
    }

    static class AvatarViewHolder extends RecyclerView.ViewHolder {

        CircleImageView mAvatarView;

        public AvatarViewHolder(View itemView) {
            super(itemView);
            mAvatarView = (CircleImageView) itemView.findViewById(R.id.iv_avatar);
        }
    }
} public class LogUtil {
    public static final int VERBOSE = 1;
    public static final int DEBUG = 2;
    public static final int INFO = 3;
    public static final int WARN = 4;
    public static final int ERROR = 5;
    public static final int NOTHING = 6;
    public static final int LEVEL = VERBOSE;

    public static void v(String tag, String msg) {
        if (LEVEL <= VERBOSE) {
            Log.v(tag, msg);
        }
    }

    public static void d(String tag, String msg) {
        if (LEVEL <= DEBUG) {
            Log.d(tag, msg);
        }
    }

    public static void i(String tag, String msg) {
        if (LEVEL <= INFO) {
            Log.i(tag, msg);
        }
    }

    public static void w(String tag, String msg) {
        if (LEVEL < WARN) {
            Log.w(tag, msg);
        }
    }

    public static void e(String tag, String msg) {
        if (LEVEL < ERROR) {
            Log.e(tag, msg);
        }
    }
} public class NetworkHelper {

    private static ConnectivityManager sConnectivityManager;

    private static NetworkHelper sNetworkHelper;

    private NetworkHelper(Context context) {
        sConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    }

    public static NetworkHelper getInstance(Context context) {
        if (sNetworkHelper == null) {
            synchronized (NetworkHelper.class) {
                if (sNetworkHelper == null) {
                    sNetworkHelper = new NetworkHelper(context);
                }
            }
        }
        return sNetworkHelper;
    }

    public boolean isNetworkAvailable() {
        if (sConnectivityManager.getActiveNetworkInfo() != null
                && sConnectivityManager.getActiveNetworkInfo().isAvailable()) {
            return true;
        }
        return false;
    }

    public int getNetworkType() {
        return sConnectivityManager.getActiveNetworkInfo().getType();
    }

    public boolean isMobieNetwork() {
        if (isNetworkAvailable()
                && ConnectivityManager.TYPE_MOBILE == getNetworkType()) {
            return true;
        }
        return false;
    }
} public class PreferenceHelper {
    private static PreferenceHelper sPreferenceHelper;

    private Context mAppContext;
    private SharedPreferences mPreferences;

    private PreferenceHelper(Context context) {
        mAppContext = context.getApplicationContext();
        mPreferences = PreferenceManager.getDefaultSharedPreferences(mAppContext);
    }

    public static PreferenceHelper getInstance(Context context) {
        if (sPreferenceHelper == null) {
            synchronized (PreferenceHelper.class) {
                if (sPreferenceHelper == null) {
                    sPreferenceHelper = new PreferenceHelper(context);
                }
            }
        }
        return sPreferenceHelper;
    }

    public boolean isNoPictureEnabled() {
        String key = mAppContext.getResources().getString(R.string.pref_key_no_picture_mode);
        return mPreferences.getBoolean(key, false);
    }
} public class ZHLinearLayout extends LinearLayout {
    public ZHLinearLayout(Context context) {
        this(context, null);
    }

    public ZHLinearLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ZHLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return true;
    }
} public class MemberEntity {
    private String zhihu_url_token;
    private String name;
    private String bio;
    private int id;
    private String avatar;

    public void setUrlToken(String urlToken) {
        this.zhihu_url_token = urlToken;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setBio(String bio) {
        this.bio = bio;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setAvatar(String avatar) {
        this.avatar = avatar;
    }

    public String getUrlToken() {
        return zhihu_url_token;
    }

    public String getName() {
        return name;
    }

    public String getBio() {
        return bio;
    }

    public int getId() {
        return id;
    }

    public String getAvatar() {
        return avatar;
    }
} public class StoryDetailEntity {

    private String image;
    private List<String> css;
    private String share_url;
    private String ga_prefix;
    private List<String> js;
    private SectionEntity section;
    private int id;
    private String body;
    private String title;
    private List<MemberEntity> recommenders;
    private int type;
    private String image_source;

    public void setImage(String image) {
        this.image = image;
    }

    public void setCss(List<String> css) {
        this.css = css;
    }

    public void setShare_url(String share_url) {
        this.share_url = share_url;
    }

    public void setGa_prefix(String ga_prefix) {
        this.ga_prefix = ga_prefix;
    }

    public void setJs(List<String> js) {
        this.js = js;
    }

    public void setSection(SectionEntity section) {
        this.section = section;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setRecommenders(List<MemberEntity> recommenders) {
        this.recommenders = recommenders;
    }

    public void setType(int type) {
        this.type = type;
    }

    public void setImage_source(String image_source) {
        this.image_source = image_source;
    }

    public String getImage() {
        return image;
    }

    public List<String> getCss() {
        return css;
    }

    public String getShare_url() {
        return share_url;
    }

    public String getGa_prefix() {
        return ga_prefix;
    }

    public List<String> getJs() {
        return js;
    }

    public SectionEntity getSection() {
        return section;
    }

    public int getId() {
        return id;
    }

    public String getBody() {
        return body;
    }

    public String getTitle() {
        return title;
    }

    public List<MemberEntity> getRecommenders() {
        return recommenders;
    }

    public int getType() {
        return type;
    }

    public String getImage_source() {
        return image_source;
    }

    public class SectionEntity {
        private String thumbnail;
        private String name;
        private int id;

        public void setThumbnail(String thumbnail) {
            this.thumbnail = thumbnail;
        }

        public void setName(String name) {
            this.name = name;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getThumbnail() {
            return thumbnail;
        }

        public String getName() {
            return name;
        }

        public int getId() {
            return id;
        }
    }
} public class FetchDetailTask extends FetchTask<StoryDetailEntity> {

    private static final String URL_STORY_DETAIL = "http://news.at.zhihu.com/api/4/news/";

    public FetchDetailTask(Context context, int storyId, boolean fetchFromNetwork) {
        super(context, URL_STORY_DETAIL + storyId, fetchFromNetwork);
    }

    @Override
    protected void parseResponse(String response, FetchCallback<StoryDetailEntity> callback) {
        StoryDetailEntity entity = new Gson().fromJson(response, StoryDetailEntity.class);
        if (callback != null) {
            callback.onFetchResponse(entity);
        }
    }
} public abstract class FetchTask<T> {
    private final String TAG = "FetchTask";
    private Context mContext;
    private String mUrl;
    private boolean mFetchFromNetworkFirst;
    private boolean mCacheOnDisk;

    protected VolleyUtils mVolleyUtils;
    protected DiskLruCache mDiskLruCache;

    protected String mCacheKey;

    protected Request mRequest;

    public FetchTask(Context context, String url, boolean fetchFromNetworkFirst, boolean cacheOnDisk) {
        mContext = context;
        mUrl = url;
        mFetchFromNetworkFirst = fetchFromNetworkFirst;
        mCacheOnDisk = cacheOnDisk;

        mVolleyUtils = VolleyUtils.getInstance(App.getContext());
        mDiskLruCache = ZHStorageUtils.getFilesDiskCache(context);
        mCacheKey = ZHStorageUtils.hashKeyForDisk(mUrl);
    }

    public FetchTask(Context context, String url, boolean fetchFromNetworkFirst) {
        this(context, url, fetchFromNetworkFirst, true);
    }

    public void execute(FetchCallback<T> callback) {
        String cacheKey = ZHStorageUtils.hashKeyForDisk(mUrl);

        boolean hasCacheFile = ZHStorageUtils.hasCacheInDisk(mDiskLruCache, cacheKey);

        if (mFetchFromNetworkFirst && NetworkHelper.getInstance(mContext).isNetworkAvailable() || !hasCacheFile) {
            fetchFromNetwork(mUrl, callback);
        } else {
            fetchFromeDiskCache(mCacheKey, callback);
        }
    }

    private void fetchFromNetwork(String url, final FetchCallback<T> callback) {
        mRequest = new StringRequest(
                url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        if (TextUtils.isEmpty(response)) {
                            return;
                        }
                        if (mCacheOnDisk) {
                            cacheToDisk(response);
                            fetchFromeDiskCache(mCacheKey, callback);
                        } else {
                            parseResponse(response, callback);
                        }
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {
                        LogUtil.d(TAG, volleyError.toString());
                    }
                }
        );

        mVolleyUtils.addRequest(mRequest);
    }

    private void cacheToDisk(String response) {
        if (mDiskLruCache == null || mDiskLruCache.isClosed()) {
            mDiskLruCache = ZHStorageUtils.getFilesDiskCache(App.getContext());
        }

        try {
            DiskLruCache.Editor editor = mDiskLruCache.edit(mCacheKey);
            editor.set(0, response);
            editor.commit();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    protected void fetchFromeDiskCache(String cacheKey, FetchCallback<T> callback) {

        if (mDiskLruCache == null || mDiskLruCache.isClosed()) {
            mDiskLruCache = ZHStorageUtils.getFilesDiskCache(App.getContext());
        }

        String cacheStr = null;
        try {
            DiskLruCache.Snapshot snapshot = mDiskLruCache.get(cacheKey);
            if (snapshot != null) {

                InputStream in = snapshot.getInputStream(0);
                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                StringBuilder buffer = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null){
                    buffer.append(line);
                }
                cacheStr = buffer.toString();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (!TextUtils.isEmpty(cacheStr) && callback != null) {
            parseResponse(cacheStr, callback);
        }
    }

    protected abstract void parseResponse(String response, FetchCallback<T> callback);

    public interface FetchCallback<T> {
        void onFetchResponse(T t);
    }

    public void cancel() {
        if (mRequest != null) {
            mRequest.cancel();
        }
    }
} public class DisplayUtils {

    public static int getStatusBarHeight(Context context) {
        int result = 0;
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = context.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }
}
import android.app.Activity;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v4.widget.NestedScrollView;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import butterknife.ButterKnife;
import butterknife.InjectView;
import io.gitcafe.zhanjiashu.newzhihudialy.R;
import io.gitcafe.zhanjiashu.newzhihudialy.adapter.AvatarsAdapter;
import io.gitcafe.zhanjiashu.common.util.LogUtil;
import io.gitcafe.zhanjiashu.newzhihudialy.util.NetworkHelper;
import io.gitcafe.zhanjiashu.newzhihudialy.util.PreferenceHelper;
import io.gitcafe.zhanjiashu.newzhihudialy.widget.ZHLinearLayout;
import io.gitcafe.zhanjiashu.newzhihudialy.model.MemberEntity;
import io.gitcafe.zhanjiashu.newzhihudialy.model.StoryDetailEntity;
import io.gitcafe.zhanjiashu.newzhihudialy.task.FetchDetailTask;
import io.gitcafe.zhanjiashu.newzhihudialy.task.FetchTask;
import io.gitcafe.zhanjiashu.common.util.DisplayUtils;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setHasOptionsMenu(true);
        PreferenceHelper helper = PreferenceHelper.getInstance(getActivity());
        mIsNoPictureMode = helper.isNoPictureEnabled() && NetworkHelper.getInstance(getActivity()).isMobieNetwork();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_detail, container, false);
        ButterKnife.inject(this, view);

        setupView();

        return view;
    }

    private void setupView() {

        final AppCompatActivity appCompatActivity = (AppCompatActivity) getActivity();
        appCompatActivity.setSupportActionBar(mToolbar);
        ActionBar actionBar = appCompatActivity.getSupportActionBar();

        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
        }

        intiNestedScrollView();

        mImageOptions = new DisplayImageOptions.Builder()
                .cacheInMemory(false)
                .cacheOnDisk(true)
                .bitmapConfig(Bitmap.Config.RGB_565)
                .build();

        Bundle arguments = getArguments();
        if (arguments != null) {
            int storyId = arguments.getInt(KEY_STORY_ID);
            FetchDetailTask task= new FetchDetailTask(getActivity(), storyId, false);
            task.execute(new FetchTask.FetchCallback<StoryDetailEntity>() {
                @Override
                public void onFetchResponse(StoryDetailEntity storyDetailEntity) {
                    showStoryDetail(storyDetailEntity);
                }
            });
        }

        mActionbarSizeTypedArray.recycle();
    }

    /**
     * 在布局文件中如果对 NestedScrollView 设置了
     *      app:layout_behavior="@string/appbar_scrolling_view_behavior" ，
     * 将会导致作为其 子View 的 WebView 无法正常显示。这个可能是一个 Bug, 通过本方法可以解决。
     */
    private void intiNestedScrollView() {
        DisplayMetrics metrics = new DisplayMetrics();
        getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);

        int statusHeight = DisplayUtils.getStatusBarHeight(getActivity());

        mActionbarSizeTypedArray = getActivity().obtainStyledAttributes(new int[]{android.R.attr.actionBarSize});
        int actionBarHeight = (int) mActionbarSizeTypedArray.getDimension(0, 0);

        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) mScrollView.getLayoutParams();
        params.height = metrics.heightPixels - statusHeight - actionBarHeight;
        mScrollView.setLayoutParams(params);
    }

    private void showStoryDetail(final StoryDetailEntity entity) {

        mToolbarLayout.setTitle(entity.getTitle());
        if (!mIsNoPictureMode) {
            ImageLoader.getInstance().displayImage(entity.getImage(), mTopView, mImageOptions);
        }
        List<MemberEntity> recommenderEntities = entity.getRecommenders();
        if (recommenderEntities != null && recommenderEntities.size() > 0) {
            List<String> avatarList = new ArrayList<>();
            for (MemberEntity recommenderEntity : recommenderEntities) {
                avatarList.add(recommenderEntity.getAvatar());
            }
            if (avatarList.size() > 0) {
                mRecommendersLayout.setVisibility(View.VISIBLE);
                LinearLayoutManager manager = new LinearLayoutManager(getActivity());
                manager.setOrientation(LinearLayoutManager.HORIZONTAL);
                mRecyclerView.setLayoutManager(manager);
                AvatarsAdapter adapter = new AvatarsAdapter(getActivity(), avatarList, mIsNoPictureMode);
                mRecyclerView.setAdapter(adapter);
            }
        }

        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);

        String contentBody = entity.getBody();
        if (!TextUtils.isEmpty(contentBody)) {
            String html = formatHtml(entity.getCss(), contentBody);
            mWebView.loadDataWithBaseURL(null, html, "text/html", "utf-8", null);
        } else {
            mWebView.setWebViewClient(new WebViewClient() {
                @Override
                public boolean shouldOverrideUrlLoading(WebView view, String url) {
                    view.loadUrl(url);
                    return true;
                }
            });
            mWebView.loadUrl(entity.getShare_url());
        }

        mShareBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                shareText(getActivity(), entity.getTitle() + " " + entity.getShare_url());
            }
        });
    }

    private String formatHtml(List<String> cssList, String body) {
        LogUtil.d(TAG, "内容长度" + body.length());