/**
* Wire
* Copyright (C) 2016 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.waz.zclient.views.images;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import com.waz.api.BitmapCallback;
import com.waz.api.ImageAsset;
import com.waz.api.LoadHandle;
import com.waz.api.UpdateListener;
import com.waz.zclient.R;
import timber.log.Timber;
public class ImageAssetView extends ImageView implements UpdateListener {
private BitmapLoadedCallback bitmapLoadedCallback;
private LoadHandle bitmapLoadHandle;
private boolean shouldScaleForPortraitMode;
private ImageAsset imageAsset;
private boolean animateFirstTimeBitmapAppearance;
private ObjectAnimator alphaAnimator;
private final BitmapCallback loadCallback = new BitmapCallback() {
@Override
public void onBitmapLoaded(Bitmap bitmap) {
Timber.i("ImageAssetView %s :: onBitmapLoaded(), id=%s, w=%d, h=%d",
ImageAssetView.this,
imageAsset.getId(),
bitmap.getWidth(),
bitmap.getHeight());
setScalePolicy(bitmap);
setImageBitmap(bitmap);
if (bitmapLoadedCallback != null) {
bitmapLoadedCallback.onBitmapLoadFinished(true);
}
if (!animateFirstTimeBitmapAppearance) {
return;
}
animateFirstTimeBitmapAppearance = false;
alphaAnimator = ObjectAnimator.ofFloat(ImageAssetView.this, View.ALPHA, 0, 1f);
alphaAnimator.setDuration(getResources().getInteger(R.integer.animation_duration_medium));
alphaAnimator.start();
}
@Override
public void onBitmapLoadingFailed(BitmapLoadingFailed reason) {
logBitmapLoadError(reason);
clearImage();
if (bitmapLoadedCallback != null) {
bitmapLoadedCallback.onBitmapLoadFinished(false);
}
}
};
public ImageAssetView(Context context) {
super(context);
}
public ImageAssetView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ImageAssetView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setImageAsset(@Nullable final ImageAsset imageAsset) {
clearImage();
animateFirstTimeBitmapAppearance = true;
bindImageAsset(imageAsset);
}
public void setShouldScaleForPortraitMode(boolean shouldScaleForPortraitMode) {
this.shouldScaleForPortraitMode = shouldScaleForPortraitMode;
}
public void clearImage() {
unbindImageAsset();
imageAsset = null;
super.setImageDrawable(null);
}
public void setBitmapLoadedCallback(BitmapLoadedCallback bitmapLoadedCallback) {
this.bitmapLoadedCallback = bitmapLoadedCallback;
}
@Override
public void updated() {
loadBitmap();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
unbindImageAsset();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (imageAsset != null) {
imageAsset.addUpdateListener(this);
loadBitmap();
}
}
private void bindImageAsset(ImageAsset imageAsset) {
this.imageAsset = imageAsset;
if (imageAsset != null) {
Timber.i("Binding ImageAssetView %s to ImageAsset %s", this, imageAsset.getId());
this.imageAsset.addUpdateListener(this);
loadBitmap();
}
}
private void unbindImageAsset() {
if (alphaAnimator != null) {
alphaAnimator.cancel();
}
cancelPreviousBitmapLoad();
if (imageAsset != null) {
Timber.i("Unbinding ImageAssetView %s from ImageAsset %s", this, imageAsset.getId());
imageAsset.removeUpdateListener(this);
}
}
private void loadBitmap() {
cancelPreviousBitmapLoad();
if (imageAsset.isEmpty()) {
Timber.i("ImageAssetView %s :: loadBitmap() empty ImageAsset", this);
if (bitmapLoadedCallback != null) {
bitmapLoadedCallback.onBitmapLoadFinished(false);
}
return;
}
Timber.i("ImageAssetView %s :: loadBitmap(), id=%s", this, imageAsset.getId());
bitmapLoadHandle = imageAsset.getBitmap(getWidth(), loadCallback);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed && imageAsset != null) {
loadBitmap(); //request bitmap again since view size has changed
}
}
private void setScalePolicy(Bitmap bitmap) {
if (shouldScaleForPortraitMode) {
if (bitmap.getWidth() > bitmap.getHeight()) {
setScaleType(ScaleType.FIT_CENTER);
} else {
setScaleType(ScaleType.CENTER_CROP);
}
}
}
private void logBitmapLoadError(BitmapCallback.BitmapLoadingFailed reason) {
Timber.e("ImageAssetView %s :: failed loading bitmap for %s id=%s, reason: %s",
this,
imageAsset,
imageAsset != null ? imageAsset.getId() : null,
reason);
}
private void cancelPreviousBitmapLoad() {
if (bitmapLoadHandle != null) {
bitmapLoadHandle.cancel();
}
}
public interface BitmapLoadedCallback {
void onBitmapLoadFinished(boolean bitmapLoaded);
}
}