aboutsummaryrefslogtreecommitdiffhomepage
path: root/platform_tools
diff options
context:
space:
mode:
authorGravatar ziadb <ziadb@google.com>2018-07-26 15:31:01 -0400
committerGravatar Ziad Ben Hadj-Alouane <ziadb@google.com>2018-07-26 19:37:04 +0000
commitf27b479f957df19b44913f81509aaef7af0d19ce (patch)
tree365ae0b64b702010a5758df9f1a57392119134e7 /platform_tools
parent3ab3b562b11014797747ef9f7b0464cebb1b530b (diff)
SkAR Java: refactored project structure
1) Changed file paths 2) Added helpers that were missing previously Bug: skia: Change-Id: Idb4194fcef815a23277a17670acf46255a47451d Reviewed-on: https://skia-review.googlesource.com/143680 Reviewed-by: Mike Reed <reed@google.com>
Diffstat (limited to 'platform_tools')
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/ARSurfaceView.java (renamed from platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/ARSurfaceView.java)2
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/HelloCanvasAR.java (renamed from platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/HelloCanvasAR.java)2
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/SkARFingerPainting.java (renamed from platform_tools/android/apps/skar_java/src/main/java/com/google/skar/SkARFingerPainting.java)2
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/CameraPermissionHelper.java65
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/DisplayRotationHelper.java112
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/FullScreenHelper.java49
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/SnackbarHelper.java113
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/TapHelper.java116
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/BackgroundRenderer.java190
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/DrawManager.java (renamed from platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/DrawManager.java)2
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/ShaderUtil.java99
11 files changed, 748 insertions, 4 deletions
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/ARSurfaceView.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/ARSurfaceView.java
index eaa336cc9e..03185c2cd3 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/ARSurfaceView.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/ARSurfaceView.java
@@ -1,4 +1,4 @@
-package com.google.ar.core.examples.java.helloskar;
+package com.google.skar.examples.helloskar.app;
import android.content.Context;
import android.graphics.Color;
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/HelloCanvasAR.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/HelloCanvasAR.java
index cc4ad93b99..718545e72b 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/HelloCanvasAR.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/HelloCanvasAR.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.ar.core.examples.java.helloskar;
+package com.google.skar.examples.helloskar.app;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/SkARFingerPainting.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/SkARFingerPainting.java
index d63295857d..eac2a6bb33 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/SkARFingerPainting.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/SkARFingerPainting.java
@@ -1,4 +1,4 @@
-package com.google.skar;
+package com.google.skar.examples.helloskar.app;
import android.graphics.Color;
import android.graphics.Path;
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
new file mode 100644
index 0000000000..f8e9c33af3
--- /dev/null
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/CameraPermissionHelper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017 Google Inc. 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.helpers;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.provider.Settings;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+
+/**
+ * Helper to ask camera permission.
+ */
+public final class CameraPermissionHelper {
+ private static final int CAMERA_PERMISSION_CODE = 0;
+ private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA;
+
+ /**
+ * Check to see we have the necessary permissions for this app.
+ */
+ public static boolean hasCameraPermission(Activity activity) {
+ return ContextCompat.checkSelfPermission(activity, CAMERA_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Check to see we have the necessary permissions for this app, and ask for them if we don't.
+ */
+ public static void requestCameraPermission(Activity activity) {
+ ActivityCompat.requestPermissions(
+ activity, new String[]{CAMERA_PERMISSION}, CAMERA_PERMISSION_CODE);
+ }
+
+ /**
+ * Check to see if we need to show the rationale for this permission.
+ */
+ public static boolean shouldShowRequestPermissionRationale(Activity activity) {
+ return ActivityCompat.shouldShowRequestPermissionRationale(activity, CAMERA_PERMISSION);
+ }
+
+ /**
+ * Launch Application Setting to grant permission.
+ */
+ public static void launchPermissionSettings(Activity activity) {
+ Intent intent = new Intent();
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ intent.setData(Uri.fromParts("package", activity.getPackageName(), null));
+ activity.startActivity(intent);
+ }
+}
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
new file mode 100644
index 0000000000..7e4ce81c29
--- /dev/null
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/DisplayRotationHelper.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017 Google Inc. 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.helpers;
+
+import android.app.Activity;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.view.Display;
+import android.view.WindowManager;
+
+import com.google.ar.core.Session;
+
+/**
+ * Helper to track the display rotations. In particular, the 180 degree rotations are not notified
+ * by the onSurfaceChanged() callback, and thus they require listening to the android display
+ * events.
+ */
+public final class DisplayRotationHelper implements DisplayListener {
+ private boolean viewportChanged;
+ private int viewportWidth;
+ private int viewportHeight;
+ private final Context context;
+ private final Display display;
+
+ /**
+ * Constructs the DisplayRotationHelper but does not register the listener yet.
+ *
+ * @param context the Android {@link Context}.
+ */
+ public DisplayRotationHelper(Context context) {
+ this.context = context;
+ display = context.getSystemService(WindowManager.class).getDefaultDisplay();
+ }
+
+ /**
+ * Registers the display listener. Should be called from {@link Activity#onResume()}.
+ */
+ public void onResume() {
+ context.getSystemService(DisplayManager.class).registerDisplayListener(this, null);
+ }
+
+ /**
+ * Unregisters the display listener. Should be called from {@link Activity#onPause()}.
+ */
+ public void onPause() {
+ context.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+ }
+
+ /**
+ * Records a change in surface dimensions. This will be later used by {@link
+ * #updateSessionIfNeeded(Session)}. Should be called from {@link
+ * android.opengl.GLSurfaceView.Renderer
+ * #onSurfaceChanged(javax.microedition.khronos.opengles.GL10, int, int)}.
+ *
+ * @param width the updated width of the surface.
+ * @param height the updated height of the surface.
+ */
+ public void onSurfaceChanged(int width, int height) {
+ viewportWidth = width;
+ viewportHeight = height;
+ viewportChanged = true;
+ }
+
+ /**
+ * Updates the session display geometry if a change was posted either by {@link
+ * #onSurfaceChanged(int, int)} call or by {@link #onDisplayChanged(int)} system callback. This
+ * function should be called explicitly before each call to {@link Session#update()}. This
+ * function will also clear the 'pending update' (viewportChanged) flag.
+ *
+ * @param session the {@link Session} object to update if display geometry changed.
+ */
+ public void updateSessionIfNeeded(Session session) {
+ if (viewportChanged) {
+ int displayRotation = display.getRotation();
+ session.setDisplayGeometry(displayRotation, viewportWidth, viewportHeight);
+ viewportChanged = false;
+ }
+ }
+
+ /**
+ * Returns the current rotation state of android display. Same as {@link Display#getRotation()}.
+ */
+ public int getRotation() {
+ return display.getRotation();
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ viewportChanged = true;
+ }
+}
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
new file mode 100644
index 0000000000..3738b963dc
--- /dev/null
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/FullScreenHelper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 Google Inc. 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.helpers;
+
+import android.app.Activity;
+import android.view.View;
+import android.view.WindowManager;
+
+/**
+ * Helper to set up the Android full screen mode.
+ */
+public final class FullScreenHelper {
+ /**
+ * Sets the Android fullscreen flags. Expected to be called from {@link
+ * Activity#onWindowFocusChanged(boolean hasFocus)}.
+ *
+ * @param activity the Activity on which the full screen mode will be set.
+ * @param hasFocus the hasFocus flag passed from the {@link Activity#onWindowFocusChanged(boolean
+ * hasFocus)} callback.
+ */
+ public static void setFullScreenOnWindowFocusChanged(Activity activity, boolean hasFocus) {
+ if (hasFocus) {
+ // https://developer.android.com/training/system-ui/immersive.html#sticky
+ activity
+ .getWindow()
+ .getDecorView()
+ .setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+ }
+}
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
new file mode 100644
index 0000000000..02393499e9
--- /dev/null
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/SnackbarHelper.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2017 Google Inc. 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.helpers;
+
+import android.app.Activity;
+import android.support.design.widget.BaseTransientBottomBar;
+import android.support.design.widget.Snackbar;
+import android.view.View;
+
+/**
+ * Helper to manage the sample snackbar. Hides the Android boilerplate code, and exposes simpler
+ * methods.
+ */
+public final class SnackbarHelper {
+ private static final int BACKGROUND_COLOR = 0xbf323232;
+ private Snackbar messageSnackbar;
+
+ private enum DismissBehavior {HIDE, SHOW, FINISH}
+
+ ;
+
+ public boolean isShowing() {
+ return messageSnackbar != null;
+ }
+
+ /**
+ * Shows a snackbar with a given message.
+ */
+ public void showMessage(Activity activity, String message) {
+ show(activity, message, DismissBehavior.HIDE);
+ }
+
+ /**
+ * Shows a snackbar with a given message, and a dismiss button.
+ */
+ public void showMessageWithDismiss(Activity activity, String message) {
+ show(activity, message, DismissBehavior.SHOW);
+ }
+
+ /**
+ * Shows a snackbar with a given error message. When dismissed, will finish the activity. Useful
+ * for notifying errors, where no further interaction with the activity is possible.
+ */
+ public void showError(Activity activity, String errorMessage) {
+ show(activity, errorMessage, DismissBehavior.FINISH);
+ }
+
+ /**
+ * Hides the currently showing snackbar, if there is one. Safe to call from any thread. Safe to
+ * call even if snackbar is not shown.
+ */
+ public void hide(Activity activity) {
+ activity.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (messageSnackbar != null) {
+ messageSnackbar.dismiss();
+ }
+ messageSnackbar = null;
+ }
+ });
+ }
+
+ private void show(
+ final Activity activity, final String message, final DismissBehavior dismissBehavior) {
+ activity.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ messageSnackbar =
+ Snackbar.make(
+ activity.findViewById(android.R.id.content),
+ message,
+ Snackbar.LENGTH_INDEFINITE);
+ messageSnackbar.getView().setBackgroundColor(BACKGROUND_COLOR);
+ if (dismissBehavior != DismissBehavior.HIDE) {
+ messageSnackbar.setAction(
+ "Dismiss",
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ messageSnackbar.dismiss();
+ }
+ });
+ if (dismissBehavior == DismissBehavior.FINISH) {
+ messageSnackbar.addCallback(
+ new BaseTransientBottomBar.BaseCallback<Snackbar>() {
+ @Override
+ public void onDismissed(Snackbar transientBottomBar, int event) {
+ super.onDismissed(transientBottomBar, event);
+ activity.finish();
+ }
+ });
+ }
+ }
+ messageSnackbar.show();
+ }
+ });
+ }
+}
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
new file mode 100644
index 0000000000..bcbfe2aa98
--- /dev/null
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/TapHelper.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2017 Google Inc. 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.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;
+
+/**
+ * Helper to detect taps using Android GestureDetector, and pass the taps between UI thread and
+ * render thread.
+ */
+public final class TapHelper implements OnTouchListener {
+ private final GestureDetector gestureDetector;
+ private final BlockingQueue<MotionEvent> queuedSingleTaps = new ArrayBlockingQueue<>(16);
+ private final BlockingQueue<ScrollEvent> queuedFingerHold = new ArrayBlockingQueue<>(16);
+ private boolean isScrolling = false;
+ private boolean previousScroll = true;
+
+ public static class ScrollEvent {
+ public MotionEvent e;
+ public boolean isStartOfScroll;
+
+ public ScrollEvent(MotionEvent e, boolean isStartOfScroll) {
+ this.e = e;
+ this.isStartOfScroll = isStartOfScroll;
+ }
+ }
+
+ /**
+ * Creates the tap helper.
+ *
+ * @param context the application's context.
+ */
+ public TapHelper(Context context) {
+ gestureDetector =
+ new GestureDetector(
+ context,
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ // Queue tap if there is space. Tap is lost if queue is full.
+ queuedSingleTaps.offer(e);
+ return true;
+ }
+
+ @Override
+ public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ // Queue motion events when scrolling
+ if (e2.getPointerCount() == 1 && e1.getPointerCount() == 1) {
+ previousScroll = isScrolling;
+ isScrolling = true;
+ queuedFingerHold.offer(new ScrollEvent(e2, startedScrolling()));
+ return true;
+ }
+ return false;
+ }
+
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Polls for a tap.
+ *
+ * @return if a tap was queued, a MotionEvent for the tap. Otherwise null if no taps are queued.
+ */
+ public MotionEvent poll() {
+ return queuedSingleTaps.poll();
+ }
+
+ 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);
+
+ // If finger is up + is scrolling: don't scroll anymore, and empty Touch Hold queue
+ if (motionEvent.getAction() == MotionEvent.ACTION_UP && isScrolling) {
+ previousScroll = true;
+ isScrolling = false;
+ queuedFingerHold.clear();
+ }
+ return val;
+ }
+}
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
new file mode 100644
index 0000000000..b6b5a859b8
--- /dev/null
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/BackgroundRenderer.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2017 Google Inc. 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;
+import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+
+import com.google.ar.core.Frame;
+import com.google.ar.core.Session;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * This class renders the AR background from camera feed. It creates and hosts the texture given to
+ * ARCore to be filled with the camera image.
+ */
+public class BackgroundRenderer {
+ private static final String TAG = BackgroundRenderer.class.getSimpleName();
+
+ // Shader names.
+ private static final String VERTEX_SHADER_NAME = "shaders/screenquad.vert";
+ private static final String FRAGMENT_SHADER_NAME = "shaders/screenquad.frag";
+
+ private static final int COORDS_PER_VERTEX = 3;
+ private static final int TEXCOORDS_PER_VERTEX = 2;
+ private static final int FLOAT_SIZE = 4;
+
+ private FloatBuffer quadVertices;
+ private FloatBuffer quadTexCoord;
+ private FloatBuffer quadTexCoordTransformed;
+
+ private int quadProgram;
+
+ private int quadPositionParam;
+ private int quadTexCoordParam;
+ private int textureId = -1;
+
+ public BackgroundRenderer() {
+ }
+
+ public int getTextureId() {
+ return textureId;
+ }
+
+ /**
+ * Allocates and initializes OpenGL resources needed by the background renderer. Must be called on
+ * the OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10,
+ * EGLConfig)}.
+ *
+ * @param context Needed to access shader source.
+ */
+ public void createOnGlThread(Context context) throws IOException {
+ // Generate the background texture.
+ int[] textures = new int[1];
+ GLES20.glGenTextures(1, textures, 0);
+ textureId = textures[0];
+ int textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
+ GLES20.glBindTexture(textureTarget, textureId);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
+
+ int numVertices = 4;
+ if (numVertices != QUAD_COORDS.length / COORDS_PER_VERTEX) {
+ throw new RuntimeException("Unexpected number of vertices in BackgroundRenderer.");
+ }
+
+ ByteBuffer bbVertices = ByteBuffer.allocateDirect(QUAD_COORDS.length * FLOAT_SIZE);
+ bbVertices.order(ByteOrder.nativeOrder());
+ quadVertices = bbVertices.asFloatBuffer();
+ quadVertices.put(QUAD_COORDS);
+ quadVertices.position(0);
+
+ ByteBuffer bbTexCoords =
+ ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE);
+ bbTexCoords.order(ByteOrder.nativeOrder());
+ quadTexCoord = bbTexCoords.asFloatBuffer();
+ quadTexCoord.put(QUAD_TEXCOORDS);
+ quadTexCoord.position(0);
+
+ ByteBuffer bbTexCoordsTransformed =
+ ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE);
+ bbTexCoordsTransformed.order(ByteOrder.nativeOrder());
+ quadTexCoordTransformed = bbTexCoordsTransformed.asFloatBuffer();
+
+ int vertexShader =
+ ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME);
+ int fragmentShader =
+ ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME);
+
+ quadProgram = GLES20.glCreateProgram();
+ GLES20.glAttachShader(quadProgram, vertexShader);
+ GLES20.glAttachShader(quadProgram, fragmentShader);
+ GLES20.glLinkProgram(quadProgram);
+ GLES20.glUseProgram(quadProgram);
+
+ ShaderUtil.checkGLError(TAG, "Program creation");
+
+ quadPositionParam = GLES20.glGetAttribLocation(quadProgram, "a_Position");
+ quadTexCoordParam = GLES20.glGetAttribLocation(quadProgram, "a_TexCoord");
+
+ ShaderUtil.checkGLError(TAG, "Program parameters");
+ }
+
+ /**
+ * Draws the AR background image. The image will be drawn such that virtual content rendered with
+ * the matrices provided by {@link com.google.ar.core.Camera#getViewMatrix(float[], int)} and
+ * {@link com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)} will
+ * accurately follow static physical objects. This must be called <b>before</b> drawing virtual
+ * content.
+ *
+ * @param frame The last {@code Frame} returned by {@link Session#update()}.
+ */
+ public void draw(Frame frame) {
+ // If display rotation changed (also includes view size change), we need to re-query the uv
+ // coordinates for the screen rect, as they may have changed as well.
+ if (frame.hasDisplayGeometryChanged()) {
+ frame.transformDisplayUvCoords(quadTexCoord, quadTexCoordTransformed);
+ }
+
+ // No need to test or write depth, the screen quad has arbitrary depth, and is expected
+ // to be drawn first.
+ GLES20.glDisable(GLES20.GL_DEPTH_TEST);
+ GLES20.glDepthMask(false);
+
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
+
+ GLES20.glUseProgram(quadProgram);
+
+ // Set the vertex positions.
+ GLES20.glVertexAttribPointer(
+ quadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadVertices);
+
+ // Set the texture coordinates.
+ GLES20.glVertexAttribPointer(
+ quadTexCoordParam,
+ TEXCOORDS_PER_VERTEX,
+ GLES20.GL_FLOAT,
+ false,
+ 0,
+ quadTexCoordTransformed);
+
+ // Enable vertex arrays
+ GLES20.glEnableVertexAttribArray(quadPositionParam);
+ GLES20.glEnableVertexAttribArray(quadTexCoordParam);
+
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+
+ // Disable vertex arrays
+ GLES20.glDisableVertexAttribArray(quadPositionParam);
+ GLES20.glDisableVertexAttribArray(quadTexCoordParam);
+
+ // Restore the depth state for further drawing.
+ GLES20.glDepthMask(true);
+ GLES20.glEnable(GLES20.GL_DEPTH_TEST);
+
+ ShaderUtil.checkGLError(TAG, "Draw");
+ }
+
+ private static final float[] QUAD_COORDS =
+ new float[]{
+ -1.0f, -1.0f, 0.0f, -1.0f, +1.0f, 0.0f, +1.0f, -1.0f, 0.0f, +1.0f, +1.0f, 0.0f,
+ };
+
+ private static final float[] QUAD_TEXCOORDS =
+ new float[]{
+ 0.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f,
+ };
+}
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/DrawManager.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/DrawManager.java
index 724ab17c02..2ef85b049e 100644
--- a/platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/DrawManager.java
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/DrawManager.java
@@ -1,4 +1,4 @@
-package com.google.ar.core.examples.java.helloskar;
+package com.google.skar.examples.helloskar.rendering;
import android.content.Context;
import android.graphics.Bitmap;
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
new file mode 100644
index 0000000000..ce33a45966
--- /dev/null
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/ShaderUtil.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017 Google Inc. 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;
+import android.opengl.GLES20;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+/**
+ * Shader helper functions.
+ */
+public class ShaderUtil {
+ /**
+ * Converts a raw text file, saved as a resource, into an OpenGL ES shader.
+ *
+ * @param type The type of shader we will be creating.
+ * @param filename The filename of the asset file about to be turned into a shader.
+ * @return The shader object handler.
+ */
+ public static int loadGLShader(String tag, Context context, int type, String filename)
+ throws IOException {
+ String code = readRawTextFileFromAssets(context, filename);
+ int shader = GLES20.glCreateShader(type);
+ GLES20.glShaderSource(shader, code);
+ GLES20.glCompileShader(shader);
+
+ // Get the compilation status.
+ final int[] compileStatus = new int[1];
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
+
+ // If the compilation failed, delete the shader.
+ if (compileStatus[0] == 0) {
+ Log.e(tag, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader));
+ GLES20.glDeleteShader(shader);
+ shader = 0;
+ }
+
+ if (shader == 0) {
+ throw new RuntimeException("Error creating shader.");
+ }
+
+ return shader;
+ }
+
+ /**
+ * Checks if we've had an error inside of OpenGL ES, and if so what that error is.
+ *
+ * @param label Label to report in case of error.
+ * @throws RuntimeException If an OpenGL error is detected.
+ */
+ public static void checkGLError(String tag, String label) {
+ int lastError = GLES20.GL_NO_ERROR;
+ // Drain the queue of all errors.
+ int error;
+ while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+ Log.e(tag, label + ": glError " + error);
+ lastError = error;
+ }
+ if (lastError != GLES20.GL_NO_ERROR) {
+ throw new RuntimeException(label + ": glError " + lastError);
+ }
+ }
+
+ /**
+ * Converts a raw text file into a string.
+ *
+ * @param filename The filename of the asset file about to be turned into a shader.
+ * @return The context of the text file, or null in case of error.
+ */
+ private static String readRawTextFileFromAssets(Context context, String filename)
+ throws IOException {
+ try (InputStream inputStream = context.getAssets().open(filename);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+ StringBuilder sb = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line).append("\n");
+ }
+ return sb.toString();
+ }
+ }
+}