package ml.puredark.hviewer.ui.fragments; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.JavascriptInterface; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.AbsListView; import com.umeng.analytics.MobclickAgent; import com.wuxiaolong.pullloadmorerecyclerview.PullLoadMoreRecyclerView; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; import ml.puredark.hviewer.HViewerApplication; import ml.puredark.hviewer.R; import ml.puredark.hviewer.beans.Collection; import ml.puredark.hviewer.beans.Rule; import ml.puredark.hviewer.beans.Site; import ml.puredark.hviewer.beans.Tag; import ml.puredark.hviewer.core.RuleParser; import ml.puredark.hviewer.dataholders.SiteTagHolder; import ml.puredark.hviewer.download.DownloadManager; import ml.puredark.hviewer.helpers.FileHelper; import ml.puredark.hviewer.helpers.Logger; import ml.puredark.hviewer.http.HViewerHttpClient; import ml.puredark.hviewer.ui.activities.BaseActivity; import ml.puredark.hviewer.ui.activities.CollectionActivity; import ml.puredark.hviewer.ui.adapters.CollectionAdapter; import ml.puredark.hviewer.ui.customs.AutoFitGridLayoutManager; import ml.puredark.hviewer.ui.customs.WrappedLinearLayoutManager; import ml.puredark.hviewer.ui.customs.WrappedStaggeredGridLayoutManager; import ml.puredark.hviewer.ui.dataproviders.ListDataProvider; import ml.puredark.hviewer.utils.DensityUtil; import ml.puredark.hviewer.utils.SimpleFileUtil; public class CollectionFragment extends MyFragment { @BindView(R.id.rv_collection) PullLoadMoreRecyclerView rvCollection; CollectionAdapter adapter; private WebView mWebView; private RecyclerView.LayoutManager mLinearLayoutManager, mGridLayoutManager; private BaseActivity activity; private Site site; private SiteTagHolder siteTagHolder; private String currUrl = null; private String keyword = null; private int clickPos = 0; private boolean onePage = false; private int startPage; private int currPage; private boolean noTouch = false; // 记录使用js进行scroll的次数,每5秒检测一次,如果没有获得新item,则+1,达到3次判定已获取完毕 private int scrollTimes = 0; public CollectionFragment() { } public static CollectionFragment newInstance(Site site, SiteTagHolder siteTagHolder) { CollectionFragment fragment = new CollectionFragment(); fragment.site = site; fragment.siteTagHolder = siteTagHolder; return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_collection, container, false); ButterKnife.bind(this, rootView); if (getActivity() instanceof BaseActivity) activity = (BaseActivity) getActivity(); if (site != null && site.hasFlag(Site.FLAG_WATERFALL_AS_LIST)) mLinearLayoutManager = new WrappedStaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); else mLinearLayoutManager = new WrappedLinearLayoutManager(this.getContext(), LinearLayoutManager.VERTICAL, false); if (site != null && site.hasFlag(Site.FLAG_WATERFALL_AS_GRID)) mGridLayoutManager = new WrappedStaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); else mGridLayoutManager = new AutoFitGridLayoutManager(this.getContext(), DensityUtil.dp2px(this.getContext(), 100)); if (site != null && (site.hasFlag(Site.FLAG_JS_NEEDED_ALL) || site.hasFlag(Site.FLAG_JS_NEEDED_INDEX))) { mWebView = new WebView(getContext()); WebSettings mWebSettings = mWebView.getSettings(); mWebSettings.setJavaScriptEnabled(true); mWebSettings.setBlockNetworkImage(true); mWebSettings.setDomStorageEnabled(false); mWebSettings.setUseWideViewPort(true); mWebSettings.setLoadWithOverviewMode(true); mWebSettings.setUserAgentString(getResources().getString(R.string.UA)); mWebSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); mWebView.addJavascriptInterface(this, "HtmlParser"); ((ViewGroup) rootView.findViewById(R.id.content)).addView(mWebView); mWebView.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT; mWebView.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; mWebView.requestLayout(); mWebView.setVisibility(View.INVISIBLE); } List<Collection> collections = new ArrayList<>(); ListDataProvider<Collection> dataProvider = new ListDataProvider<>(collections); adapter = new CollectionAdapter(getContext(), dataProvider, siteTagHolder); rvCollection.setAdapter(adapter); Bundle bundle = getArguments(); boolean isGrid = (bundle == null) ? false : bundle.getBoolean("isGrid", false); if (isGrid) setRecyclerViewToGrid(); else setRecyclerViewToList(); rvCollection.setPullRefreshEnable(true); rvCollection.getRecyclerView().setClipToPadding(false); rvCollection.getRecyclerView().setPadding(0, DensityUtil.dp2px(getActivity(), 8), 0, DensityUtil.dp2px(getActivity(), 8)); rvCollection.getRecyclerView().addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (activity == null || !activity.isInOneHandMode()) return; switch (newState) { case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: activity.setDrawerEnabled(false); break; case AbsListView.OnScrollListener.SCROLL_STATE_FLING: case AbsListView.OnScrollListener.SCROLL_STATE_IDLE: activity.setDrawerEnabled(true); break; } } }); rvCollection.getRecyclerView().setOnTouchListener((v, event) -> noTouch); //下拉刷新和加载更多 rvCollection.setOnPullLoadMoreListener(new PullLoadMoreRecyclerView.PullLoadMoreListener() { @Override public void onRefresh() { currPage = startPage; rvCollection.setRefreshing(true); activity.setDrawerEnabled(true); getCollections(keyword, currPage); } @Override public void onLoadMore() { activity.setDrawerEnabled(true); getCollections(keyword, currPage + 1); } }); if (site != null) { adapter.setSite(site); } adapter.setOnItemClickListener(new CollectionAdapter.OnItemClickListener() { @Override public void onItemClick(View v, int position) { Collection collection = (Collection) adapter.getDataProvider().getItem(position); HViewerApplication.temp = site; HViewerApplication.temp2 = collection; Intent intent = new Intent(CollectionFragment.this.getContext(), CollectionActivity.class); startActivity(intent); clickPos = position; } @Override public boolean onItemLongClick(View v, int position) { return false; } }); return rootView; } public void toggleWebView() { if (mWebView != null) { if (mWebView.getVisibility() == View.VISIBLE) { mWebView.setVisibility(View.INVISIBLE); activity.setDrawerEnabled(true); } else { mWebView.setVisibility(View.VISIBLE); activity.setDrawerEnabled(false); } } } private void getCollections(String keyword, final int page) { if (onePage && page > startPage && !site.hasFlag(Site.FLAG_JS_SCROLL)) { // 如果URL中根本没有page参数的位置,则肯定只有1页,无需多加载一次 new Handler().postDelayed(() -> rvCollection.setPullLoadMoreCompleted(), 250); noTouch = false; return; } this.keyword = keyword; if (keyword == null) keyword = ""; if (currUrl == null || site == null) return; final String url = site.getListUrl(currUrl, page, keyword, adapter.getDataProvider().getItems()); Logger.d("CollectionFragment", url); //如果需要执行JS才能获取完整数据,则不得不使用webView来载入页面 if (site.hasFlag(Site.FLAG_JS_NEEDED_ALL) || site.hasFlag(Site.FLAG_JS_NEEDED_INDEX)) { mWebView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { //Load HTML mWebView.loadUrl("javascript:window.HtmlParser.onResultGot(document.documentElement.outerHTML, '" + url + "', " + page + ");"); Logger.d("CollectionFragment", "onPageFinished"); } }); if (site.hasFlag(Site.FLAG_JS_SCROLL) && page != startPage && mWebView.getUrl().equals(url)) { Logger.d("CollectionFragment", "FLAG_JS_SCROLL"); mWebView.loadUrl("javascript:document.body.scrollTop = document.body.scrollHeight;"); new Handler().postDelayed(() -> { scrollTimes++; mWebView.loadUrl("javascript:window.HtmlParser.onResultGot(document.documentElement.outerHTML, '" + url + "', " + page + ");"); Logger.d("CollectionFragment", "onAjaxFinished"); }, 5000); } else { mWebView.loadUrl(url); new Handler().postDelayed(() -> mWebView.stopLoading(), 30000); } Logger.d("CollectionFragment", "WebView"); } else HViewerHttpClient.get(url, site.disableHProxy, site.getHeaders(), site.hasFlag(Site.FLAG_POST_INDEX) || site.hasFlag(Site.FLAG_POST_ALL), new HViewerHttpClient.OnResponseListener() { @Override public void onSuccess(String contentType, final Object result) { if (!(result instanceof String)) return; String html = (String) result; onResultGot(html, url, page); } @Override public void onFailure(HViewerHttpClient.HttpError error) { BaseActivity activity = (BaseActivity) getActivity(); if (activity != null) activity.showSnackBar(error.getErrorString()); rvCollection.setPullLoadMoreCompleted(); noTouch = false; } }); } @JavascriptInterface public void onResultGot(String html, String url, int page) { if (page == startPage) { int preSize = adapter.getItemCount(); if (preSize > 0) { adapter.getDataProvider().clear(); adapter.notifyItemRangeRemoved(0, preSize); } } noTouch = true; new Thread(() -> { if (HViewerApplication.DEBUG) FileHelper.writeString(html, "html.txt", DownloadManager.getDownloadPath()); final Rule rule; rule = (keyword != null && site.searchRule != null && site.searchRule.item != null) ? site.searchRule : site.indexRule; List<Collection> newCollections = RuleParser.getCollections(new ArrayList<>(), html, rule, url); Logger.d("CollectionFragment", "newCollections.size():" + newCollections.size()); List<Collection> collections = adapter.getDataProvider().getItems(); Logger.d("CollectionFragment", "collections.size():" + collections.size()); if (site.hasFlag(Site.FLAG_EXTRA_INDEX_INFO) && site.extraRule != null) { List<Collection> extraCollections = RuleParser.getCollections(new ArrayList<>(), html, site.extraRule, url); for (int i = 0; i < extraCollections.size() && i < newCollections.size(); i++) { newCollections.get(i).fillEmpty(extraCollections.get(i)); } } int oldSize = collections.size(); Logger.d("CollectionFragment", "oldSize():" + oldSize); for (Collection newCollection : newCollections) { if (!TextUtils.isEmpty(newCollection.idCode) && !collections.contains(newCollection)) { newCollection.cid = collections.size() + 1; collections.add(newCollection); } } Logger.d("CollectionFragment", "newSize():" + collections.size()); if (collections.size() > oldSize || scrollTimes == 3) { currPage = page; scrollTimes = 0; addSearchSuggestions(collections, oldSize); new Handler(Looper.getMainLooper()).post(() -> { adapter.notifyItemRangeInserted(oldSize, collections.size() - oldSize); rvCollection.setPullLoadMoreCompleted(); noTouch = false; }); } else if (site.hasFlag(Site.FLAG_JS_SCROLL) && mWebView != null) { new Handler(Looper.getMainLooper()).postDelayed(() -> { scrollTimes++; mWebView.loadUrl("javascript:window.HtmlParser.onResultGot(document.documentElement.outerHTML, '" + url + "', " + page + ");"); Logger.d("CollectionFragment", "onAjaxFinished"); }, 5000); } else { scrollTimes = 0; new Handler(Looper.getMainLooper()).post(() -> { rvCollection.setPullLoadMoreCompleted(); noTouch = false; }); } }).start(); } private void addSearchSuggestions(List<Collection> collections, int start) { for (int i = start; i < collections.size(); i++) { Collection collection = collections.get(i); if (collection.tags != null) { for (Tag tag : collection.tags) { HViewerApplication.searchSuggestionHolder.addSearchSuggestion(tag.title); siteTagHolder.addTag(site.sid, tag); } } } } @Override public void onResume() { super.onResume(); if (clickPos >= 0 && clickPos < adapter.getItemCount()) adapter.notifyItemChanged(clickPos); if (site != null) MobclickAgent.onPageStart(site.title); } @Override public void onPause() { super.onPause(); if (clickPos >= 0 && clickPos < adapter.getItemCount()) adapter.notifyItemChanged(clickPos); if (site != null) MobclickAgent.onPageEnd(site.title); } @Override public void onAttach(Context context) { super.onAttach(context); } @Override public void onDetach() { super.onDetach(); } @Override public Site getCurrSite() { return site; } @Override public void onSearch(String keyword) { if (site == null || site.searchUrl == null || "".equals(site.searchUrl)) { BaseActivity activity = (BaseActivity) getActivity(); if (activity != null) activity.showSnackBar("该站点不支持搜索,试试单选TAG"); return; } try { keyword = URLEncoder.encode(keyword, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } Logger.d("CollectionFragment", " keyword:" + keyword); currUrl = site.searchUrl; parseUrl(currUrl); rvCollection.setRefreshing(true); getCollections(keyword, startPage); } private void parseUrl(String url) { String pageStr = RuleParser.parseUrl(url).get("page"); try { if (pageStr == null) { onePage = true; startPage = 0; } else { onePage = false; String[] pageStrs = pageStr.split(":"); if (pageStrs.length > 1) { startPage = Integer.parseInt(pageStrs[0]); } else { startPage = Integer.parseInt(pageStr); } } currPage = startPage; } catch (NumberFormatException e) { startPage = 0; currPage = startPage; } } @Override public void onLoadUrl(String url) { currUrl = url; if (rvCollection != null) { parseUrl(currUrl); rvCollection.setRefreshing(true); getCollections(null, startPage); } else { new Handler().postDelayed(() -> { if (rvCollection != null) { parseUrl(currUrl); rvCollection.setRefreshing(true); getCollections(null, startPage); } }, 300); } } @Override public void onJumpToPage(int page) { rvCollection.setRefreshing(true); adapter.getDataProvider().clear(); getCollections(keyword, page); } @Override public void setRecyclerViewToList() { adapter.setIsGrid(false); rvCollection.getRecyclerView().setLayoutManager(mLinearLayoutManager); } @Override public void setRecyclerViewToGrid() { adapter.setIsGrid(true); rvCollection.getRecyclerView().setLayoutManager(mGridLayoutManager); } }