package pl.droidsonroids.gif;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.Callback;
import android.support.annotation.NonNull;
import android.view.View;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* {@link Callback} which allows single {@link Drawable} to be associated with multiple callbacks,
* eg. with multiple {@link View}s.
* If drawable needs to be redrawn all currently associated callbacks are invoked.
* If Callback is a View it is then invalidated.
*
* @author koral--
* @author Doctoror
*/
public class MultiCallback implements Callback {
private final CopyOnWriteArrayList<CallbackWeakReference> mCallbacks = new CopyOnWriteArrayList<>();
private final boolean mUseViewInvalidate;
/**
* Equivalent to {@link #MultiCallback(boolean)} with <code>false</code> value.
*/
public MultiCallback() {
this(false);
}
/**
* Set <code>useViewInvalidate</code> to <code>true</code> if displayed {@link Drawable} is not supported by
* {@link Drawable.Callback#invalidateDrawable(Drawable)} of the target. For example if it is located inside {@link android.text.style.ImageSpan}
* displayed in {@link TextView}.
*
* @param useViewInvalidate whether {@link View#invalidate()} should be used instead of {@link Drawable.Callback#invalidateDrawable(Drawable)}
*/
public MultiCallback(final boolean useViewInvalidate) {
mUseViewInvalidate = useViewInvalidate;
}
@Override
public void invalidateDrawable(@NonNull final Drawable who) {
for (int i = 0; i < mCallbacks.size(); i++) {
final CallbackWeakReference reference = mCallbacks.get(i);
final Callback callback = reference.get();
if (callback != null) {
if (mUseViewInvalidate && callback instanceof View) {
((View) callback).invalidate();
} else {
callback.invalidateDrawable(who);
}
} else {
// Always remove null references to reduce list size
mCallbacks.remove(reference);
}
}
}
@Override
public void scheduleDrawable(@NonNull final Drawable who, @NonNull final Runnable what, final long when) {
for (int i = 0; i < mCallbacks.size(); i++) {
final CallbackWeakReference reference = mCallbacks.get(i);
final Callback callback = reference.get();
if (callback != null) {
callback.scheduleDrawable(who, what, when);
} else {
// Always remove null references to reduce Set size
mCallbacks.remove(reference);
}
}
}
@Override
public void unscheduleDrawable(@NonNull final Drawable who, @NonNull final Runnable what) {
for (int i = 0; i < mCallbacks.size(); i++) {
final CallbackWeakReference reference = mCallbacks.get(i);
final Callback callback = reference.get();
if (callback != null) {
callback.unscheduleDrawable(who, what);
} else {
// Always remove null references to reduce list size
mCallbacks.remove(reference);
}
}
}
/**
* Associates given {@link Callback}. If callback has been already added, nothing happens.
*
* @param callback Callback to be associated
*/
public void addView(final Callback callback) {
for (int i = 0; i < mCallbacks.size(); i++) {
final CallbackWeakReference reference = mCallbacks.get(i);
final Callback item = reference.get();
if (item == null) {
// Always remove null references to reduce list size
mCallbacks.remove(reference);
}
}
mCallbacks.addIfAbsent(new CallbackWeakReference(callback));
}
/**
* Disassociates given {@link Callback}. If callback is not associated, nothing happens.
*
* @param callback Callback to be disassociated
*/
public void removeView(final Callback callback) {
for (int i = 0; i < mCallbacks.size(); i++) {
final CallbackWeakReference reference = mCallbacks.get(i);
final Callback item = reference.get();
if (item == null || item == callback) {
// Always remove null references to reduce list size
mCallbacks.remove(reference);
}
}
}
static final class CallbackWeakReference extends WeakReference<Callback> {
CallbackWeakReference(final Callback r) {
super(r);
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
return get() == ((CallbackWeakReference) o).get();
}
@Override
public int hashCode() {
final Callback callback = get();
return callback != null ? callback.hashCode() : 0;
}
}
}