/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Piasy
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.piasy.biv.view;
import android.Manifest;
import android.content.Context;
import android.content.res.TypedArray;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.annotation.RequiresPermission;
import android.support.annotation.UiThread;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.widget.FrameLayout;
import com.davemorrissey.labs.subscaleview.ImageSource;
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
import com.github.piasy.biv.BigImageViewer;
import com.github.piasy.biv.R;
import com.github.piasy.biv.indicator.ProgressIndicator;
import com.github.piasy.biv.loader.ImageLoader;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Piasy{github.com/Piasy} on 06/11/2016.
*
* Use FrameLayout for extensibility.
*/
public class BigImageView extends FrameLayout implements ImageLoader.Callback {
public static final int INIT_SCALE_TYPE_CENTER_INSIDE = 1;
public static final int INIT_SCALE_TYPE_CENTER_CROP = 2;
public static final int INIT_SCALE_TYPE_AUTO = 3;
private final SubsamplingScaleImageView mImageView;
private final ImageLoader mImageLoader;
private final List<File> mTempImages;
private View mProgressIndicatorView;
private View mThumbnailView;
private ImageSaveCallback mImageSaveCallback;
private File mCurrentImageFile;
private Uri mThumbnail;
private ProgressIndicator mProgressIndicator;
private final ProgressNotifyRunnable mProgressNotifyRunnable
= new ProgressNotifyRunnable() {
@Override
public void run() {
if (mProgressIndicator != null) {
mProgressIndicator.onProgress(mProgress);
notified();
}
}
};
private DisplayOptimizeListener mDisplayOptimizeListener;
private int mInitScaleType;
private boolean mOptimizeDisplay;
public BigImageView(Context context) {
this(context, null);
}
public BigImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BigImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.getTheme()
.obtainStyledAttributes(attrs, R.styleable.BigImageView, defStyleAttr, 0);
mInitScaleType = array.getInteger(R.styleable.BigImageView_initScaleType,
INIT_SCALE_TYPE_CENTER_INSIDE);
mOptimizeDisplay = array.getBoolean(R.styleable.BigImageView_optimizeDisplay, true);
array.recycle();
mImageView = new SubsamplingScaleImageView(context, attrs);
addView(mImageView);
LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mImageView.setLayoutParams(params);
mImageView.setMinimumTileDpi(160);
setOptimizeDisplay(mOptimizeDisplay);
setInitScaleType(mInitScaleType);
mImageLoader = BigImageViewer.imageLoader();
mTempImages = new ArrayList<>();
}
@Override
public void setOnClickListener(OnClickListener listener) {
mImageView.setOnClickListener(listener);
}
@Override
public void setOnLongClickListener(OnLongClickListener listener) {
mImageView.setOnLongClickListener(listener);
}
public void setInitScaleType(int initScaleType) {
mInitScaleType = initScaleType;
switch (initScaleType) {
case INIT_SCALE_TYPE_CENTER_CROP:
mImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
break;
case INIT_SCALE_TYPE_AUTO:
mImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM);
break;
case INIT_SCALE_TYPE_CENTER_INSIDE:
default:
mImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE);
break;
}
if (mDisplayOptimizeListener != null) {
mDisplayOptimizeListener.setInitScaleType(initScaleType);
}
}
public void setOptimizeDisplay(boolean optimizeDisplay) {
mOptimizeDisplay = optimizeDisplay;
if (mOptimizeDisplay) {
mDisplayOptimizeListener = new DisplayOptimizeListener(mImageView);
mImageView.setOnImageEventListener(mDisplayOptimizeListener);
} else {
mDisplayOptimizeListener = null;
mImageView.setOnImageEventListener(null);
}
}
public void setImageSaveCallback(ImageSaveCallback imageSaveCallback) {
mImageSaveCallback = imageSaveCallback;
}
public void setProgressIndicator(ProgressIndicator progressIndicator) {
mProgressIndicator = progressIndicator;
}
public String currentImageFile() {
return mCurrentImageFile == null ? "" : mCurrentImageFile.getAbsolutePath();
}
@RequiresPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
public void saveImageIntoGallery() {
if (mCurrentImageFile == null) {
if (mImageSaveCallback != null) {
mImageSaveCallback.onFail(new IllegalStateException("image not downloaded yet"));
}
return;
}
try {
String result = MediaStore.Images.Media.insertImage(getContext().getContentResolver(),
mCurrentImageFile.getAbsolutePath(), mCurrentImageFile.getName(), "");
if (mImageSaveCallback != null) {
if (!TextUtils.isEmpty(result)) {
mImageSaveCallback.onSuccess(result);
} else {
mImageSaveCallback.onFail(new RuntimeException("saveImageIntoGallery fail"));
}
}
} catch (FileNotFoundException e) {
if (mImageSaveCallback != null) {
mImageSaveCallback.onFail(e);
}
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
for (int i = 0, size = mTempImages.size(); i < size; i++) {
mTempImages.get(i).delete();
}
mTempImages.clear();
}
public void showImage(Uri uri) {
showImage(Uri.EMPTY, uri);
}
public void showImage(Uri thumbnail, Uri uri) {
Log.d("BigImageView", "showImage with thumbnail " + thumbnail + ", " + uri);
mThumbnail = thumbnail;
mImageLoader.loadImage(uri, this);
}
public SubsamplingScaleImageView getSSIV() {
return mImageView;
}
@UiThread
@Override
public void onCacheHit(File image) {
Log.d("BigImageView", "onCacheHit " + image);
mCurrentImageFile = image;
doShowImage(image);
}
@WorkerThread
@Override
public void onCacheMiss(final File image) {
Log.d("BigImageView", "onCacheMiss " + image);
mCurrentImageFile = image;
mTempImages.add(image);
post(new Runnable() {
@Override
public void run() {
doShowImage(image);
}
});
}
@WorkerThread
@Override
public void onStart() {
post(new Runnable() {
@Override
public void run() {
// why show thumbnail in onStart? because we may not need download it from internet
if (mThumbnail != Uri.EMPTY) {
mThumbnailView = mImageLoader.showThumbnail(BigImageView.this, mThumbnail,
mInitScaleType);
addView(mThumbnailView);
}
if (mProgressIndicator != null) {
mProgressIndicatorView = mProgressIndicator.getView(BigImageView.this);
addView(mProgressIndicatorView);
mProgressIndicator.onStart();
}
}
});
}
@WorkerThread
@Override
public void onProgress(final int progress) {
if (mProgressIndicator != null && mProgressNotifyRunnable.update(progress)) {
post(mProgressNotifyRunnable);
}
}
@WorkerThread
@Override
public void onFinish() {
post(new Runnable() {
@Override
public void run() {
doOnFinish();
}
});
}
@UiThread
private void doOnFinish() {
if (mOptimizeDisplay) {
AnimationSet set = new AnimationSet(true);
AlphaAnimation animation = new AlphaAnimation(1, 0);
animation.setDuration(500);
animation.setFillAfter(true);
set.addAnimation(animation);
if (mThumbnailView != null) {
mThumbnailView.setAnimation(set);
}
if (mProgressIndicator != null) {
mProgressIndicator.onFinish();
}
if (mProgressIndicatorView != null) {
mProgressIndicatorView.setAnimation(set);
}
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
if (mThumbnailView != null) {
mThumbnailView.setVisibility(GONE);
}
if (mProgressIndicatorView != null) {
mProgressIndicatorView.setVisibility(GONE);
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
} else {
if (mProgressIndicator != null) {
mProgressIndicator.onFinish();
}
if (mThumbnailView != null) {
mThumbnailView.setVisibility(GONE);
}
if (mProgressIndicatorView != null) {
mProgressIndicatorView.setVisibility(GONE);
}
}
}
@UiThread
private void doShowImage(File image) {
mImageView.setImage(ImageSource.uri(Uri.fromFile(image)));
}
}