SkAR Java: refactoring main app class & finger painting class

Main things to look at:
1) onDrawFrame changes + moving many functions as separate helpers
2) SkARFingerPainting changes (name should change too)

Bug: skia:
Change-Id: I5068ce6c416a2f5d6c6c389cd63d08d5350e83e6
Reviewed-on: https://skia-review.googlesource.com/143701
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/platform_tools/android/apps/skar_java/src/main/AndroidManifest.xml b/platform_tools/android/apps/skar_java/src/main/AndroidManifest.xml
index abfacc5..efd40d1 100644
--- a/platform_tools/android/apps/skar_java/src/main/AndroidManifest.xml
+++ b/platform_tools/android/apps/skar_java/src/main/AndroidManifest.xml
@@ -33,7 +33,7 @@
       tools:ignore="GoogleAppIndexingWarning">
 
     <activity
-        android:name="com.google.ar.core.examples.java.helloskar.HelloCanvasAR"
+        android:name="com.google.skar.examples.helloskar.app.HelloCanvasAR"
         android:configChanges="orientation|screenSize"
         android:exported="true"
         android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/CanvasMatrixUtil.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/CanvasMatrixUtil.java
index bfab93e..d0d594a 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/CanvasMatrixUtil.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/CanvasMatrixUtil.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.google.skar;
 
 import android.graphics.Matrix;
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/PaintUtil.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/PaintUtil.java
index 75fa86b..b9aaa01 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/PaintUtil.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/PaintUtil.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.google.skar;
 
 import android.graphics.ColorFilter;
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/ARSurfaceView.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/ARSurfaceView.java
deleted file mode 100644
index 03185c2..0000000
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/ARSurfaceView.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.google.skar.examples.helloskar.app;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.util.AttributeSet;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-
-/**
- * SurfaceView that is overlayed on top of a GLSurfaceView. All 2D drawings can be done on this
- * surface.
- */
-public class ARSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
-
-    boolean running;
-
-    public ARSurfaceView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        SurfaceHolder holder = getHolder();
-        this.setBackgroundColor(Color.TRANSPARENT);
-        this.setZOrderOnTop(true);
-        holder.setFormat(PixelFormat.TRANSPARENT);
-        holder.addCallback(this);
-    }
-
-    public boolean isRunning() {
-        return running;
-    }
-
-    @Override
-    public void surfaceCreated(SurfaceHolder holder) {
-        running = true;
-    }
-
-    @Override
-    public void surfaceDestroyed(SurfaceHolder holder) {
-        running = false;
-    }
-
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-    }
-}
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/CanvasARSurfaceView.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/CanvasARSurfaceView.java
new file mode 100644
index 0000000..40c789d
--- /dev/null
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/CanvasARSurfaceView.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.skar.examples.helloskar.app;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.util.AttributeSet;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+/**
+ * SurfaceView that is overlayed on top of a GLSurfaceView. All Canvas 2D drawings can be done on
+ * this surface.
+ */
+
+public class CanvasARSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+
+    boolean running;
+
+    public CanvasARSurfaceView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        SurfaceHolder holder = getHolder();
+        this.setBackgroundColor(Color.TRANSPARENT);
+        this.setZOrderOnTop(true);
+        holder.setFormat(PixelFormat.TRANSPARENT);
+        holder.addCallback(this);
+    }
+
+    public boolean isRunning() {
+        return running;
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        running = true;
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        running = false;
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+    }
+}
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/FingerPainting.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/FingerPainting.java
new file mode 100644
index 0000000..e4aaf57
--- /dev/null
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/FingerPainting.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.skar.examples.helloskar.app;
+
+import android.graphics.Color;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.util.Log;
+
+import com.google.skar.examples.helloskar.helpers.TapHelper;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import java.util.ArrayList;
+
+public class FingerPainting {
+    public static class BuiltPath {
+        public Path path;
+        public int color;
+
+        BuiltPath(Path p, int c) {
+            this.path = p;
+            this.color = c;
+        }
+    }
+
+    // Points obtained by touching the screen. The first point is always brought to (0,0).
+    // All subsequent points are translated by the same amount.
+    private ArrayList<PointF> points = new ArrayList<>();
+
+    // Indices in points array that indicate the start of a new path. E.g: if 5 is added to
+    // jumpPoints, then index 5 of the points array is the start of a new path (use moveTo())
+    private ArrayList<Integer> jumpPoints = new ArrayList<>();
+
+    // Map from index (start of path) to color of path
+    private Map<Integer, Integer> indexColors = new HashMap<>();
+
+    // List of built paths (reset each frame)
+    private ArrayList<BuiltPath> paths = new ArrayList<>();
+
+    // Previous point added to the path. This points belongs to the path in local space.
+    private float[] previousLocalPoint = new float[2];
+
+    // Previous point added to the path. This points belongs to the path in global space (i.e Pose)
+    private float[] previousGlobalPoint = new float[2];
+
+    // Holds the model matrix of the first point added to such that the path can be drawn at the
+    // model location (i.e on the Plane)
+    private float[] modelMatrix;
+
+    // Currently selected color in the UI
+    private int color = Color.RED;
+
+    // True if path should be drawn using buildSmoothFromTo()
+    private boolean isSmooth;
+
+    public FingerPainting(boolean smooth) {
+        this.isSmooth = smooth;
+    }
+
+    public void setSmoothness(boolean smooth) {
+        isSmooth = smooth;
+    }
+
+    /**
+     * Given a hit location in Global space (e.g on a Plane), and the associated ScrollEvent,
+     * construct the next point in the path in Local space. The first point of the Finger Painting
+     * must be at (0,0)
+     * @param hitLocation   (x, y) coordinates of the hit position in Global space
+     * @param holdTap       ScrollEvent associated with the hit test that calls this function
+     * @return              true if point was computed and added. False otherwise.
+     */
+    public boolean computeNextPoint(float[] hitLocation, TapHelper.ScrollEvent holdTap) {
+        if (isEmpty()) {
+            // If finger painting is empty, then first point is origin. Model matrix
+            // of the finger painting is the model matrix of the first point
+            addPoint(new PointF(0, 0), true);
+
+            // Get model matrix of first point
+            setModelMatrix(modelMatrix);
+        } else {
+            // Else, construct next point given its distance from previous point
+            float localDistanceScale = 1000;
+            PointF distance = new PointF(hitLocation[0] - previousGlobalPoint[0],
+                                         hitLocation[2] - previousGlobalPoint[1]);
+
+            if (distance.length() < 0.01f) {
+                // If distance between previous stored point and current point is too
+                // small, skip it
+                return false;
+            }
+
+            // New point is distance + old point
+            PointF p = new PointF(distance.x * localDistanceScale + previousLocalPoint[0],
+                                  distance.y * localDistanceScale + previousLocalPoint[1]);
+
+            addPoint(p, holdTap.isStartOfScroll);
+        }
+        previousGlobalPoint[0] = hitLocation[0];
+        previousGlobalPoint[1] = hitLocation[1];
+        return true;
+    }
+
+    /**
+     * Constructs the Paths to be drawn (populates the paths List). Call this before drawing this
+     * Finger Painting (every frame).
+     */
+    public void buildPath() {
+        if (points.size() <= 1) {
+            // Don't build anything if the path only contains one point
+            return;
+        }
+
+        paths = new ArrayList<>();
+
+        if (isSmooth) {
+            buildSmooth();
+        } else {
+            buildRough();
+        }
+    }
+
+    /**
+     * @return 16-float matrix that takes a point from Local space to Global space (onto the Plane)
+     */
+    public float[] getModelMatrix() {
+        return modelMatrix;
+    }
+
+    /**
+     * Change currently selected color. Preferably called through a UI element (a menu)
+     * @param color   color to be selected this frame
+     */
+    public void setColor(int color) {
+        this.color = color;
+    }
+
+    /**
+     * @return List of built paths contained within this Finger Painting
+     */
+    public List<BuiltPath> getPaths() {
+        return paths;
+    }
+
+    /**
+     * Clears data contained within this Finger Painting
+     */
+    public void reset() {
+        points.clear();
+        jumpPoints.clear();
+        paths.clear();
+        indexColors.clear();
+    }
+
+    /********************** PRIVATE HELPERS **************************************/
+
+    // Adds another point to the path in Local space
+    private void addPoint(PointF p, boolean jumpPoint) {
+        points.add(p);
+        if (jumpPoint) {
+            jumpPoints.add(points.size() - 1);
+            indexColors.put(points.size() - 1, color);
+        }
+        previousLocalPoint[0] = p.x;
+        previousLocalPoint[1] = p.y;
+    }
+
+    // Builds paths of this Finger Painting using the rough algorithm
+    private void buildRough() {
+        int start = 0; // starting index of each path. 1st path starts at index 0 points list
+        for (int j = 1; j < jumpPoints.size(); j++) {
+            int finish = jumpPoints.get(j); // finishing index of current path
+            buildRoughFromTo(start, finish);
+            start = finish;
+        }
+
+        buildRoughFromTo(start, points.size());
+    }
+
+    // Builds paths of this Finger Painting using the smooth algorithm
+    private void buildSmooth() {
+        int start = 0;
+        for (int j = 1; j < jumpPoints.size(); j++) {
+            int finish = jumpPoints.get(j);
+            buildSmoothFromTo(start, finish);
+            start = finish;
+        }
+
+        buildSmoothFromTo(start, points.size());
+    }
+
+    // Builds a rough path that starts at index (start) of the points List, and ends at (finish - 1)
+    // of the points List
+    private void buildRoughFromTo(int start, int finish) {
+        Path p = new Path();
+        int c = indexColors.get(start);
+        p.moveTo(points.get(start).x, points.get(start).y);
+
+        for (int i = start + 1; i < finish; i++) {
+            p.lineTo(points.get(i).x, points.get(i).y);
+        }
+
+        BuiltPath bp = new BuiltPath(p, c);
+        paths.add(bp);
+    }
+
+    // Builds a smooth path that starts at index (start) of the points List, and ends at (finish - 1)
+    // of the points List
+    private void buildSmoothFromTo(int start, int finish) {
+        Path p = new Path();
+        int c = indexColors.get(start);
+
+        int nbPts = finish - start; // # of points within this path (not including the finish index)
+
+        // If only 2 points in path, draw a line between them
+        if (nbPts == 2) {
+            p.moveTo(points.get(start).x, points.get(start).y);
+            p.lineTo(points.get(start + 1).x, points.get(start + 1).y);
+        } else if (nbPts >= 3) {
+            // Else (3 pts +), essentially run deCasteljau
+            p.moveTo(points.get(start).x, points.get(start).y);
+            p.lineTo((points.get(start).x + points.get(start + 1).x) / 2,
+                     (points.get(start).y + points.get(start + 1).y) / 2);
+
+            for (int i = start + 1; i < finish - 1; i++) {
+                PointF p1 = points.get(i);
+                PointF p2 = points.get(i + 1);
+                p.quadTo(p1.x, p1.y, (p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
+            }
+
+            p.lineTo(points.get(finish - 1).x, points.get(finish - 1).y);
+        }
+
+        BuiltPath bp = new BuiltPath(p, c);
+        paths.add(bp);
+    }
+
+    private boolean isEmpty() { return points.isEmpty(); }
+
+    private void setModelMatrix(float[] m) {
+        modelMatrix = m;
+    }
+}
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/HelloCanvasAR.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/HelloCanvasAR.java
index 718545e..d883c69 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/HelloCanvasAR.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/HelloCanvasAR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 Google Inc. All Rights Reserved.
+ * Copyright 2018 Google LLC All Rights Reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,14 +20,13 @@
 import android.animation.ValueAnimator;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.PointF;
 import android.graphics.PorterDuff;
 import android.opengl.GLES20;
 import android.opengl.GLSurfaceView;
 import android.opengl.Matrix;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
-import android.support.design.internal.BottomNavigationMenuView;
+
 import android.support.design.widget.BottomNavigationView;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
@@ -53,12 +52,17 @@
 import com.google.ar.core.Session;
 import com.google.ar.core.Trackable;
 import com.google.ar.core.TrackingState;
-import com.google.ar.core.examples.java.common.helpers.CameraPermissionHelper;
-import com.google.ar.core.examples.java.common.helpers.DisplayRotationHelper;
-import com.google.ar.core.examples.java.common.helpers.FullScreenHelper;
-import com.google.ar.core.examples.java.common.helpers.SnackbarHelper;
-import com.google.ar.core.examples.java.common.helpers.TapHelper;
-import com.google.ar.core.examples.java.common.rendering.BackgroundRenderer;
+
+import com.google.ar.core.examples.java.helloskar.R;
+import com.google.skar.examples.helloskar.helpers.CameraPermissionHelper;
+import com.google.skar.examples.helloskar.helpers.DisplayRotationHelper;
+import com.google.skar.examples.helloskar.helpers.FullScreenHelper;
+import com.google.skar.examples.helloskar.helpers.SnackbarHelper;
+import com.google.skar.examples.helloskar.helpers.TapHelper;
+
+import com.google.skar.examples.helloskar.rendering.BackgroundRenderer;
+import com.google.skar.examples.helloskar.rendering.DrawManager;
+
 import com.google.ar.core.exceptions.CameraNotAvailableException;
 import com.google.ar.core.exceptions.UnavailableApkTooOldException;
 import com.google.ar.core.exceptions.UnavailableArcoreNotInstalledException;
@@ -79,41 +83,33 @@
  */
 
 public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Renderer {
-    public enum DrawingType {
-        circle, rect, text, animation
-    }
-
     private static final String TAG = HelloCanvasAR.class.getSimpleName();
+    private final int MAX_NUMBER_DRAWABLES = 50; // Arbitrary limit to the # of anchors to store
 
-    //Simple SurfaceView used to draw 2D objects on top of the GLSurfaceView
-    private ARSurfaceView arSurfaceView;
-    private Canvas canvas;
+    // Simple SurfaceView used to draw 2D objects on top of the GLSurfaceView
+    private CanvasARSurfaceView arSurfaceView;
     private SurfaceHolder holder;
 
-    //GLSurfaceView used to draw 3D objects & camera input
+    // GLSurfaceView used to draw 3D objects & camera input
     private GLSurfaceView glSurfaceView;
 
-    //ARSession
+    // ARSession
     private Session session;
 
-    private boolean installRequested;
-    private final SnackbarHelper messageSnackbarHelper = new SnackbarHelper();
-    private DisplayRotationHelper displayRotationHelper;
-    private TapHelper tapHelper;
-
     // OpenGL background renderer
     private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer();
 
     // 2D Renderer
     private DrawManager drawManager = new DrawManager();
-    private DrawingType currentDrawabletype = DrawingType.circle;
-    private boolean drawSmoothPainting = true;
+
+    private boolean installRequested;
+    private final SnackbarHelper messageSnackbarHelper = new SnackbarHelper();
+    private DisplayRotationHelper displayRotationHelper;
+    private TapHelper tapHelper;
 
     // Temporary matrix allocated here to reduce number of allocations for each frame.
     private final float[] anchorMatrix = new float[16];
 
-    PointF previousEvent;
-
     // Anchors created from taps used for object placing.
     private final ArrayList<Anchor> anchors = new ArrayList<>();
 
@@ -127,22 +123,22 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
 
-        Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
-        setSupportActionBar(myToolbar);
+        // Menu tool bar set up
+        Toolbar toolbar = findViewById(R.id.main_toolbar);
+        setSupportActionBar(toolbar);
 
-
-        //hide notifications bar
+        // Hide notifications bar
         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                 WindowManager.LayoutParams.FLAG_FULLSCREEN);
 
-        arSurfaceView = findViewById(R.id.arsurfaceview);
-        glSurfaceView = findViewById(R.id.glsurfaceview);
+        // Canvas Surface View set up
+        arSurfaceView = findViewById(R.id.canvas_surfaceview);
         arSurfaceView.bringToFront();
         arSurfaceView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-        displayRotationHelper = new DisplayRotationHelper(/*context=*/ this);
+        holder = arSurfaceView.getHolder();
 
         // Set up tap listener.
-        tapHelper = new TapHelper(/*context=*/ this);
+        tapHelper = new TapHelper(this);
         glSurfaceView.setOnTouchListener(tapHelper);
 
         // Set up renderer.
@@ -151,9 +147,10 @@
         glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending.
         glSurfaceView.setRenderer(this);
         glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
-
+        displayRotationHelper = new DisplayRotationHelper(this);
         installRequested = false;
 
+        // Set up finger painting palette bar
         BottomNavigationView bottomNav = findViewById(R.id.palette);
         bottomNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
             @Override
@@ -174,7 +171,7 @@
             }
         });
 
-        // Animator set up
+        // Value Animator set up
         PropertyValuesHolder propertyRadius = PropertyValuesHolder.ofFloat(PROPERTY_RADIUS, 0, 0.5f);
         animator = new ValueAnimator();
         animator.setValues(propertyRadius);
@@ -310,7 +307,7 @@
 
     @Override
     public void onDrawFrame(GL10 gl) {
-        canvas = null;
+        Canvas canvas = null;
         holder = null;
 
         // Clear screen to notify driver it should not load any pixels from previous frame.
@@ -319,38 +316,18 @@
         if (session == null) {
             return;
         }
+
         // Notify ARCore session that the view size changed so that the perspective matrix and
         // the video background can be properly adjusted.
         displayRotationHelper.updateSessionIfNeeded(session);
 
-
         try {
             session.setCameraTextureName(backgroundRenderer.getTextureId());
             Frame frame = session.update();
             Camera camera = frame.getCamera();
 
-            MotionEvent tap = tapHelper.poll();
-            if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) {
-                for (HitResult hit : frame.hitTest(tap)) {
-                    // Check if any plane was hit, and if it was hit inside the plane polygon
-                    Trackable trackable = hit.getTrackable();
-                    // Creates an anchor if a plane or an oriented point was hit.
-                    if ((trackable instanceof Plane
-                            && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())
-                            && (DrawManager.calculateDistanceToPlane(hit.getHitPose(), camera.getPose())
-                            > 0))
-                            || (trackable instanceof Point
-                            && ((Point) trackable).getOrientationMode()
-                            == OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
-                        if (anchors.size() >= 20) {
-                            anchors.get(0).detach();
-                            anchors.remove(0);
-                        }
-                        anchors.add(hit.createAnchor());
-                        break;
-                    }
-                }
-            }
+            // Query information from single tap gestures to get anchors
+            handleSingleTaps(frame, camera);
 
             // Draw background with OpenGL.
             // TODO: possibly find a way to extract texture and draw on Canvas
@@ -362,76 +339,26 @@
             }
 
             // Get projection matrix.
-            float[] projmtx = new float[16];
-            camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
-            drawManager.updateProjectionMatrix(projmtx);
+            float[] projMatrix = new float[16];
+            camera.getProjectionMatrix(projMatrix, 0, 0.1f, 100.0f);
+            drawManager.updateProjectionMatrix(projMatrix);
 
             // Get camera matrix and draw.
-            float[] viewmtx = new float[16];
-            camera.getViewMatrix(viewmtx, 0);
-            drawManager.updateViewMatrix(viewmtx);
+            float[] viewMatrix = new float[16];
+            camera.getViewMatrix(viewMatrix, 0);
+            drawManager.updateViewMatrix(viewMatrix);
 
             final float[] colorCorrectionRgba = new float[4];
             frame.getLightEstimate().getColorCorrection(colorCorrectionRgba, 0);
             drawManager.updateLightColorFilter(colorCorrectionRgba);
 
-            // Building finger painting
-            TapHelper.ScrollEvent holdTap = tapHelper.holdPoll();
-            if (holdTap != null && camera.getTrackingState() == TrackingState.TRACKING) {
-                for (HitResult hit : frame.hitTest(holdTap.e)) {
-                    // Check if any plane was hit, and if it was hit inside the plane polygon
-                    Trackable trackable = hit.getTrackable();
-                    // Creates an anchor if a plane or an oriented point was hit.
-                    if ((trackable instanceof Plane
-                            && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())
-                            && (DrawManager.calculateDistanceToPlane(hit.getHitPose(), camera.getPose())
-                            > 0))
-                            || (trackable instanceof Point
-                            && ((Point) trackable).getOrientationMode()
-                            == OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
-
-                        // Get hit point transform, apply it to the origin
-                        float[] gm = new float[16];
-                        hit.getHitPose().toMatrix(gm, 0);
-                        float[] point = {0, 0, 0, 1};
-                        Matrix.multiplyMV(point, 0, gm, 0, point, 0);
-
-                        if (drawManager.fingerPainting.isEmpty()) {
-                            drawManager.fingerPainting.addPoint(new PointF(0, 0), true);
-
-                            // Get model matrix of first point
-                            float[] m = new float[16];
-                            hit.getHitPose().toMatrix(m, 0);
-                            drawManager.fingerPainting.setModelMatrix(m);
-                        } else {
-                            float localDistanceScale = 1000;
-                            PointF distance = new PointF(point[0] - previousEvent.x,
-                                                         point[2] - previousEvent.y);
-
-                            if (distance.length() < 0.05f) {
-                                continue;
-                            }
-
-                            // New point is distance + old point
-                            PointF p = new PointF(distance.x * localDistanceScale
-                                                   + drawManager.fingerPainting.previousPoint.x,
-                                                  distance.y * localDistanceScale
-                                                   + drawManager.fingerPainting.previousPoint.y);
-
-                            drawManager.fingerPainting.addPoint(p, holdTap.isStartOfScroll);
-                        }
-
-                        previousEvent = new PointF(point[0], point[2]);
-                        break;
-                    }
-                }
-            }
+            // Query information from scrolling gestures to build finger paintings
+            handleHoldTaps(frame, camera);
 
             // Drawing on Canvas (SurfaceView)
             if (arSurfaceView.isRunning()) {
                 // Lock canvas
-                SurfaceHolder holder = arSurfaceView.getHolder();
-                Canvas canvas = holder.lockHardwareCanvas();
+                canvas = holder.lockHardwareCanvas();
                 canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
 
                 // Draw point cloud
@@ -472,6 +399,81 @@
         }
     }
 
+    /**************************** Gesture helpers ******************************/
+    /**
+     * Given a Frame and a Camera, perform hit tests on stored UI touch events. If a hit test is
+     * successful, construct an Anchor at the hit position and add it to the set of anchors.
+     * @param frame     Frame of this update() call
+     * @param camera    Camera of this update() call
+     */
+    private void handleSingleTaps(Frame frame, Camera camera) {
+        MotionEvent tap = tapHelper.poll();
+        if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) {
+            for (HitResult hit : frame.hitTest(tap)) {
+                // Check if any plane was hit, and if it was hit inside the plane polygon
+                Trackable trackable = hit.getTrackable();
+                // Creates an anchor if a plane or an oriented point was hit.
+                if ((trackable instanceof Plane
+                        && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())
+                        && (DrawManager.calculateDistanceToPlane(hit.getHitPose(), camera.getPose())
+                        > 0))
+                        || (trackable instanceof Point
+                        && ((Point) trackable).getOrientationMode()
+                        == OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
+                    if (anchors.size() >= MAX_NUMBER_DRAWABLES) {
+                        anchors.get(0).detach();
+                        anchors.remove(0);
+                    }
+                    anchors.add(hit.createAnchor());
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Given a Frame and a Camera, perform hit tests on stored UI touch events. If a hit test is
+     * successful, construct an Anchor at the hit position and add it to the set of anchors.
+     * @param frame     Frame of this update() call
+     * @param camera    Camera of this update() call
+     */
+    private void handleHoldTaps(Frame frame, Camera camera) {
+        // Building finger painting
+        TapHelper.ScrollEvent holdTap = tapHelper.holdPoll();
+        if (holdTap != null && camera.getTrackingState() == TrackingState.TRACKING) {
+            for (HitResult hit : frame.hitTest(holdTap.e)) {
+                // Check if any plane was hit, and if it was hit inside the plane polygon
+                Trackable trackable = hit.getTrackable();
+                // Creates an anchor if a plane or an oriented point was hit.
+                if ((trackable instanceof Plane
+                        && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())
+                        && (DrawManager.calculateDistanceToPlane(hit.getHitPose(), camera.getPose())
+                        > 0))
+                        || (trackable instanceof Point
+                        && ((Point) trackable).getOrientationMode()
+                        == OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
+
+                    // Get hit point transform, apply it to the origin --> point is not in hit
+                    // location on the plane
+                    float[] modelMatrix = new float[16];
+                    hit.getHitPose().toMatrix(modelMatrix, 0);
+                    float[] hitLocation = {0, 0, 0, 1};
+                    Matrix.multiplyMV(hitLocation, 0, modelMatrix, 0,
+                                      hitLocation, 0);
+
+                    if (! drawManager.fingerPainting.computeNextPoint(hitLocation, holdTap)) {
+                        // Try to add the next point to the finger painting. If return value
+                        // is false, then keep looping
+                        continue;
+                    }
+
+                    break;
+                }
+            }
+        }
+    }
+
+    /**************************** Drawing helpers ******************************/
     // Helper drawing functions that invoke drawManager
     private void drawPlanes(Canvas canvas, Camera camera) {
         drawManager.drawPlanes(canvas, camera.getPose(), session.getAllTrackables(Plane.class));
@@ -486,12 +488,11 @@
             if (anchor.getTrackingState() != TrackingState.TRACKING) {
                 continue;
             }
-            // Get the current pose of an Anchor in world space. The Anchor pose is updated
-            // during calls to session.update() as ARCore refines its estimate of the world.
+            // Get the current pose of an Anchor in world space
             anchor.getPose().toMatrix(anchorMatrix, 0);
             drawManager.modelMatrices.add(0, anchorMatrix);
 
-            switch (currentDrawabletype) {
+            switch (drawManager.currentDrawabletype) {
                 case circle:
                     drawManager.drawCircle(canvas);
                     break;
@@ -512,11 +513,12 @@
     }
 
     private void drawFingerPainting(Canvas canvas) {
-        drawManager.fingerPainting.setSmoothness(drawSmoothPainting);
+        drawManager.fingerPainting.setSmoothness(drawManager.drawSmoothPainting);
         drawManager.drawFingerPainting(canvas);
     }
 
-    // Menu functions
+    /**************************** UI helpers ******************************/
+    // Tool bar functions
     public boolean onCreateOptionsMenu(Menu menu) {
         MenuInflater inflater = getMenuInflater();
         inflater.inflate(R.menu.main_menu, menu);
@@ -529,22 +531,22 @@
                 drawManager.fingerPainting.reset();
                 return true;
             case R.id.smooth_paint:
-                drawSmoothPainting = true;
+                drawManager.drawSmoothPainting = true;
                 return true;
             case R.id.rough_paint:
-                drawSmoothPainting = false;
+                drawManager.drawSmoothPainting = false;
                 return true;
             case R.id.draw_circle:
-                currentDrawabletype = DrawingType.circle;
+                drawManager.currentDrawabletype = DrawManager.DrawingType.circle;
                 return true;
             case R.id.draw_rect:
-                currentDrawabletype = DrawingType.rect;
+                drawManager.currentDrawabletype = DrawManager.DrawingType.rect;
                 return true;
             case R.id.draw_animation:
-                currentDrawabletype = DrawingType.animation;
+                drawManager.currentDrawabletype = DrawManager.DrawingType.animation;
                 return true;
             case R.id.draw_text:
-                currentDrawabletype = DrawingType.text;
+                drawManager.currentDrawabletype = DrawManager.DrawingType.text;
                 return true;
             default:
                 return super.onOptionsItemSelected(item);
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/SkARFingerPainting.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/SkARFingerPainting.java
deleted file mode 100644
index eac2a6b..0000000
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/SkARFingerPainting.java
+++ /dev/null
@@ -1,155 +0,0 @@
-package com.google.skar.examples.helloskar.app;
-
-import android.graphics.Color;
-import android.graphics.Path;
-import android.graphics.PointF;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-import java.util.ArrayList;
-
-public class SkARFingerPainting {
-    // Points obtained by touching the screen. The first point is always brough to (0,0).
-    // All subsequent points are translated by the same amount.
-    private ArrayList<PointF> points = new ArrayList<>();
-    private ArrayList<Integer> jumpPoints = new ArrayList<>();
-    private Map<Integer, Integer> indexColors = new HashMap<>();
-    private Map<Path, Integer> pathColors = new HashMap<>();
-    private ArrayList<Path> paths = new ArrayList<>();
-    private int color = Color.RED;
-
-    // Previous point added to the path. This points belongs to the path in local space.
-    public PointF previousPoint;
-
-    // Holds the model matrix of the first point added to such that the path can be drawn at the
-    // model location (i.e on the Plane)
-    private float[] modelMatrix;
-
-    private boolean isSmooth;
-
-    public SkARFingerPainting(boolean smooth) {
-        this.isSmooth = smooth;
-    }
-
-    public boolean getSmoothness() {
-        return isSmooth;
-    }
-
-    public void setSmoothness(boolean smooth) {
-        isSmooth = smooth;
-    }
-
-    // Adds another point to the path in Local space
-    public void addPoint(PointF p, boolean jumpPoint) {
-        points.add(p);
-        if (jumpPoint) {
-            Log.i("Jumped!", Integer.toString(points.size() - 1));
-            jumpPoints.add(points.size() - 1);
-            indexColors.put(points.size() - 1, color);
-        }
-        previousPoint = p;
-    }
-
-    // Used to build a path before rendering it
-    public void buildPath() {
-        paths = new ArrayList<>();
-        if (points.size() <= 1) {
-            return;
-        }
-
-
-        if (isSmooth) {
-            int start = 0;
-            for (int j = 1; j < jumpPoints.size(); j++) {
-
-                int finish = jumpPoints.get(j);
-                buildSmoothFromTo(start, finish);
-                start = finish;
-            }
-
-            buildSmoothFromTo(start, points.size());
-        } else {
-
-            int start = 0;
-            for (int j = 1; j < jumpPoints.size(); j++) {
-                int finish = jumpPoints.get(j);
-                buildRoughFromTo(start, finish);
-                start = finish;
-            }
-
-            buildRoughFromTo(start, points.size());
-        }
-    }
-
-    private void buildRoughFromTo(int start, int finish) {
-        Path p = new Path();
-        int c = indexColors.get(start);
-        p.moveTo(points.get(start).x, points.get(start).y);
-        for (int i = start + 1; i < finish; i++) {
-            p.lineTo(points.get(i).x, points.get(i).y);
-        }
-        paths.add(p);
-        pathColors.put(p, c);
-    }
-
-    private void buildSmoothFromTo(int start, int finish) {
-        Path p = new Path();
-        int c = indexColors.get(start);
-        int nbPts = finish - start;
-        // If less than 3 points, than draw a line between the two points
-        if (nbPts <= 2 && nbPts > 1) {
-            p.moveTo(points.get(start).x, points.get(start).y);
-            p.lineTo(points.get(start + 1).x, points.get(start + 1).y);
-        } else if (nbPts >= 3){
-            // Else, essentially run deCasteljau
-            p.moveTo(points.get(start).x, points.get(start).y);
-            p.lineTo((points.get(start).x + points.get(start + 1).x) / 2,
-                     (points.get(start).y + points.get(start + 1).y) / 2);
-
-            for (int i = start + 1; i < finish - 1; i++) {
-                PointF p1 = points.get(i);
-                PointF p2 = points.get(i + 1);
-                p.quadTo(p1.x, p1.y, (p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
-            }
-
-            p.lineTo(points.get(finish - 1).x, points.get(finish - 1).y);
-        }
-        paths.add(p);
-        pathColors.put(p, c);
-    }
-
-    public boolean isEmpty() {
-        return points.isEmpty();
-    }
-
-    public float[] getModelMatrix() {
-        return modelMatrix;
-    }
-
-    public void setModelMatrix(float[] m) {
-        modelMatrix = m;
-    }
-
-    public void setColor(int color) {
-        this.color = color;
-    }
-
-    public int getPathColor(Path p) {
-        return pathColors.get(p);
-    }
-
-    public ArrayList<Path> getPaths() {
-        return paths;
-    }
-
-    public void reset() {
-        points.clear();
-        jumpPoints.clear();
-        paths.clear();
-        pathColors.clear();
-        indexColors.clear();
-    }
-}
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/CameraPermissionHelper.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/CameraPermissionHelper.java
index f8e9c33..9cab829 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/CameraPermissionHelper.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/CameraPermissionHelper.java
@@ -1,10 +1,11 @@
 /*
- * Copyright 2017 Google Inc. All Rights Reserved.
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.google.skar.examples.helloskar.helpers;
 
 import android.Manifest;
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/DisplayRotationHelper.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/DisplayRotationHelper.java
index 7e4ce81..0f06094 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/DisplayRotationHelper.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/DisplayRotationHelper.java
@@ -1,10 +1,11 @@
 /*
- * Copyright 2017 Google Inc. All Rights Reserved.
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.google.skar.examples.helloskar.helpers;
 
 import android.app.Activity;
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/FullScreenHelper.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/FullScreenHelper.java
index 3738b96..6951459 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/FullScreenHelper.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/FullScreenHelper.java
@@ -1,10 +1,11 @@
 /*
- * Copyright 2017 Google Inc. All Rights Reserved.
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.google.skar.examples.helloskar.helpers;
 
 import android.app.Activity;
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/SnackbarHelper.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/SnackbarHelper.java
index 0239349..6294f93 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/SnackbarHelper.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/SnackbarHelper.java
@@ -1,10 +1,11 @@
 /*
- * Copyright 2017 Google Inc. All Rights Reserved.
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.google.skar.examples.helloskar.helpers;
 
 import android.app.Activity;
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/TapHelper.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/TapHelper.java
index bcbfe2a..6dcfd8f 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/TapHelper.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/TapHelper.java
@@ -1,10 +1,11 @@
 /*
- * Copyright 2017 Google Inc. All Rights Reserved.
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,19 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.google.skar.examples.helloskar.helpers;
 
 import android.content.Context;
-import android.util.Log;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnTouchListener;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Queue;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
 
@@ -72,7 +68,9 @@
                                 if (e2.getPointerCount() == 1 && e1.getPointerCount() == 1) {
                                     previousScroll = isScrolling;
                                     isScrolling = true;
-                                    queuedFingerHold.offer(new ScrollEvent(e2, startedScrolling()));
+
+                                    queuedFingerHold.offer(new ScrollEvent(e2, isStartedScrolling()));
+
                                     return true;
                                 }
                                 return false;
@@ -97,10 +95,6 @@
 
     public ScrollEvent holdPoll() { return queuedFingerHold.poll(); }
 
-    public boolean startedScrolling() {
-        return isScrolling && !previousScroll;
-    }
-
     @Override
     public boolean onTouch(View view, MotionEvent motionEvent) {
         boolean val = gestureDetector.onTouchEvent(motionEvent);
@@ -113,4 +107,5 @@
         }
         return val;
     }
+    private boolean isStartedScrolling() { return isScrolling && !previousScroll; }
 }
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/BackgroundRenderer.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/BackgroundRenderer.java
index b6b5a85..acf3dc5 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/BackgroundRenderer.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/BackgroundRenderer.java
@@ -1,10 +1,11 @@
 /*
- * Copyright 2017 Google Inc. All Rights Reserved.
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.google.skar.examples.helloskar.rendering;
 
 import android.content.Context;
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/DrawManager.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/DrawManager.java
index 2ef85b0..35bcf70 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/DrawManager.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/DrawManager.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.google.skar.examples.helloskar.rendering;
 
 import android.content.Context;
@@ -21,7 +37,8 @@
 import com.google.ar.core.TrackingState;
 import com.google.skar.CanvasMatrixUtil;
 import com.google.skar.PaintUtil;
-import com.google.skar.SkARFingerPainting;
+import com.google.skar.examples.helloskar.app.FingerPainting;
+
 import java.io.IOException;
 import java.nio.FloatBuffer;
 import java.util.ArrayList;
@@ -34,6 +51,14 @@
  */
 
 public class DrawManager {
+    public enum DrawingType {
+        circle, rect, text, animation
+    }
+
+    // App defaults
+    public DrawManager.DrawingType currentDrawabletype = DrawManager.DrawingType.circle;
+    public boolean drawSmoothPainting = true;
+
     private float[] projectionMatrix = new float[16];
     private float[] viewMatrix = new float[16];
     private float viewportWidth;
@@ -41,7 +66,7 @@
     private ColorFilter lightFilter;
     private BitmapShader planeShader;
     public ArrayList<float[]> modelMatrices = new ArrayList<>();
-    public SkARFingerPainting fingerPainting = new SkARFingerPainting(false);
+    public FingerPainting fingerPainting = new FingerPainting(false);
 
     public void updateViewport(float width, float height) {
         viewportWidth = width;
@@ -165,11 +190,11 @@
         p.setStrokeWidth(30f);
         p.setAlpha(120);
 
-        for (Path path : fingerPainting.getPaths()) {
-            if (path.isEmpty()) {
+        for (FingerPainting.BuiltPath bp : fingerPainting.getPaths()) {
+            if (bp.path.isEmpty()) {
                 continue;
             }
-            p.setColor(fingerPainting.getPathColor(path));
+            p.setColor(bp.color);
 
             // Scaling issues appear to happen when drawing a Path and transforming the Canvas
             // directly with a matrix on Android versions less than P. Ideally we would
@@ -179,12 +204,12 @@
                 // Transform applied through canvas
                 canvas.save();
                 canvas.setMatrix(mvpv);
-                canvas.drawPath(path, p);
+                canvas.drawPath(bp.path, p);
                 canvas.restore();
             } else {
                 // Transform path directly
                 Path pathDst = new Path();
-                path.transform(mvpv, pathDst);
+                bp.path.transform(mvpv, pathDst);
 
                 // Draw dest path
                 canvas.save();
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/ShaderUtil.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/ShaderUtil.java
index ce33a45..468b47d 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/ShaderUtil.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/ShaderUtil.java
@@ -1,10 +1,11 @@
 /*
- * Copyright 2017 Google Inc. All Rights Reserved.
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.google.skar.examples.helloskar.rendering;
 
 import android.content.Context;
diff --git a/platform_tools/android/apps/skar_java/src/main/res/layout/activity_main.xml b/platform_tools/android/apps/skar_java/src/main/res/layout/activity_main.xml
index d1737c5..a08144a 100644
--- a/platform_tools/android/apps/skar_java/src/main/res/layout/activity_main.xml
+++ b/platform_tools/android/apps/skar_java/src/main/res/layout/activity_main.xml
@@ -18,14 +18,14 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    tools:context="com.google.ar.core.examples.java.helloskar.HelloCanvasAR">
+    tools:context="com.google.skar.examples.helloskar.app.HelloCanvasAR">
 
     <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
         <android.support.v7.widget.Toolbar
-            android:id="@+id/my_toolbar"
+            android:id="@+id/main_toolbar"
             android:layout_width="match_parent"
             android:layout_height="?attr/actionBarSize"
             android:background="?attr/colorPrimary"
@@ -33,13 +33,13 @@
             android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
             app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
 
-        <com.google.ar.core.examples.java.helloskar.ARSurfaceView
-            android:id="@+id/arsurfaceview"
+        <com.google.skar.examples.helloskar.app.CanvasARSurfaceView
+            android:id="@+id/canvas_surfaceview"
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
 
         <android.opengl.GLSurfaceView
-            android:id="@+id/glsurfaceview"
+            android:id="@+id/gl_surfaceview"
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
         <android.support.design.widget.BottomNavigationView