26 TextureView.SurfaceTextureListener, SurfaceHolder.Callback {
34 private static final String LOG_TAG =
"SkottiePlayer";
36 private boolean mIsRunning =
false;
37 private SurfaceTexture mSurfaceTexture;
38 private EGLSurface mEglSurface;
39 private boolean mNewSurface =
false;
40 private SurfaceHolder mSurfaceHolder;
41 private int mRepeatCount;
42 private int mRepeatCounter;
44 private int mBackgroundColor;
45 private long mNativeProxy;
46 private long mDuration;
47 private float mProgress;
48 private long mAnimationStartTime;
52 mSurfaceTexture = surfaceTexture;
55 SkottieAnimation(TextureView view, InputStream is,
int backgroundColor,
int repeatCount) {
57 mSurfaceTexture = view.getSurfaceTexture();
59 view.setSurfaceTextureListener(
this);
60 mBackgroundColor = backgroundColor;
61 mRepeatCount = repeatCount;
62 mRepeatCounter = mRepeatCount;
65 SkottieAnimation(SurfaceView view, InputStream is,
int backgroundColor,
int repeatCount) {
67 mSurfaceHolder = view.getHolder();
69 mSurfaceHolder.addCallback(
this);
70 mBackgroundColor = backgroundColor;
71 mRepeatCount = repeatCount;
72 mRepeatCounter = mRepeatCount;
79 private ByteBuffer convertToByteBuffer(InputStream is)
throws IOException {
80 if (is instanceof FileInputStream) {
81 FileChannel fileChannel = ((FileInputStream)is).getChannel();
82 return fileChannel.map(FileChannel.MapMode.READ_ONLY,
83 fileChannel.position(), fileChannel.size());
86 ByteArrayOutputStream byteStream =
new ByteArrayOutputStream();
87 byte[] tmpStorage =
new byte[4096];
89 while ((bytesRead = is.read(tmpStorage, 0, tmpStorage.length)) != -1) {
90 byteStream.write(tmpStorage, 0, bytesRead);
94 tmpStorage = byteStream.toByteArray();
96 ByteBuffer
buffer = ByteBuffer.allocateDirect(tmpStorage.length);
97 buffer.order(ByteOrder.nativeOrder());
98 buffer.put(tmpStorage, 0, tmpStorage.length);
99 return buffer.asReadOnlyBuffer();
102 private boolean init(InputStream is) {
104 ByteBuffer byteBuffer;
106 byteBuffer = convertToByteBuffer(is);
107 }
catch (IOException e) {
108 Log.e(LOG_TAG,
"failed to read input stream", e);
113 mNativeProxy = nCreateProxy(proxy, byteBuffer);
114 mDuration = nGetDuration(mNativeProxy);
119 private void notifyAnimationEnd() {
120 if (this.getListeners() !=
null) {
123 ArrayList<Animator.AnimatorListener> listeners =
124 new ArrayList<Animator.AnimatorListener>(this.getListeners());
125 for (AnimatorListener l : listeners) {
126 l.onAnimationEnd(
this);
135 nDeleteProxy(mNativeProxy);
144 config.mSurfaceWidth =
width;
145 config.mSurfaceHeight =
height;
155 long currentTime = System.nanoTime();
156 mAnimationStartTime = currentTime - (long)(1000000 * mDuration * mProgress);
159 mRepeatCounter = mRepeatCount;
164 catch (Throwable t) {
165 Log.e(
LOG_TAG,
"start failed", t);
166 throw new RuntimeException(t);
168 if (this.getListeners() !=
null) {
169 for (AnimatorListener l : this.getListeners()) {
170 l.onAnimationStart(
this);
180 if (mEglSurface !=
null) {
189 catch (Throwable t) {
190 Log.e(
LOG_TAG,
"stop failed", t);
191 throw new RuntimeException(t);
193 notifyAnimationEnd();
203 catch (Throwable t) {
204 Log.e(
LOG_TAG,
"pause failed", t);
205 throw new RuntimeException(t);
214 long currentTime = System.nanoTime();
215 mAnimationStartTime = currentTime - (long)(1000000 * mDuration * mProgress);
222 catch (Throwable t) {
223 Log.e(
LOG_TAG,
"resume failed", t);
224 throw new RuntimeException(t);
257 if (mRepeatCount == -1) {
258 return DURATION_INFINITE;
261 return mDuration * (1 + mRepeatCount);
273 mProgress = progress;
275 mAnimationStartTime = System.nanoTime()
276 - (
long)(1000000 * mDuration * mProgress);
281 catch (Throwable t) {
282 Log.e(
LOG_TAG,
"setProgress failed", t);
283 throw new RuntimeException(t);
291 private void drawFrame() {
293 boolean forceDraw =
false;
297 if (mEglSurface !=
null) {
304 if (mEglSurface ==
null) {
306 if (mSurfaceTexture !=
null) {
311 }
else if (mSurfaceHolder !=
null) {
318 if (mEglSurface !=
null) {
319 if (!mRunner.
mEgl.eglMakeCurrent(mRunner.
mEglDisplay, mEglSurface, mEglSurface,
322 Log.w(LOG_TAG,
"eglMakeCurrent failed "
323 + GLUtils.getEGLErrorString(mRunner.
mEgl.eglGetError()));
329 mProgress, mBackgroundColor, forceDraw)) {
332 if (
error == EGL10.EGL_BAD_SURFACE
333 ||
error == EGL10.EGL_BAD_NATIVE_WINDOW) {
339 Log.w(LOG_TAG,
"swapBuffers failed "
340 + GLUtils.getEGLErrorString(
error));
346 throw new RuntimeException(
"Cannot swap buffers "
347 + GLUtils.getEGLErrorString(
error));
361 }
catch (Throwable t) {
362 Log.e(LOG_TAG,
"drawFrame failed", t);
367 private void checkSurface() throws RuntimeException {
369 if (mEglSurface ==
null || mEglSurface == EGL10.EGL_NO_SURFACE) {
372 throw new RuntimeException(
"createWindowSurface failed "
373 + GLUtils.getEGLErrorString(
error));
381 Choreographer.getInstance().postFrameCallback(
this);
384 long durationNS = mDuration * 1000000;
385 long timeSinceAnimationStartNS = frameTimeNanos - mAnimationStartTime;
386 long animationProgressNS = timeSinceAnimationStartNS % durationNS;
387 mProgress = animationProgressNS / (float)durationNS;
388 if (timeSinceAnimationStartNS > durationNS) {
389 mAnimationStartTime += durationNS;
391 if (timeSinceAnimationStartNS > durationNS) {
392 if (mRepeatCounter > 0) {
394 }
else if (mRepeatCounter == 0) {
397 notifyAnimationEnd();
413 config.mValidSurface =
true;
416 catch (Throwable t) {
417 Log.e(
LOG_TAG,
"updateSurface failed", t);
418 throw new RuntimeException(t);
432 config.mValidSurface =
false;
450 mSurfaceHolder = holder;
452 config.mValidSurface =
true;
455 catch (Throwable t) {
456 Log.e(
LOG_TAG,
"updateSurface failed", t);
457 throw new RuntimeException(t);
463 config.mValidSurface =
false;
477 private native
long nCreateProxy(
long runner, ByteBuffer data);
478 private native
void nDeleteProxy(
long nativeProxy);
479 private native
boolean nDrawFrame(
long nativeProxy,
int width,
int height,
480 boolean wideColorGamut,
float progress,
481 int backgroundColor,
boolean forceDraw);
482 private native
long nGetDuration(
long nativeProxy);
static const uint8_t buffer[]
const uint8_t uint32_t uint32_t GError ** error
uint32_t uint32_t * format