[androidkit] initial upload of SkottieView util

Change-Id: I6f83ee6947121bef8a0c5b76b7a96ad85f24bd36
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/430018
Commit-Queue: Jorge Betancourt <jmbetancourt@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Color.java b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Color.java
index e9919ea..693eb31 100644
--- a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Color.java
+++ b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Color.java
@@ -21,6 +21,17 @@
         this(0, 0, 0, 1);
     }
 
+    /*
+     * Converts int given by android.graphics.Color
+     * to AndroidKit Color
+     */
+    public Color(int color) {
+        mA = ((color >> 24) & 0xff) / 255.f;
+        mR = ((color >> 16) & 0xff) / 255.f;
+        mG = ((color >>  8) & 0xff) / 255.f;
+        mB = ((color)       & 0xff) / 255.f;
+    }
+
     public float r() { return mR; }
     public float g() { return mG; }
     public float b() { return mB; }
diff --git a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/util/SkottieView.java b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/util/SkottieView.java
new file mode 100644
index 0000000..2d449b0
--- /dev/null
+++ b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/util/SkottieView.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+package org.skia.androidkit.util;
+
+import android.content.Context;
+
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.SurfaceView;
+
+import android.view.TextureView;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import java.io.InputStream;
+
+import org.skia.androidkit.Canvas;
+import org.skia.androidkit.Color;
+import org.skia.androidkit.Matrix;
+import org.skia.androidkit.SkottieAnimation;
+import org.skia.androidkit.Surface;
+
+import org.skia.androidkit.R;
+
+class SkottieRenderer extends SurfaceRenderer {
+    private float mSurfaceWidth,
+                  mSurfaceHeight;
+    private Color mBackground;
+    private SkottieAnimation mAnimation;
+    private boolean mPlaying;
+
+    SkottieRenderer(SkottieAnimation mAnimation, Color mBackground) {
+        this.mAnimation = mAnimation;
+        this.mBackground = mBackground;
+    }
+    @Override
+    protected void onSurfaceInitialized(Surface surface) {
+        mSurfaceWidth  = surface.getWidth();
+        mSurfaceHeight = surface.getHeight();
+        mPlaying = true;
+    }
+
+    @Override
+    protected void onRenderFrame(Canvas canvas, long ms) {
+        if(mPlaying) {
+            canvas.drawColor(mBackground);
+            double t = (double)ms / 1000 % mAnimation.getDuration();
+            mAnimation.seekTime(t);
+
+            float s = Math.min(mSurfaceWidth / mAnimation.getWidth(),
+                    mSurfaceHeight / mAnimation.getHeight());
+            canvas.save();
+            canvas.concat(new Matrix().translate((mSurfaceWidth  - s*mAnimation.getWidth())/2,
+                    (mSurfaceHeight - s*mAnimation.getHeight())/2)
+                    .scale(s, s));
+
+            mAnimation.render(canvas);
+            canvas.restore();
+        }
+    }
+
+    void play() {
+        if (!mPlaying) {
+            mPlaying = true;
+        }
+    }
+
+    void pause() {
+        if (mPlaying) {
+            mPlaying = false;
+        }
+    }
+
+    // TODO: adjust the super time base (otherwise onRenderFrame will overwrite via seekTime).
+    void seekFrame(double f) {
+        mAnimation.seekFrame(f);
+    }
+
+    @Override
+    public void release() {
+        mAnimation.release();
+        super.release();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        this.release();
+    }
+}
+
+public class SkottieView extends FrameLayout {
+    private SurfaceView mBackingView;
+    private SkottieRenderer mRenderer;
+
+    private final String LOG_TAG = "SkottieView";
+
+    public SkottieView(Context context, int resID, Color background) {
+        super(context);
+        mBackingView = new SurfaceView(context);
+        initBackingView();
+        InputStream inputStream = context.getResources().openRawResource(resID);
+        mRenderer = new SkottieRenderer(makeAnimation(inputStream), background);
+        mBackingView.getHolder().addCallback(mRenderer);
+    }
+
+    public SkottieView(Context context) {
+        super(context);
+        mBackingView = new SurfaceView(context);
+        initBackingView();
+    }
+
+    public SkottieView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0, 0);
+    }
+
+    public SkottieView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    // SkottieView constructor when initialized in XML layout
+    public SkottieView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs);
+        TypedArray a = context.getTheme()
+                .obtainStyledAttributes(attrs, R.styleable.SkottieView, defStyleAttr, defStyleRes);
+        try {
+            // set backing view and background color
+            mBackingView = new SurfaceView(context);
+            initBackingView();
+            int backgroundColor = a.getColor(R.styleable.SkottieView_background_color, -1);
+            if (backgroundColor == -1) {
+                throw new RuntimeException("background_color attribute "
+                        + "needed for SurfaceView");
+            }
+            if (android.graphics.Color.alpha(backgroundColor) != 255) {
+                throw new RuntimeException("background_color cannot be transparent");
+            }
+            // set source
+            int src = a.getResourceId(R.styleable.SkottieView_src, -1);
+            Color c = new Color(backgroundColor);
+            setSource(src, context, new Color(backgroundColor));
+        } finally {
+            a.recycle();
+        }
+    }
+
+    private void initBackingView() {
+        mBackingView.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        addView(mBackingView);
+    }
+
+    static private SkottieAnimation makeAnimation(InputStream is) {
+        String json = "";
+        try {
+            byte[] data = new byte[is.available()];
+            is.read(data);
+            json = new String(data);
+        } catch (Exception e) {}
+        return new SkottieAnimation(json);
+    }
+
+    public void setSource(int resID, Context context, Color background) {
+        InputStream inputStream = context.getResources().openRawResource(resID);
+        mRenderer = new SkottieRenderer(makeAnimation(inputStream), background);
+        mBackingView.getHolder().addCallback(mRenderer);
+    }
+
+    public void play() {
+        mRenderer.play();
+    }
+
+    public void pause() {
+        mRenderer.pause();
+    }
+
+    // TODO: track the time base locally
+    public void seekFrame(float frame) {
+        mRenderer.seekFrame(frame);
+    }
+}
diff --git a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/util/SurfaceRenderer.java b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/util/SurfaceRenderer.java
index 2ed79b7..6a774f2 100644
--- a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/util/SurfaceRenderer.java
+++ b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/util/SurfaceRenderer.java
@@ -92,4 +92,8 @@
             } catch (InterruptedException e) {}
         }
     }
+
+    public void release() {
+        stopRenderThread();
+    }
 }
diff --git a/platform_tools/android/apps/AndroidKit/src/main/res/values/attrs.xml b/platform_tools/android/apps/AndroidKit/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..d6dddef
--- /dev/null
+++ b/platform_tools/android/apps/AndroidKit/src/main/res/values/attrs.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <declare-styleable name="SkottieView">
+    <attr name="background_color" format="color"/>
+    <attr name="src" format="reference"/>
+    <attr name="android:repeatCount" format="integer"/>
+  </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java b/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
index fc44c50..d60cb05 100644
--- a/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
+++ b/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
@@ -14,8 +14,13 @@
 import android.util.Log;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
+
 import org.skia.androidkit.*;
+import org.skia.androidkit.util.SkottieView;
 import org.skia.androidkit.util.SurfaceRenderer;
 
 public class MainActivity extends Activity {
@@ -83,6 +88,18 @@
          */
         SurfaceView runtimeEffectView = findViewById(R.id.runtimeEffect);
         runtimeEffectView.getHolder().addCallback(new DemoRuntimeShaderRenderer());
+
+        /*
+         * SkottieView added programmatically to view hierarchy
+         */
+        SkottieView skottieView = new SkottieView(this, R.raw.im_thirsty, new Color(1, 1, 1, 1));
+        skottieView.setLayoutParams(new ViewGroup.LayoutParams(400, 400));
+        skottieView.setOnClickListener((View v) -> {
+            SkottieView s = (SkottieView)v;
+            s.pause();
+        });
+        LinearLayout skottieContainer = findViewById(R.id.skottie_container);
+        skottieContainer.addView(skottieView);
     }
 
     private class ThreadedSurfaceHandler implements SurfaceHolder.Callback {
diff --git a/platform_tools/android/apps/androidkitdemo/src/main/res/layout/activity_main.xml b/platform_tools/android/apps/androidkitdemo/src/main/res/layout/activity_main.xml
index 079e854..283c11a 100644
--- a/platform_tools/android/apps/androidkitdemo/src/main/res/layout/activity_main.xml
+++ b/platform_tools/android/apps/androidkitdemo/src/main/res/layout/activity_main.xml
@@ -83,8 +83,15 @@
                     app:layout_constraintTop_toTopOf="parent"
                     android:text="Runtime effect drawn on a AndroidKit's\nutil thread. GL on Surface View.">
                 </TextView>
-
             </android.support.constraint.ConstraintLayout>
+
+            <org.skia.androidkit.util.SkottieView
+                android:layout_width="400px"
+                android:layout_height="400px"
+                app:background_color="#fefefe"
+                app:src="@raw/permission">
+            </org.skia.androidkit.util.SkottieView>
+
         </LinearLayout>
     </ScrollView>
     <org.skia.androidkitdemo1.NavigationSpinner