package com.airbnb.lottie;
import com.airbnb.lottie.model.KeyPath;
import com.airbnb.lottie.utils.ContextUtil;
import com.airbnb.lottie.utils.HMOSLogUtil;
import com.airbnb.lottie.value.SimpleLottieValueCallback;
import com.airbnb.lottie.utils.Utils;
import com.airbnb.lottie.value.LottieAnimationViewData;
import com.airbnb.lottie.value.LottieFrameInfo;
import com.airbnb.lottie.value.LottieValueCallback;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.Image;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.ColorFilter;
import ohos.agp.utils.Color;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
/**
* This view will load, deserialize, and display an After Effects animation exported with bodymovin.
* <p>
* You may set the animation in following ways:
* {@link #setAnimation(String)}
* {@link #setAnimationFromJson(String, String)}
* {@link #setAnimationFromUrl(String)}
* {@link #setComposition(LottieComposition)}
* <p>
* You can set a default cache strategy
* <p>
* You can manually set the progress of the animation with {@link #setProgress(float)}
*
* @see <a href="http://airbnb.io/lottie">Full Documentation</a>
*/
public class LottieAnimationView extends Image implements Component.BindStateChangedListener,
Component.ComponentStateChangedListener, Component.DrawTask {
private static final String TAG = LottieAnimationView.class.getSimpleName();
private static final LottieListener<Throwable> DEFAULT_FAILURE_LISTENER = throwable -> {
// By default, fail silently for network errors.
if (Utils.isNetworkException(throwable)) {
HMOSLogUtil.error(L.TAG, "Unable to load composition.", throwable);
return;
}
throw new IllegalStateException("Unable to parse composition", throwable);
};
private final LottieListener<LottieComposition> loadedListener = composition -> setComposition(composition);
private final LottieListener<Throwable> wrappedFailureListener = new LottieListener<Throwable>() {
@Override
public void onResult(Throwable result) {
if (fallbackResource != 0) {
// Element drawable = getContext().getResourceManager().getDrawable(fallbackResource);
//From context get ResouceMangager
ohos.global.resource.ResourceManager rm = getContext().getResourceManager();
//Pass resrouceId and get Inputstream
try {
InputStream is = (InputStream) rm.getResource(fallbackResource);
//Create options to decode bitmap
ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions();
Utils.resetOptions(decodingOpts);
PixelMap pixelmap = Utils.decodePixelMap(is, decodingOpts, null);
setImageElement(new PixelMapElement(pixelmap));
} catch (IOException | NotExistException e) {
HMOSLogUtil.error(L.TAG, "Exception :" + e.getLocalizedMessage());
}
}
LottieListener<Throwable> l = failureListener == null ? DEFAULT_FAILURE_LISTENER : failureListener;
l.onResult(result);
}
};
private LottieListener<Throwable> failureListener;
private int fallbackResource = 0;
private final LottieDrawable lottieDrawable = new LottieDrawable();
private boolean isInitialized;
private String animationName;
private int animationResId;
private boolean playAnimationWhenShown = false;
private boolean wasAnimatingWhenNotShown = false;
private boolean wasAnimatingWhenDetached = false;
/**
* When we set a new composition, we set LottieDrawable to null then back again so that ImageView re-checks its bounds.
* However, this causes the drawable to get unscheduled briefly. Normally, we would pause the animation but in this case, we don't want to.
*/
private boolean ignoreUnschedule = false;
private boolean autoPlay = false;
private boolean cacheComposition = true;
private RenderMode renderMode = RenderMode.AUTOMATIC;
private final Set<LottieOnCompositionLoadedListener> lottieOnCompositionLoadedListeners = new HashSet<>();
/**
* Prevents a StackOverflowException on 4.4 in which getDrawingCache() calls buildDrawingCache().
* This isn't a great solution but it works and has very little performance overhead.
* At some point in the future, the original goal of falling back to hardware rendering when
* the animation is set to software rendering but it is too large to fit in a software bitmap
* should be reevaluated.
*/
private int buildDrawingCacheDepth = 0;
private LottieTask<LottieComposition> compositionTask;
/**
* Can be null because it is created async
*/
private LottieComposition composition;
private LottieAnimationViewData animationData;
public LottieAnimationView(Context context) {
super(context);
init(null);
setBindStateChangedListener(this);
}
public LottieAnimationView(Context context, AttrSet attrs) {
super(context, attrs);
//init(attrs);
setBindStateChangedListener(this);
}
public LottieAnimationView(Context context, AttrSet attrs, String styleName) {
super(context, attrs, styleName);
//init(attrs);
setBindStateChangedListener(this);
}
//TODO : TypedArray usage need to implement in HMOS
private void init(LottieAnimationViewData attrs) {
HMOSLogUtil.error(L.TAG, "Enter init");
if (attrs.isHasRawRes() && attrs.isHasFileName()) {
throw new IllegalArgumentException("rawRes and fileName cannot be used at " +
"the same time. Please use only one at once.");
} else if (attrs.isHasRawRes()) {
setAnimation(attrs.getResId());
} else if (attrs.isHasFileName()) {
setAnimation(attrs.getFileName());
} else if (attrs.isHasUrl()) {
setAnimationFromUrl(attrs.getUrl());
} else {
HMOSLogUtil.error(L.TAG, "No RawRes, No FileName & no URL");
}
if (attrs.autoPlay) {
wasAnimatingWhenDetached = true;
autoPlay = true;
}
if (attrs.loop) {
lottieDrawable.setRepeatCount(LottieDrawable.INFINITE);
}
setRepeatCount(attrs.getRepeatCount());
setSpeed(1f);
setProgress(0);
SimpleColorFilter filter = new SimpleColorFilter(Color.TRANSPARENT.getValue());
KeyPath keyPath = new KeyPath("**");
LottieValueCallback<ColorFilter> callback = new LottieValueCallback<>(filter);
addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback);
HMOSLogUtil.error(L.TAG, "addValueCallback done");
lottieDrawable.setScale(1f);
int renderModeOrdinal = RenderMode.AUTOMATIC.ordinal();
if (renderModeOrdinal >= RenderMode.values().length) {
renderModeOrdinal = RenderMode.AUTOMATIC.ordinal();
}
setRenderMode(RenderMode.values()[renderModeOrdinal]);
HMOSLogUtil.error(L.TAG, "set render modes done");
if (getScaleMode() != null) {
lottieDrawable.setScaleMode