diff options
Diffstat (limited to 'platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers')
5 files changed, 455 insertions, 0 deletions
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; + } +} |