/*
* Copyright 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.nimingban.widget;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
import com.hippo.conaco.Conaco;
import com.hippo.conaco.ConacoTask;
import com.hippo.conaco.DataContainer;
import com.hippo.conaco.ProgressNotify;
import com.hippo.conaco.Unikery;
import com.hippo.conaco.ValueHolder;
import com.hippo.drawable.ImageDrawable;
import com.hippo.drawable.ImageWrapper;
import com.hippo.io.UniFileInputStreamPipe;
import com.hippo.nimingban.NMBAppConfig;
import com.hippo.nimingban.NMBApplication;
import com.hippo.nimingban.util.Settings;
import com.hippo.unifile.UniFile;
import com.hippo.widget.FixedAspectImageView;
import com.hippo.yorozuya.IOUtils;
import com.hippo.yorozuya.io.InputStreamPipe;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public final class HeaderImageView extends FixedAspectImageView
implements Unikery<ImageWrapper>, View.OnClickListener, View.OnLongClickListener {
private static final String KEY_SUPER = "super";
private static final String KEY_IMAGE_UNI_FILE_URI = "image_uni_file_uri";
private int mTaskId = Unikery.INVALID_ID;
private Conaco<ImageWrapper> mConaco;
private final long[] mHits = new long[8];
private UniFile mImageFile;
private TempDataContainer mContainer;
private ValueHolder<ImageWrapper> mHolder;
private OnLongClickImageListener mOnLongClickImageListener;
public HeaderImageView(Context context) {
super(context);
init(context);
}
public HeaderImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public HeaderImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
mConaco = NMBApplication.getConaco(context);
setScaleType(ScaleType.CENTER_CROP);
setSoundEffectsEnabled(false);
setOnClickListener(this);
setOnLongClickListener(this);
}
public void setOnLongClickImageListener(OnLongClickImageListener listener) {
mOnLongClickImageListener = listener;
}
@Override
public void onClick(@NonNull View v) {
System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);
mHits[mHits.length-1] = SystemClock.uptimeMillis();
if (mHits[0] >= (SystemClock.uptimeMillis() - 3000)) {
Arrays.fill(mHits, 0);
Toast.makeText(getContext(), "(<ゝω・)☆ Kira", Toast.LENGTH_SHORT).show();
load();
}
}
@Override
public boolean onLongClick(@NonNull View v) {
if (mImageFile != null && mOnLongClickImageListener != null) {
return mOnLongClickImageListener.onLongClickImage(mImageFile);
} else {
return false;
}
}
public void load() {
mContainer = new TempDataContainer(getContext());
ConacoTask.Builder<ImageWrapper> builder = new ConacoTask.Builder<ImageWrapper>()
.setUnikery(this)
.setKey(null)
.setUrl("http://cover.acfunwiki.org/cover.php")
.setDataContainer(mContainer);
mConaco.load(builder);
}
public void unload() {
mConaco.cancel(this);
removeDrawableAndHolder();
}
@Override
public void setTaskId(int id) {
mTaskId = id;
}
@Override
public int getTaskId() {
return mTaskId;
}
@Override
public void onMiss(Conaco.Source source) {
}
@Override
public void onRequest() {
}
@Override
public void onProgress(long singleReceivedSize, long receivedSize, long totalSize) {
}
private void removeDrawableAndHolder() {
// Remove drawable
Drawable drawable = getDrawable();
if (drawable instanceof ImageDrawable) {
((ImageDrawable) drawable).recycle();
}
setImageDrawable(null);
// Remove holder
if (mHolder != null) {
mHolder.release(this);
ImageWrapper imageWrapper = mHolder.getValue();
if (mHolder.isFree()) {
// ImageWrapper is free, stop animate
imageWrapper.stop();
if (!mHolder.isInMemoryCache()) {
// ImageWrapper is not needed any more, recycle it
imageWrapper.recycle();
}
}
mHolder = null;
}
}
@Override
public boolean onGetObject(@NonNull ValueHolder<ImageWrapper> holder, Conaco.Source source) {
// Update image file
if (mImageFile != null) {
mImageFile = null;
}
if (mContainer != null) {
mImageFile = mContainer.mTempFile;
mContainer = null;
}
holder.obtain(this);
removeDrawableAndHolder();
mHolder = holder;
ImageWrapper imageWrapper = holder.getValue();
Drawable drawable = new ImageDrawable(imageWrapper);
imageWrapper.start();
setImageDrawable(drawable);
return true;
}
@Override
public void onSetDrawable(Drawable drawable) {
removeDrawableAndHolder();
setImageDrawable(drawable);
}
@Override
public void onFailure() {
mContainer = null;
}
@Override
public void onCancel() {
mContainer = null;
}
@NonNull
@Override
protected Parcelable onSaveInstanceState() {
Bundle saved = new Bundle();
saved.putParcelable(KEY_SUPER, super.onSaveInstanceState());
if (mImageFile != null) {
saved.putParcelable(KEY_IMAGE_UNI_FILE_URI, mImageFile.getUri());
}
return saved;
}
private void setImageFile(UniFile file) {
if (file == null) {
return;
}
Object object = NMBApplication.getImageWrapperHelper(getContext())
.decode(new UniFileInputStreamPipe(file));
if (object != null) {
ImageWrapper imageWrapper = (ImageWrapper) object;
imageWrapper.start();
Drawable drawable = new ImageDrawable(imageWrapper);
removeDrawableAndHolder();
setImageDrawable(drawable);
mImageFile = file;
mContainer = null;
}
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
Bundle saved = (Bundle) state;
Uri uri = saved.getParcelable(KEY_IMAGE_UNI_FILE_URI);
if (uri != null) {
UniFile file = UniFile.fromUri(getContext(), uri);
if (file != null && file.exists()) {
setImageFile(file);
}
}
super.onRestoreInstanceState(saved.getParcelable(KEY_SUPER));
}
public interface OnLongClickImageListener {
boolean onLongClickImage(UniFile imageFile);
}
private static class TempDataContainer implements DataContainer {
private Context mContext;
private String mName;
private UniFile mTempFile;
public TempDataContainer(Context context) {
mContext = context.getApplicationContext();
}
/**
* http://stackoverflow.com/questions/332079
*
* @param bytes The bytes to convert.
* @return A {@link String} converted from the bytes of a hashable key used
* to store a filename on the disk, to hex digits.
*/
private static String bytesToHexString(final byte[] bytes) {
final StringBuilder builder = new StringBuilder();
for (final byte b : bytes) {
final String hex = Integer.toHexString(0xFF & b);
if (hex.length() == 1) {
builder.append('0');
}
builder.append(hex);
}
return builder.toString();
}
private static String getKeyForUrl(String url) {
String key;
try {
final MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(url.getBytes());
key = bytesToHexString(digest.digest());
} catch (final NoSuchAlgorithmException e) {
key = String.valueOf(url.hashCode());
}
return key;
}
@Override
public void onUrlMoved(String requestUrl, String responseUrl) {
mName = "ac-cover-" + getKeyForUrl(responseUrl);
}
@Override
public boolean save(InputStream is, long length, String mediaType, ProgressNotify notify) {
OutputStream os = null;
try {
boolean autoSave = Settings.getSaveImageAuto() && mName != null;
if (autoSave) {
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mediaType);
if (TextUtils.isEmpty(extension)) {
extension = "jpg";
}
String filename = mName + '.' + extension;
UniFile dir = Settings.getImageSaveLocation();
if (dir != null) {
mTempFile = dir.createFile(filename);
} else {
mTempFile = UniFile.fromFile(NMBAppConfig.createTempFileWithFilename(filename));
}
} else {
mTempFile = UniFile.fromFile(NMBAppConfig.createTempFile());
}
if (mTempFile == null) {
return false;
}
os = mTempFile.openOutputStream();
IOUtils.copy(is, os);
// Notify media scanner
if (autoSave) {
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, mTempFile.getUri()));
}
return true;
} catch (IOException e) {
return false;
} finally {
IOUtils.closeQuietly(os);
}
}
@Override
public InputStreamPipe get() {
if (mTempFile == null) {
return null;
} else {
return new UniFileInputStreamPipe(mTempFile);
}
}
@Override
public void remove() {
mTempFile.delete();
}
}
}