
io.nextop.view.ImageView Maven / Gradle / Ivy
package io.nextop.view;
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.net.Uri;
import android.util.AttributeSet;
import com.google.common.annotations.Beta;
import io.nextop.*;
import io.nextop.vm.ImageViewModel;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.functions.Action1;
import rx.functions.Func2;
import rx.internal.util.SubscriptionList;
import javax.annotation.Nullable;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Beta
public class ImageView extends android.widget.ImageView {
@Nullable
Nextop.LayersConfig layersConfig = null;
@Nullable
Source source = null;
// loading loadSubscriptions FIXME rename to loadSubscriptions
@Nullable
SubscriptionList loadSubscriptions = null;
@Nullable
Progress progress = null;
// CONFIG
/** align visual transitions on these boundaries */
int transitionQMs = 200;
int fadeInMs = 200;
int transferQPx = 48;
float defaultMaxTransferMultiple = 2.f;
int defaultMinTransferPx = 48;
int downProgressTimeoutMs = 2000;
// DRAW STATE
Paint tempPaint = new Paint();
public ImageView(Context context) {
super(context);
}
public ImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
private Nextop.LayersConfig createLayersConfig() {
Nextop.LayersConfig config;
if (null != layersConfig) {
config = layersConfig.copy();
} else {
config = createDefaultLayersConfig();
}
// now compute the target sizes for each bounds
int w = getWidth();
int h = getHeight();
// set the max values depending on the scale type
int maxW;
int maxH;
switch (getScaleType()) {
case CENTER:
case CENTER_CROP:
int s = Math.max(w, h);
maxW = s;
maxH = s;
break;
case CENTER_INSIDE:
case FIT_CENTER:
case FIT_END:
case FIT_START:
case FIT_XY:
maxW = w;
maxH = h;
break;
case MATRIX:
// use the scale values from the matrix
float[] mvalues = new float[9];
getImageMatrix().getValues(mvalues);
float sx = mvalues[Matrix.MSCALE_X];
float sy = mvalues[Matrix.MSCALE_Y];
maxW = Math.round(sx * w);
maxH = Math.round(sy * h);
break;
default:
throw new IllegalArgumentException();
}
maxW = Math.max(defaultMinTransferPx, maxW);
maxH = Math.max(defaultMinTransferPx, maxH);
// now quantize the max sizes to hit the same cache values in each band
// (round up)
maxW = ((maxW + transferQPx - 1) / transferQPx) * transferQPx;
maxH = ((maxH + transferQPx - 1) / transferQPx) * transferQPx;
for (Nextop.LayersConfig.Bound bound : config.receiveBounds) {
bound.maxWidth = w;
bound.maxHeight = h;
}
return config;
}
private Nextop.LayersConfig createDefaultLayersConfig() {
// 1. set the max transfer size based on the current view size (multiple e.g. 2x)
// 2. set the min transfer size of all but the last based on a fixed size (e.g. 48px)
// 3. use two quality steps (0.3 and 1.0)
int w = getWidth();
int h = getHeight();
Nextop.LayersConfig.Bound base = new Nextop.LayersConfig.Bound();
base.maxTransferWidth = Math.round(defaultMaxTransferMultiple * w);
base.maxTransferHeight = Math.round(defaultMaxTransferMultiple * h);
base.minTransferWidth = defaultMinTransferPx;
base.minTransferHeight = defaultMinTransferPx;
Nextop.LayersConfig.Bound a = base.copy();
a.quality = 0.3f;
Nextop.LayersConfig.Bound b = base.copy();
b.quality = 1.f;
return Nextop.LayersConfig.receive(a, b);
}
// only the transfer properties are used in Bounds
// the display properties are set by the view
public void setLayersConfig(Nextop.LayersConfig layersConfig) {
this.layersConfig = layersConfig;
reload();
}
/////// SOURCE ///////
public void clearImage() {
setSource(null);
}
@Override
public void setImageURI(Uri uri) {
setImageUri(uri);
}
public void setImageUri(Uri uri) {
setSource(Source.uri(uri));
}
// set to an image in the progress of uploading (the localId is the message that is sending the image)
public void setLocalImage(Id id) {
setSource(Source.local(id));
}
@Override
public void setImageBitmap(Bitmap bitmap) {
setSource(Source.memory(bitmap));
}
private void setSource(@Nullable Source source) {
if (!Objects.equals(this.source, source)) {
reset();
this.source = source;
reload();
}
}
private void cancelLoadSubscriptions() {
if (null != loadSubscriptions) {
loadSubscriptions.unsubscribe();
loadSubscriptions = null;
}
}
private void reset() {
cancelLoadSubscriptions();
setProgress(null);
setImageDrawable(null);
}
private void reload() {
cancelLoadSubscriptions();
setProgress(null);
if (null != source) {
switch (source.type) {
case URI: {
loadUri(source.uri);
break;
}
case LOCAL: {
loadLocal(source.localId);
break;
}
case MEMORY: {
loadMemory(source.bitmap);
break;
}
}
}
}
private void loadUri(Uri uri) {
assert null == loadSubscriptions;
@Nullable Nextop nextop = NextopAndroid.getActive(this);
if (null != nextop) {
loadSubscriptions = new SubscriptionList();
Message message = MessageAndroid.valueOf(Route.Method.GET, uri);
Observable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy