package ml.puredark.hviewer.ui.activities; import android.app.Dialog; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Animatable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.provider.DocumentFile; import android.support.v4.util.Pair; import android.support.v4.view.ViewPager; import android.support.v7.app.AlertDialog; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.webkit.JavascriptInterface; import android.webkit.MimeTypeMap; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.facebook.common.logging.FLog; import com.facebook.common.references.CloseableReference; import com.facebook.datasource.BaseDataSubscriber; import com.facebook.datasource.DataSource; import com.facebook.drawee.controller.BaseControllerListener; import com.facebook.drawee.view.DraweeView; import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.image.ImageInfo; import com.facebook.imagepipeline.memory.PooledByteBuffer; import com.umeng.analytics.MobclickAgent; import net.rdrei.android.dirchooser.DirectoryChooserConfig; import net.rdrei.android.dirchooser.DirectoryChooserFragment; import java.io.ByteArrayOutputStream; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; 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.Picture; import ml.puredark.hviewer.beans.Selector; import ml.puredark.hviewer.beans.Site; import ml.puredark.hviewer.core.RuleParser; import ml.puredark.hviewer.download.DownloadManager; import ml.puredark.hviewer.helpers.FileHelper; import ml.puredark.hviewer.helpers.Logger; import ml.puredark.hviewer.helpers.MDStatusBarCompat; import ml.puredark.hviewer.http.HViewerHttpClient; import ml.puredark.hviewer.http.ImageLoader; import ml.puredark.hviewer.ui.adapters.PicturePagerAdapter; import ml.puredark.hviewer.ui.adapters.PictureViewerAdapter; import ml.puredark.hviewer.ui.customs.AreaClickHelper; import ml.puredark.hviewer.ui.customs.MultiTouchViewPager; import ml.puredark.hviewer.ui.dataproviders.ListDataProvider; import ml.puredark.hviewer.ui.fragments.SettingFragment; import ml.puredark.hviewer.ui.listeners.OnItemLongClickListener; import ml.puredark.hviewer.utils.DensityUtil; import ml.puredark.hviewer.utils.FileType; import ml.puredark.hviewer.utils.FileUtils; import ml.puredark.hviewer.utils.RegexValidateUtil; import ml.puredark.hviewer.utils.SharedPreferencesUtil; import ml.puredark.hviewer.utils.SimpleFileUtil; import static android.webkit.WebSettings.LOAD_CACHE_ELSE_NETWORK; import static ml.puredark.hviewer.ui.fragments.SettingFragment.DIREACTION_LEFT_TO_RIGHT; import static ml.puredark.hviewer.ui.fragments.SettingFragment.DIREACTION_RIGHT_TO_LEFT; import static ml.puredark.hviewer.ui.fragments.SettingFragment.DIREACTION_TOP_TO_BOTTOM; public class PictureViewerActivity extends BaseActivity { public final static int RESULT_CHOOSE_DIRECTORY = 1; private static int ACTION_SAVE = 0; private static int ACTION_SHARE = 1; private static int ACTION_SHOW_INFO = 2; @BindView(R.id.container) LinearLayout container; @BindView(R.id.tv_count) TextView tvCount; @BindView(R.id.view_pager) MultiTouchViewPager viewPager; @BindView(R.id.rv_picture) RecyclerView rvPicture; @BindView(R.id.bottom_bar) LinearLayout bottomBar; @BindView(R.id.btn_load_high_res) ImageView btnLoadHighRes; @BindView(R.id.btn_rotate_screen) ImageView btnRotateScreen; @BindView(R.id.btn_picture_info) ImageView btnPictureInfo; InfoDialogViewHolder viewHolder; private boolean volumeKeyEnabled = false; private String viewDirection = DIREACTION_LEFT_TO_RIGHT; private CollectionActivity collectionActivity; private PicturePagerAdapter picturePagerAdapter; private PictureViewerAdapter pictureViewerAdapter; private Site site = null; private Collection collection = null; private List<Picture> pictures = null; private MyOnItemLongClickListener onItemLongClickListener; private int currPos = 0; private boolean move; private int mIndex; private Map<Integer, Pair<Picture, Object>> pictureInQueue = new HashMap<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_picture_viewer); ButterKnife.bind(this); MDStatusBarCompat.setImageTransparent(this); setContainer(container); // 关闭边缘滑动返回 setSwipeBackEnable(false); if (HViewerApplication.temp instanceof CollectionActivity) collectionActivity = (CollectionActivity) HViewerApplication.temp; if (HViewerApplication.temp2 instanceof Site) site = (Site) HViewerApplication.temp2; if (HViewerApplication.temp3 instanceof Collection) collection = (Collection) HViewerApplication.temp3; if (HViewerApplication.temp4 instanceof List) pictures = (List<Picture>) HViewerApplication.temp4; if (site == null || collection == null || pictures == null) { Toast.makeText(this, "数据错误,请刷新后重试", Toast.LENGTH_SHORT).show(); finish(); return; } if (collectionActivity != null) collectionActivity.setPictureViewerActivity(this); HViewerApplication.temp = null; HViewerApplication.temp2 = null; HViewerApplication.temp3 = null; HViewerApplication.temp4 = null; volumeKeyEnabled = (boolean) SharedPreferencesUtil.getData(this, SettingFragment.KEY_PREF_VIEW_VOLUME_FLICK, true); currPos = getIntent().getIntExtra("position", 0); onItemLongClickListener = new MyOnItemLongClickListener(); initViewDirection(); initBottomBar(); } private void initViewDirection() { viewDirection = (String) SharedPreferencesUtil.getData(this, SettingFragment.KEY_PREF_VIEW_DIRECTION, DIREACTION_LEFT_TO_RIGHT); if (!DIREACTION_LEFT_TO_RIGHT.equals(viewDirection) && !DIREACTION_RIGHT_TO_LEFT.equals(viewDirection) && !DIREACTION_TOP_TO_BOTTOM.equals(viewDirection)) viewDirection = DIREACTION_LEFT_TO_RIGHT; if (DIREACTION_LEFT_TO_RIGHT.equals(viewDirection) || DIREACTION_RIGHT_TO_LEFT.equals(viewDirection)) { viewPager.setVisibility(View.VISIBLE); rvPicture.setVisibility(View.GONE); picturePagerAdapter = new PicturePagerAdapter(this, site, pictures); picturePagerAdapter.setViewDirection(viewDirection); picturePagerAdapter.setOnItemLongClickListener(onItemLongClickListener); picturePagerAdapter.setAreaClickListener(new AreaClickHelper.OnLeftRightClickListener() { @Override public void left() { if (DIREACTION_LEFT_TO_RIGHT.equals(viewDirection)) prevPage(false); else nextPage(false); } @Override public void right() { if (DIREACTION_LEFT_TO_RIGHT.equals(viewDirection)) nextPage(false); else prevPage(false); } @Override public void center() { toogleStatus(); if (isStatusBarEnabled()) { Animation animation = AnimationUtils.loadAnimation(PictureViewerActivity.this, R.anim.bottom_bar_show_from_bottom); animation.setFillAfter(true); bottomBar.startAnimation(animation); } else { Animation animation = AnimationUtils.loadAnimation(PictureViewerActivity.this, R.anim.bottom_bar_hide_to_bottom); animation.setFillAfter(true); bottomBar.startAnimation(animation); } } }); int position = picturePagerAdapter.getPicturePostion(currPos); tvCount.setText((position + 1) + "/" + picturePagerAdapter.getCount()); if (position < pictures.size() && pictures.size() > 0) { Picture picture = (position < 0) ? pictures.get(0) : pictures.get(position); if (TextUtils.isEmpty(picture.highRes) || picture.loadedHighRes) btnLoadHighRes.setVisibility(View.GONE); else btnLoadHighRes.setVisibility(View.VISIBLE); } viewPager.setAdapter(picturePagerAdapter); ViewPager.OnPageChangeListener listener = new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { currPos = position; position = picturePagerAdapter.getPicturePostion(currPos); tvCount.setText((position + 1) + "/" + picturePagerAdapter.getCount()); if (position < pictures.size()) { Picture picture = pictures.get(position); if (TextUtils.isEmpty(picture.highRes) || picture.loadedHighRes) btnLoadHighRes.setVisibility(View.GONE); else btnLoadHighRes.setVisibility(View.VISIBLE); } } @Override public void onPageScrollStateChanged(int state) { } }; viewPager.addOnPageChangeListener(listener); int limit = (int) SharedPreferencesUtil.getData(this, SettingFragment.KEY_PREF_VIEW_PRELOAD_PAGES, 2); viewPager.setOffscreenPageLimit(limit); viewPager.setCurrentItem(position); } else if (DIREACTION_TOP_TO_BOTTOM.equals(viewDirection)) { viewPager.setVisibility(View.GONE); rvPicture.setVisibility(View.VISIBLE); ListDataProvider<Picture> dataProvider = new ListDataProvider<>(pictures); pictureViewerAdapter = new PictureViewerAdapter(this, site, dataProvider); pictureViewerAdapter.setOnItemLongClickListener(onItemLongClickListener); rvPicture.setAdapter(pictureViewerAdapter); rvPicture.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); //在这里进行第二次滚动(最后的100米!) if (move) { move = false; //获取要置顶的项在当前屏幕的位置,mIndex是记录的要置顶项在RecyclerView中的位置 int n = mIndex - linearLayoutManager.findFirstVisibleItemPosition(); if (0 <= n && n < rvPicture.getChildCount()) { //获取要置顶的项顶部离RecyclerView顶部的距离 int top = rvPicture.getChildAt(n).getTop(); //最后的移动 rvPicture.scrollBy(0, top); } } currPos = linearLayoutManager.findLastVisibleItemPosition(); tvCount.setText((currPos + 1) + "/" + pictureViewerAdapter.getItemCount()); Picture picture = pictures.get(currPos); if (TextUtils.isEmpty(picture.highRes) || picture.loadedHighRes) btnLoadHighRes.setVisibility(View.GONE); else btnLoadHighRes.setVisibility(View.VISIBLE); } }); moveToPosition(rvPicture, currPos); LinearLayoutManager linearLayoutManager = (LinearLayoutManager) rvPicture.getLayoutManager(); currPos = linearLayoutManager.findLastVisibleItemPosition(); tvCount.setText((currPos + 1) + "/" + pictureViewerAdapter.getItemCount()); if (currPos < pictures.size() && pictures.size() > 0) { Picture picture = (currPos < 0) ? pictures.get(0) : pictures.get(currPos); if (TextUtils.isEmpty(picture.highRes) || picture.loadedHighRes) btnLoadHighRes.setVisibility(View.GONE); else btnLoadHighRes.setVisibility(View.VISIBLE); } } } private void initBottomBar() { View view = LayoutInflater.from(this).inflate(R.layout.dialog_picture_exif, null); viewHolder = new InfoDialogViewHolder(view); Dialog dialog = new AlertDialog.Builder(PictureViewerActivity.this) .setView(view) .create(); dialog.setCanceledOnTouchOutside(true); //设置对话框位置 WindowManager.LayoutParams lp = dialog.getWindow().getAttributes(); lp.width = DensityUtil.getScreenWidth(this) - DensityUtil.dp2px(this, 64); dialog.getWindow().setAttributes(lp); viewHolder.btnConfirm.setOnClickListener(v -> dialog.dismiss()); btnLoadHighRes.setOnClickListener(v -> { if (pictures != null && currPos >= 0 && currPos < pictures.size()) { Picture picture = null; if (picturePagerAdapter != null) { int position = picturePagerAdapter.getPicturePostion(currPos); if (position < pictures.size()) { picture = pictures.get(position); PicturePagerAdapter.PictureViewHolder picVH = picturePagerAdapter.getViewHolderAt(currPos); getUrlAndLoadImage(picVH, picture, true); btnLoadHighRes.setVisibility(View.GONE); } else { showSnackBar("无法加载原图"); return; } } else if (pictureViewerAdapter != null) { if (pictures.size() > 0) { for (int pos = 0; pos < pictures.size(); pos++) { picture = pictures.get(pos); picture.loadedHighRes = true; } btnLoadHighRes.setVisibility(View.GONE); pictureViewerAdapter.notifyDataSetChanged(); } else { showSnackBar("无法加载原图"); return; } } } }); btnRotateScreen.setOnClickListener(v -> { int width = DensityUtil.getScreenWidth(this); int height = DensityUtil.getScreenHeight(this); if (height > width) setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); else setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); }); btnPictureInfo.setOnClickListener(v -> { viewHolder.tvImageType.setText(""); viewHolder.tvFileSize.setText(""); viewHolder.tvImageSize.setText(""); if (pictures != null && currPos >= 0 && currPos <= pictures.size()) { Picture picture = null; int position = 0; if (picturePagerAdapter != null) { position = picturePagerAdapter.getPicturePostion(currPos); } else if (pictureViewerAdapter != null) { position = currPos; } if (position < pictures.size()) { picture = pictures.get(position); } else { showSnackBar("图片未加载,请等待"); return; } if (picture.loadedHighRes) viewHolder.iconHighRes.setVisibility(View.VISIBLE); else viewHolder.iconHighRes.setVisibility(View.GONE); loadPicture(picture, "", ACTION_SHOW_INFO); dialog.show(); } }); } private void moveToPosition(RecyclerView recyclerView, int n) { mIndex = n; LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); //先从RecyclerView的LayoutManager中获取第一项和最后一项的Position int firstItem = linearLayoutManager.findFirstVisibleItemPosition(); int lastItem = linearLayoutManager.findLastVisibleItemPosition(); //然后区分情况 if (n <= firstItem) { //当要置顶的项在当前显示的第一个项的前面时 recyclerView.scrollToPosition(n); } else if (n <= lastItem) { //当要置顶的项已经在屏幕上显示时 int top = recyclerView.getChildAt(n - firstItem).getTop(); recyclerView.scrollBy(0, top); } else { //当要置顶的项在当前显示的最后一项的后面时 recyclerView.scrollToPosition(n); //这里这个变量是用在RecyclerView滚动监听里面的 move = true; } } public void notifyDataSetChanged(List<Picture> pictures) { this.pictures = pictures; if (picturePagerAdapter != null) { picturePagerAdapter.pictures = pictures; picturePagerAdapter.notifyDataSetChanged(); tvCount.setText((picturePagerAdapter.getPicturePostion(currPos) + 1) + "/" + picturePagerAdapter.getCount()); } if (pictureViewerAdapter != null) { pictureViewerAdapter.getDataProvider().setDataSet(pictures); pictureViewerAdapter.notifyDataSetChanged(); tvCount.setText((currPos + 1) + "/" + pictureViewerAdapter.getItemCount()); } } private boolean viewHighRes() { return (boolean) SharedPreferencesUtil.getData(this, SettingFragment.KEY_PREF_VIEW_HIGH_RES, false); } public void getUrlAndLoadImage(Object viewHolder, Picture picture, boolean loadHighRes) { if (picture.pic != null) { loadImage(picture, viewHolder, loadHighRes); } else if (site.hasFlag(Site.FLAG_SINGLE_PAGE_BIG_PICTURE) && site.extraRule != null) { if (site.extraRule.pictureRule != null && site.extraRule.pictureRule.url != null) getPictureUrl(viewHolder, picture, site.extraRule.pictureRule.url, site.extraRule.pictureRule.highRes); else if (site.extraRule.pictureUrl != null) getPictureUrl(viewHolder, picture, site.extraRule.pictureUrl, site.extraRule.pictureHighRes); } else if (site.picUrlSelector != null) { getPictureUrl(viewHolder, picture, site.picUrlSelector, null); } else { picture.pic = picture.url; loadImage(picture, viewHolder, loadHighRes); } } public void loadImage(Picture picture, final Object viewHolder, boolean loadHighRes) { String url = ((loadHighRes || picture.loadedHighRes || viewHighRes()) && !TextUtils.isEmpty(picture.highRes)) ? picture.highRes : picture.pic; if (url.equals(picture.highRes)) picture.loadedHighRes = true; if (site.hasFlag(Site.FLAG_SINGLE_PAGE_BIG_PICTURE)) picture.referer = RegexValidateUtil.getHostFromUrl(site.galleryUrl); Logger.d("PictureViewerActivity", "url:" + url + "\n picture.referer:" + picture.referer); if (site == null) return; DraweeView draweeView; if (viewHolder instanceof PicturePagerAdapter.PictureViewHolder) { draweeView = ((PicturePagerAdapter.PictureViewHolder) viewHolder).ivPicture; } else if (viewHolder instanceof PictureViewerAdapter.PictureViewHolder) { draweeView = ((PictureViewerAdapter.PictureViewHolder) viewHolder).ivPicture; } else return; ImageLoader.loadImageFromUrl(this, draweeView, url, site.cookie, picture.referer, new BaseControllerListener<ImageInfo>() { @Override public void onSubmit(String id, Object callerContext) { super.onSubmit(id, callerContext); if (viewHolder instanceof PicturePagerAdapter.PictureViewHolder) { PicturePagerAdapter.PictureViewHolder pictureViewHolder = (PicturePagerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.progressBar.setVisibility(View.VISIBLE); pictureViewHolder.btnRefresh.setVisibility(View.GONE); } else if (viewHolder instanceof PictureViewerAdapter.PictureViewHolder) { PictureViewerAdapter.PictureViewHolder pictureViewHolder = (PictureViewerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.progressBar.setVisibility(View.VISIBLE); pictureViewHolder.btnRefresh.setVisibility(View.GONE); } } @Override public void onFinalImageSet(String id, @Nullable ImageInfo imageInfo, @Nullable Animatable anim) { super.onFinalImageSet(id, imageInfo, anim); if (imageInfo == null) { return; } if (viewHolder instanceof PicturePagerAdapter.PictureViewHolder) { PicturePagerAdapter.PictureViewHolder pictureViewHolder = (PicturePagerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.progressBar.setVisibility(View.GONE); pictureViewHolder.btnRefresh.setVisibility(View.GONE); pictureViewHolder.ivPicture.update(imageInfo.getWidth(), imageInfo.getHeight()); } else if (viewHolder instanceof PictureViewerAdapter.PictureViewHolder) { PictureViewerAdapter.PictureViewHolder pictureViewHolder = (PictureViewerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.progressBar.setVisibility(View.GONE); pictureViewHolder.btnRefresh.setVisibility(View.GONE); WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); final float factor = (float) imageInfo.getHeight() / imageInfo.getWidth(); final int originWidth = wm.getDefaultDisplay().getWidth(); final int originHeight = (int) (factor * originWidth); pictureViewHolder.ivPicture.getLayoutParams().height = originHeight; pictureViewHolder.ivPicture.requestLayout(); pictureViewHolder.ivPicture.update(imageInfo.getWidth(), imageInfo.getHeight()); pictureViewHolder.ivPicture.setOnScaleChangeListener((scaleFactor, focusX, focusY) -> { float scale = pictureViewHolder.ivPicture.getScale(); if (scale > 1) { pictureViewHolder.ivPicture.getLayoutParams().height = (int) (scale * originHeight) + 1; } else { pictureViewHolder.ivPicture.getLayoutParams().height = originHeight; } pictureViewHolder.ivPicture.requestLayout(); }); pictureViewHolder.ivPicture.setAllowParentInterceptOnEdge(true); } } @Override public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) { } @Override public void onFailure(String id, Throwable throwable) { FLog.e(getClass(), throwable, "Error loading %s", id); if (viewHolder instanceof PicturePagerAdapter.PictureViewHolder) { PicturePagerAdapter.PictureViewHolder pictureViewHolder = (PicturePagerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.progressBar.setVisibility(View.GONE); pictureViewHolder.btnRefresh.setVisibility(View.VISIBLE); } else if (viewHolder instanceof PictureViewerAdapter.PictureViewHolder) { PictureViewerAdapter.PictureViewHolder pictureViewHolder = (PictureViewerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.progressBar.setVisibility(View.GONE); pictureViewHolder.btnRefresh.setVisibility(View.VISIBLE); } } }); } public void getPictureUrl(final Object viewHolder, final Picture picture, final Selector selector, final Selector highResSelector) { Logger.d("PictureViewerActivity", "picture.url = " + picture.url); if (Picture.hasPicPosfix(picture.url)) { picture.pic = picture.url; loadImage(picture, viewHolder, false); } else //如果需要执行JS才能获取完整数据,则不得不使用webView来载入页面 if (site.hasFlag(Site.FLAG_JS_NEEDED_ALL) || site.hasFlag(Site.FLAG_JS_NEEDED_PICTURE)) { WebView webView = new WebView(this); WebSettings mWebSettings = webView.getSettings(); mWebSettings.setJavaScriptEnabled(true); mWebSettings.setBlockNetworkImage(true); mWebSettings.setDomStorageEnabled(true); mWebSettings.setUserAgentString(getResources().getString(R.string.UA)); mWebSettings.setCacheMode(LOAD_CACHE_ELSE_NETWORK); webView.addJavascriptInterface(this, "HtmlParser"); webView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { //Load HTML pictureInQueue.put(picture.pid, new Pair<>(picture, viewHolder)); boolean extra = !selector.equals(site.picUrlSelector); webView.loadUrl("javascript:window.HtmlParser.onResultGot(document.documentElement.outerHTML, " + picture.pid + ", " + extra + ");"); Logger.d("PictureViewerActivity", "onPageFinished"); } }); webView.loadUrl(picture.url); new Handler().postDelayed(() -> webView.stopLoading(), 30000); Logger.d("PictureViewerActivity", "WebView"); } else HViewerHttpClient.get(picture.url, site.disableHProxy, site.getHeaders(), site.hasFlag(Site.FLAG_POST_PICTURE), new HViewerHttpClient.OnResponseListener() { @Override public void onSuccess(String contentType, Object result) { if (result == null || result.equals("")) return; if (contentType.contains("image")) { picture.pic = picture.url; if (result instanceof Bitmap) { if (viewHolder instanceof PicturePagerAdapter.PictureViewHolder) { PicturePagerAdapter.PictureViewHolder pictureViewHolder = (PicturePagerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.ivPicture.setImageBitmap((Bitmap) result); pictureViewHolder.progressBar.setVisibility(View.GONE); } else if (viewHolder instanceof PictureViewerAdapter.PictureViewHolder) { PictureViewerAdapter.PictureViewHolder pictureViewHolder = (PictureViewerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.ivPicture.setImageBitmap((Bitmap) result); pictureViewHolder.progressBar.setVisibility(View.GONE); } } else { loadImage(picture, viewHolder, false); } } else { picture.pic = RuleParser.getPictureUrl((String) result, selector, picture.url); picture.highRes = RuleParser.getPictureUrl((String) result, highResSelector, picture.url); Logger.d("PictureViewerActivity", "getPictureUrl: picture.pic: " + picture.pic); Logger.d("PictureViewerActivity", "getPictureUrl: picture.highRes: " + picture.highRes); if (picture.pic != null) { picture.retries = 0; picture.referer = picture.url; loadImage(picture, viewHolder, false); } else { onFailure(null); } } } @Override public void onFailure(HViewerHttpClient.HttpError error) { if (picture.retries < 15) { picture.retries++; getPictureUrl(viewHolder, picture, selector, highResSelector); } else { picture.retries = 0; if (viewHolder instanceof PicturePagerAdapter.PictureViewHolder) { PicturePagerAdapter.PictureViewHolder pictureViewHolder = (PicturePagerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.progressBar.setVisibility(View.GONE); pictureViewHolder.btnRefresh.setVisibility(View.VISIBLE); } else if (viewHolder instanceof PictureViewerAdapter.PictureViewHolder) { PictureViewerAdapter.PictureViewHolder pictureViewHolder = (PictureViewerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.progressBar.setVisibility(View.GONE); pictureViewHolder.btnRefresh.setVisibility(View.VISIBLE); } } } }); } @JavascriptInterface public void onResultGot(String html, int pid, boolean extra) { Pair<Picture, Object> pair = pictureInQueue.get(pid); if (pair == null) return; Picture picture = pair.first; Object viewHolder = pair.second; if (picture == null || viewHolder == null) return; pictureInQueue.remove(pid); Selector selector = (extra) ? (site.extraRule.pictureRule != null) ? site.extraRule.pictureRule.url : site.extraRule.pictureUrl : site.picUrlSelector; Selector highResSelector = (extra) ? (site.extraRule.pictureRule != null) ? site.extraRule.pictureRule.highRes : site.extraRule.pictureHighRes : null; picture.pic = RuleParser.getPictureUrl(html, selector, picture.url); picture.highRes = RuleParser.getPictureUrl(html, highResSelector, picture.url); Logger.d("PicturePagerAdapter", "getPictureUrl: picture.pic: " + picture.pic); Logger.d("PicturePagerAdapter", "getPictureUrl: picture.highRes: " + picture.highRes); if (picture.pic != null) { picture.retries = 0; picture.referer = picture.url; new Handler(Looper.getMainLooper()).post(() -> loadImage(picture, viewHolder, false)); } else { new Handler(Looper.getMainLooper()).post(() -> { if (picture.retries < 15) { picture.retries++; getPictureUrl(viewHolder, picture, selector, highResSelector); } else { picture.retries = 0; if (viewHolder instanceof PicturePagerAdapter.PictureViewHolder) { PicturePagerAdapter.PictureViewHolder pictureViewHolder = (PicturePagerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.progressBar.setVisibility(View.GONE); pictureViewHolder.btnRefresh.setVisibility(View.VISIBLE); } else if (viewHolder instanceof PictureViewerAdapter.PictureViewHolder) { PictureViewerAdapter.PictureViewHolder pictureViewHolder = (PictureViewerAdapter.PictureViewHolder) viewHolder; pictureViewHolder.progressBar.setVisibility(View.GONE); pictureViewHolder.btnRefresh.setVisibility(View.VISIBLE); } } }); } } private void loadPicture(final Picture picture, final String path, int action) { String url = (picture.loadedHighRes && !TextUtils.isEmpty(picture.highRes)) ? picture.highRes : picture.pic; if (url != null && (url.startsWith("file://"))) { if (action == ACTION_SHARE) { Uri uri = Uri.parse(url); Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, uri); shareIntent.setType("image/*"); startActivity(Intent.createChooser(shareIntent, "将图片分享到")); MobclickAgent.onEvent(this, "ShareSinglePicture"); } else if (action == ACTION_SHOW_INFO) { Uri uri = Uri.parse(url); viewHolder.tvImageType.setText(FileUtils.getMimeType(this, uri)); viewHolder.tvFileSize.setText(FileUtils.getReadableFileSize((int) SimpleFileUtil.getFileSize(new File(uri.getPath())))); BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; opts.inSampleSize = 1; BitmapFactory.decodeFile(uri.getPath(), opts); int width = opts.outWidth; int height = opts.outHeight; viewHolder.tvImageSize.setText(width + " × " + height); } } else if (url != null && url.startsWith("content://")) { ImageLoader.loadBitmapFromUrl(this, url, site.cookie, picture.referer, new BaseBitmapDataSubscriber() { @Override protected void onNewResultImpl(Bitmap bitmap) { Logger.d("PictureViewerActivity", "onNewResultImpl"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); byte[] bytes = baos.toByteArray(); if (action == ACTION_SAVE) savePicture(path, bytes, false); else if (action == ACTION_SHARE) savePicture(path, bytes, true); else if (action == ACTION_SHOW_INFO) { runOnUiThread(() -> { String postfix = FileType.getFileType(bytes, FileType.TYPE_IMAGE); viewHolder.tvImageType.setText(MimeTypeMap.getSingleton().getMimeTypeFromExtension(postfix)); viewHolder.tvFileSize.setText(FileUtils.getReadableFileSize(bytes.length)); int width = bitmap.getWidth(); int height = bitmap.getHeight(); viewHolder.tvImageSize.setText(width + " × " + height); }); } } @Override protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) { Logger.d("PictureViewerActivity", "onFailureImpl"); } }); } else { ImageLoader.loadResourceFromUrl(this, url, site.cookie, picture.referer, new BaseDataSubscriber<CloseableReference<PooledByteBuffer>>() { @Override protected void onNewResultImpl(DataSource<CloseableReference<PooledByteBuffer>> dataSource) { Logger.d("PictureViewerActivity", "onNewResultImpl"); if (!dataSource.isFinished()) { return; } CloseableReference<PooledByteBuffer> ref = dataSource.getResult(); if (ref != null) { try { PooledByteBuffer imageBuffer = ref.get(); byte[] bytes = new byte[imageBuffer.size()]; imageBuffer.read(0, bytes, 0, imageBuffer.size()); if (action == ACTION_SAVE) savePicture(path, bytes, false); else if (action == ACTION_SHARE) savePicture(path, bytes, true); else if (action == ACTION_SHOW_INFO) { runOnUiThread(() -> { String postfix = FileType.getFileType(bytes, FileType.TYPE_IMAGE); viewHolder.tvImageType.setText(MimeTypeMap.getSingleton().getMimeTypeFromExtension(postfix)); viewHolder.tvFileSize.setText(FileUtils.getReadableFileSize(bytes.length)); Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); if (bitmap != null) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); viewHolder.tvImageSize.setText(width + " × " + height); } }); } } finally { CloseableReference.closeSafely(ref); } } } @Override protected void onFailureImpl(DataSource<CloseableReference<PooledByteBuffer>> dataSource) { Logger.d("PictureViewerActivity", "onFailureImpl"); } } ); } } private void savePicture(String path, byte[] bytes, boolean share) { try { String postfix = FileType.getFileType(bytes, FileType.TYPE_IMAGE); String fileName; if (share) { if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) path = Environment.getExternalStorageDirectory().getAbsolutePath(); fileName = "tempImage"; } else { int i = 1; do { fileName = Uri.encode(site.title + "_" + FileHelper.filenameFilter(collection.idCode) + "_" + (i++) + "." + postfix); } while (FileHelper.isFileExist(fileName, path)); } DocumentFile documentFile = FileHelper.createFileIfNotExist(fileName, path); if (FileHelper.writeBytes(bytes, documentFile)) { if (share) { Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, documentFile.getUri()); shareIntent.setType("image/*"); startActivity(Intent.createChooser(shareIntent, "将图片分享到")); MobclickAgent.onEvent(this, "ShareSinglePicture"); } else { showSnackBar("保存成功"); // 统计保存单图次数 MobclickAgent.onEvent(this, "SaveSinglePicture"); } } else { showSnackBar("保存失败,请重新设置下载目录"); } } catch (OutOfMemoryError error) { showSnackBar("保存失败,内存不足"); } } // 监听音量键,实现翻页 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (volumeKeyEnabled) switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: nextPage(false); return true; case KeyEvent.KEYCODE_VOLUME_UP: prevPage(false); return true; } return super.onKeyDown(keyCode, event); } // 监听音量键,消除按键音 @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (volumeKeyEnabled) switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: return true; case KeyEvent.KEYCODE_VOLUME_UP: return true; } return super.onKeyUp(keyCode, event); } /** * 屏幕旋转时调用此方法 */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (picturePagerAdapter != null) picturePagerAdapter.onConfigurationChanged(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) { if (requestCode == RESULT_CHOOSE_DIRECTORY) { Uri uriTree = data.getData(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { try { getContentResolver().takePersistableUriPermission( uriTree, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } catch (SecurityException e) { e.printStackTrace(); } } onItemLongClickListener.onSelectDirectory(uriTree); } } } private void prevPage(boolean anim) { if (DIREACTION_LEFT_TO_RIGHT.equals(viewDirection) && picturePagerAdapter != null) { int currItem = viewPager.getCurrentItem(); if (currItem > 0) viewPager.setCurrentItem(currItem - 1, anim); } else if (DIREACTION_RIGHT_TO_LEFT.equals(viewDirection) && picturePagerAdapter != null) { int currItem = viewPager.getCurrentItem(); if (currItem + 1 < viewPager.getAdapter().getCount()) viewPager.setCurrentItem(currItem + 1, anim); } else if (DIREACTION_TOP_TO_BOTTOM.equals(viewDirection) && pictureViewerAdapter != null) { LinearLayoutManager layoutManager = (LinearLayoutManager) rvPicture.getLayoutManager(); int firstItemPosition = layoutManager.findFirstVisibleItemPosition(); if (firstItemPosition > 0) { moveToPosition(rvPicture, firstItemPosition - 1); } } } private void nextPage(boolean anim) { if (DIREACTION_LEFT_TO_RIGHT.equals(viewDirection) && picturePagerAdapter != null) { int currItem = viewPager.getCurrentItem(); if (currItem + 1 < viewPager.getAdapter().getCount()) viewPager.setCurrentItem(currItem + 1, anim); } else if (DIREACTION_RIGHT_TO_LEFT.equals(viewDirection) && picturePagerAdapter != null) { int currItem = viewPager.getCurrentItem(); if (currItem > 0) viewPager.setCurrentItem(currItem - 1, anim); } else if (DIREACTION_TOP_TO_BOTTOM.equals(viewDirection) && pictureViewerAdapter != null) { LinearLayoutManager layoutManager = (LinearLayoutManager) rvPicture.getLayoutManager(); int firstItemPosition = layoutManager.findFirstVisibleItemPosition(); if (firstItemPosition + 1 < pictureViewerAdapter.getItemCount()) { moveToPosition(rvPicture, firstItemPosition + 1); } } } public static class InfoDialogViewHolder { @BindView(R.id.tv_image_type) TextView tvImageType; @BindView(R.id.tv_file_size) TextView tvFileSize; @BindView(R.id.tv_image_size) TextView tvImageSize; @BindView(R.id.btn_confirm) TextView btnConfirm; @BindView(R.id.icon_high_res) ImageView iconHighRes; InfoDialogViewHolder(View view) { ButterKnife.bind(this, view); } } private class MyOnItemLongClickListener implements OnItemLongClickListener { private DirectoryChooserFragment mDialog; private String lastPath = DownloadManager.getDownloadPath(); private Picture pictureToBeSaved; private DirectoryChooserFragment.OnFragmentInteractionListener onFragmentInteractionListener = new DirectoryChooserFragment.OnFragmentInteractionListener() { @Override public void onSelectDirectory(@NonNull String path) { if (pictureToBeSaved == null) return; lastPath = path; loadPicture(pictureToBeSaved, path, ACTION_SAVE); mDialog.dismiss(); } @Override public void onCancelChooser() { mDialog.dismiss(); } }; public void onSelectDirectory(Uri rootUri) { String path = rootUri.toString(); if (pictureToBeSaved == null) return; lastPath = path; loadPicture(pictureToBeSaved, path, ACTION_SAVE); } @Override public boolean onItemLongClick(View view, int position) { if (!(position >= 0 && position < pictures.size())) return false; pictureToBeSaved = pictures.get(position); new AlertDialog.Builder(PictureViewerActivity.this) .setTitle("操作") .setItems(new String[]{"保存", "分享"}, (dialogInterface, i) -> { if (i == 0) { new AlertDialog.Builder(PictureViewerActivity.this).setTitle("是否直接保存到下载目录?") .setMessage("或者另存到其他目录") .setPositiveButton("保存", (dialog1, which1) -> onSelectDirectory(Uri.parse(DownloadManager.getDownloadPath()))) .setNegativeButton("选择目录", (dialog12, which12) -> { final DirectoryChooserConfig config = DirectoryChooserConfig.builder() .initialDirectory(lastPath) .newDirectoryName("download") .allowNewDirectoryNameModification(true) .build(); mDialog = DirectoryChooserFragment.newInstance(config); mDialog.setDirectoryChooserListener(onFragmentInteractionListener); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); try { startActivityForResult(intent, PictureViewerActivity.RESULT_CHOOSE_DIRECTORY); } catch (ActivityNotFoundException e) { e.printStackTrace(); mDialog.show(getFragmentManager(), null); } } else { mDialog.show(getFragmentManager(), null); } }).show(); } else if (i == 1) { loadPicture(pictureToBeSaved, DownloadManager.getDownloadPath(), ACTION_SHARE); } }) .setNegativeButton("取消", null) .show(); return true; } } }