mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Refactor GlViewGroup to ViewRenderer
GlViewGroup doesn't work properly as an actual ViewGroup. For example, it doesn't support addition of child views after instantiation. This change turns the class into a renderer, which is also more consistent with other classes in the package. PiperOrigin-RevId: 275322295
This commit is contained in:
parent
0c0a67bb93
commit
64786c6ce4
3 changed files with 157 additions and 132 deletions
|
|
@ -21,8 +21,10 @@ import android.graphics.SurfaceTexture;
|
||||||
import android.opengl.Matrix;
|
import android.opengl.Matrix;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
import android.view.View;
|
||||||
import androidx.annotation.BinderThread;
|
import androidx.annotation.BinderThread;
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.CallSuper;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
@ -30,9 +32,9 @@ import androidx.annotation.UiThread;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.ui.PlayerControlView;
|
import com.google.android.exoplayer2.ui.PlayerControlView;
|
||||||
import com.google.android.exoplayer2.ui.spherical.GlViewGroup;
|
|
||||||
import com.google.android.exoplayer2.ui.spherical.PointerRenderer;
|
import com.google.android.exoplayer2.ui.spherical.PointerRenderer;
|
||||||
import com.google.android.exoplayer2.ui.spherical.SceneRenderer;
|
import com.google.android.exoplayer2.ui.spherical.SceneRenderer;
|
||||||
|
import com.google.android.exoplayer2.ui.spherical.ViewRenderer;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.vr.ndk.base.DaydreamApi;
|
import com.google.vr.ndk.base.DaydreamApi;
|
||||||
import com.google.vr.sdk.base.AndroidCompat;
|
import com.google.vr.sdk.base.AndroidCompat;
|
||||||
|
|
@ -71,18 +73,18 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
||||||
// If a custom theme isn't specified, the Context's theme is used. For VR Activities, this is
|
// If a custom theme isn't specified, the Context's theme is used. For VR Activities, this is
|
||||||
// the old Android default theme rather than a modern theme. Override this with a custom theme.
|
// the old Android default theme rather than a modern theme. Override this with a custom theme.
|
||||||
Context theme = new ContextThemeWrapper(this, R.style.ExoVrTheme);
|
Context theme = new ContextThemeWrapper(this, R.style.ExoVrTheme);
|
||||||
GlViewGroup glViewGroup = new GlViewGroup(theme, R.layout.exo_vr_ui);
|
View viewGroup = LayoutInflater.from(theme).inflate(R.layout.exo_vr_ui, /* root= */ null);
|
||||||
|
|
||||||
playerControlView = Assertions.checkNotNull(glViewGroup.findViewById(R.id.controller));
|
ViewRenderer viewRenderer = new ViewRenderer(/* context= */ this, gvrView, viewGroup);
|
||||||
|
|
||||||
|
playerControlView = Assertions.checkNotNull(viewGroup.findViewById(R.id.controller));
|
||||||
playerControlView.setShowVrButton(true);
|
playerControlView.setShowVrButton(true);
|
||||||
playerControlView.setVrButtonListener(v -> exit());
|
playerControlView.setVrButtonListener(v -> exit());
|
||||||
|
|
||||||
sceneRenderer = new SceneRenderer();
|
sceneRenderer = new SceneRenderer();
|
||||||
PointerRenderer pointerRenderer = new PointerRenderer();
|
PointerRenderer pointerRenderer = new PointerRenderer();
|
||||||
Renderer renderer = new Renderer(sceneRenderer, pointerRenderer, glViewGroup);
|
Renderer renderer = new Renderer(sceneRenderer, pointerRenderer, viewRenderer);
|
||||||
|
|
||||||
// Attach glViewGroup to gvrView in order to properly handle UI events.
|
|
||||||
gvrView.addView(glViewGroup);
|
|
||||||
// Standard GvrView configuration
|
// Standard GvrView configuration
|
||||||
gvrView.setEGLConfigChooser(
|
gvrView.setEGLConfigChooser(
|
||||||
8, 8, 8, 8, // RGBA bits.
|
8, 8, 8, 8, // RGBA bits.
|
||||||
|
|
@ -104,7 +106,7 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
||||||
new ControllerManager(/* context= */ this, new ControllerManagerEventListener());
|
new ControllerManager(/* context= */ this, new ControllerManagerEventListener());
|
||||||
Controller controller = controllerManager.getController();
|
Controller controller = controllerManager.getController();
|
||||||
ControllerEventListener controllerEventListener =
|
ControllerEventListener controllerEventListener =
|
||||||
new ControllerEventListener(controller, pointerRenderer, glViewGroup);
|
new ControllerEventListener(controller, pointerRenderer, viewRenderer);
|
||||||
controller.setEventListener(controllerEventListener);
|
controller.setEventListener(controllerEventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,14 +233,14 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
||||||
|
|
||||||
private final SceneRenderer sceneRenderer;
|
private final SceneRenderer sceneRenderer;
|
||||||
private final PointerRenderer pointerRenderer;
|
private final PointerRenderer pointerRenderer;
|
||||||
private final GlViewGroup glViewGroup;
|
private final ViewRenderer viewRenderer;
|
||||||
private final float[] viewProjectionMatrix;
|
private final float[] viewProjectionMatrix;
|
||||||
|
|
||||||
public Renderer(
|
public Renderer(
|
||||||
SceneRenderer sceneRenderer, PointerRenderer pointerRenderer, GlViewGroup glViewGroup) {
|
SceneRenderer sceneRenderer, PointerRenderer pointerRenderer, ViewRenderer viewRenderer) {
|
||||||
this.sceneRenderer = sceneRenderer;
|
this.sceneRenderer = sceneRenderer;
|
||||||
this.pointerRenderer = pointerRenderer;
|
this.pointerRenderer = pointerRenderer;
|
||||||
this.glViewGroup = glViewGroup;
|
this.viewRenderer = viewRenderer;
|
||||||
viewProjectionMatrix = new float[16];
|
viewProjectionMatrix = new float[16];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,8 +252,8 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
||||||
Matrix.multiplyMM(
|
Matrix.multiplyMM(
|
||||||
viewProjectionMatrix, 0, eye.getPerspective(Z_NEAR, Z_FAR), 0, eye.getEyeView(), 0);
|
viewProjectionMatrix, 0, eye.getPerspective(Z_NEAR, Z_FAR), 0, eye.getEyeView(), 0);
|
||||||
sceneRenderer.drawFrame(viewProjectionMatrix, eye.getType() == Eye.Type.RIGHT);
|
sceneRenderer.drawFrame(viewProjectionMatrix, eye.getType() == Eye.Type.RIGHT);
|
||||||
if (glViewGroup.isVisible()) {
|
if (viewRenderer.isVisible()) {
|
||||||
glViewGroup.getRenderer().draw(viewProjectionMatrix);
|
viewRenderer.draw(viewProjectionMatrix);
|
||||||
pointerRenderer.draw(viewProjectionMatrix);
|
pointerRenderer.draw(viewProjectionMatrix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -262,7 +264,7 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onSurfaceCreated(EGLConfig config) {
|
public void onSurfaceCreated(EGLConfig config) {
|
||||||
onSurfaceTextureAvailable(sceneRenderer.init());
|
onSurfaceTextureAvailable(sceneRenderer.init());
|
||||||
glViewGroup.getRenderer().init();
|
viewRenderer.init();
|
||||||
pointerRenderer.init();
|
pointerRenderer.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,7 +273,7 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRendererShutdown() {
|
public void onRendererShutdown() {
|
||||||
glViewGroup.getRenderer().shutdown();
|
viewRenderer.shutdown();
|
||||||
pointerRenderer.shutdown();
|
pointerRenderer.shutdown();
|
||||||
sceneRenderer.shutdown();
|
sceneRenderer.shutdown();
|
||||||
}
|
}
|
||||||
|
|
@ -281,16 +283,16 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
||||||
|
|
||||||
private final Controller controller;
|
private final Controller controller;
|
||||||
private final PointerRenderer pointerRenderer;
|
private final PointerRenderer pointerRenderer;
|
||||||
private final GlViewGroup glViewGroup;
|
private final ViewRenderer viewRenderer;
|
||||||
private final float[] controllerOrientationMatrix;
|
private final float[] controllerOrientationMatrix;
|
||||||
private boolean clickButtonDown;
|
private boolean clickButtonDown;
|
||||||
private boolean appButtonDown;
|
private boolean appButtonDown;
|
||||||
|
|
||||||
public ControllerEventListener(
|
public ControllerEventListener(
|
||||||
Controller controller, PointerRenderer pointerRenderer, GlViewGroup glViewGroup) {
|
Controller controller, PointerRenderer pointerRenderer, ViewRenderer viewRenderer) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
this.pointerRenderer = pointerRenderer;
|
this.pointerRenderer = pointerRenderer;
|
||||||
this.glViewGroup = glViewGroup;
|
this.viewRenderer = viewRenderer;
|
||||||
controllerOrientationMatrix = new float[16];
|
controllerOrientationMatrix = new float[16];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -319,7 +321,7 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispatchClick(int action, float yaw, float pitch) {
|
private void dispatchClick(int action, float yaw, float pitch) {
|
||||||
boolean clickedOnView = glViewGroup.simulateClick(action, yaw, pitch);
|
boolean clickedOnView = viewRenderer.simulateClick(action, yaw, pitch);
|
||||||
if (action == MotionEvent.ACTION_DOWN && !clickedOnView) {
|
if (action == MotionEvent.ACTION_DOWN && !clickedOnView) {
|
||||||
togglePlayerControlVisibility();
|
togglePlayerControlVisibility();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* 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.android.exoplayer2.ui.spherical;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.PointF;
|
|
||||||
import android.graphics.PorterDuff;
|
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import androidx.annotation.AnyThread;
|
|
||||||
import androidx.annotation.UiThread;
|
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
|
||||||
|
|
||||||
/** This View uses standard Android APIs to render its child Views to a texture. */
|
|
||||||
public final class GlViewGroup extends FrameLayout {
|
|
||||||
|
|
||||||
private final CanvasRenderer canvasRenderer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param context The Context the view is running in, through which it can access the current
|
|
||||||
* theme, resources, etc.
|
|
||||||
* @param layoutId ID for an XML layout resource to load (e.g., * <code>R.layout.main_page</code>)
|
|
||||||
*/
|
|
||||||
public GlViewGroup(Context context, int layoutId) {
|
|
||||||
super(context);
|
|
||||||
this.canvasRenderer = new CanvasRenderer();
|
|
||||||
|
|
||||||
LayoutInflater.from(context).inflate(layoutId, this);
|
|
||||||
|
|
||||||
measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
|
||||||
int width = getMeasuredWidth();
|
|
||||||
int height = getMeasuredHeight();
|
|
||||||
Assertions.checkState(width > 0 && height > 0);
|
|
||||||
canvasRenderer.setSize(width, height);
|
|
||||||
setLayoutParams(new FrameLayout.LayoutParams(width, height));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns whether the view is currently visible. */
|
|
||||||
@UiThread
|
|
||||||
public boolean isVisible() {
|
|
||||||
int childCount = getChildCount();
|
|
||||||
for (int i = 0; i < childCount; i++) {
|
|
||||||
if (getChildAt(i).getVisibility() == VISIBLE) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispatchDraw(Canvas notUsed) {
|
|
||||||
Canvas glCanvas = canvasRenderer.lockCanvas();
|
|
||||||
if (glCanvas == null) {
|
|
||||||
// This happens if Android tries to draw this View before GL initialization completes. We need
|
|
||||||
// to retry until the draw call happens after GL invalidation.
|
|
||||||
postInvalidate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the canvas first.
|
|
||||||
glCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
|
|
||||||
// Have Android render the child views.
|
|
||||||
super.dispatchDraw(glCanvas);
|
|
||||||
// Commit the changes.
|
|
||||||
canvasRenderer.unlockCanvasAndPost(glCanvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simulates a click on the view.
|
|
||||||
*
|
|
||||||
* @param action Click action.
|
|
||||||
* @param yaw Yaw of the click's orientation in radians.
|
|
||||||
* @param pitch Pitch of the click's orientation in radians.
|
|
||||||
* @return Whether the click was simulated. If false then the view is not visible or the click was
|
|
||||||
* outside of its bounds.
|
|
||||||
*/
|
|
||||||
@UiThread
|
|
||||||
public boolean simulateClick(int action, float yaw, float pitch) {
|
|
||||||
if (!isVisible()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
PointF point = canvasRenderer.translateClick(yaw, pitch);
|
|
||||||
if (point == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
long now = SystemClock.uptimeMillis();
|
|
||||||
MotionEvent event = MotionEvent.obtain(now, now, action, point.x, point.y, /* metaState= */ 1);
|
|
||||||
dispatchTouchEvent(event);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@AnyThread
|
|
||||||
public CanvasRenderer getRenderer() {
|
|
||||||
return canvasRenderer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* 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.android.exoplayer2.ui.spherical;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.PointF;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
|
||||||
|
/** Renders a {@link View} on a quad and supports simulated clicks on the view. */
|
||||||
|
public final class ViewRenderer {
|
||||||
|
|
||||||
|
private final CanvasRenderer canvasRenderer;
|
||||||
|
private final View view;
|
||||||
|
private final InternalFrameLayout frameLayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param context A context.
|
||||||
|
* @param parentView The parent view.
|
||||||
|
* @param view The view to render.
|
||||||
|
*/
|
||||||
|
public ViewRenderer(Context context, ViewGroup parentView, View view) {
|
||||||
|
this.canvasRenderer = new CanvasRenderer();
|
||||||
|
this.view = view;
|
||||||
|
// Wrap the view in an internal view that redirects rendering.
|
||||||
|
frameLayout = new InternalFrameLayout(context, view, canvasRenderer);
|
||||||
|
canvasRenderer.setSize(frameLayout.getMeasuredWidth(), frameLayout.getMeasuredHeight());
|
||||||
|
// The internal view must be added to the parent to ensure proper delivery of UI events.
|
||||||
|
parentView.addView(frameLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Finishes constructing this object on the GL Thread. */
|
||||||
|
public void init() {
|
||||||
|
canvasRenderer.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the view as a quad.
|
||||||
|
*
|
||||||
|
* @param viewProjectionMatrix Array of floats containing the quad's 4x4 perspective matrix in the
|
||||||
|
* {@link android.opengl.Matrix} format.
|
||||||
|
*/
|
||||||
|
public void draw(float[] viewProjectionMatrix) {
|
||||||
|
canvasRenderer.draw(viewProjectionMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Frees GL resources. */
|
||||||
|
public void shutdown() {
|
||||||
|
canvasRenderer.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns whether the view is currently visible. */
|
||||||
|
@UiThread
|
||||||
|
public boolean isVisible() {
|
||||||
|
return view.getVisibility() == View.VISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates a click on the view.
|
||||||
|
*
|
||||||
|
* @param action Click action.
|
||||||
|
* @param yaw Yaw of the click's orientation in radians.
|
||||||
|
* @param pitch Pitch of the click's orientation in radians.
|
||||||
|
* @return Whether the click was simulated. If false then the view is not visible or the click was
|
||||||
|
* outside of its bounds.
|
||||||
|
*/
|
||||||
|
@UiThread
|
||||||
|
public boolean simulateClick(int action, float yaw, float pitch) {
|
||||||
|
if (!isVisible()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Nullable PointF point = canvasRenderer.translateClick(yaw, pitch);
|
||||||
|
if (point == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
long now = SystemClock.uptimeMillis();
|
||||||
|
MotionEvent event = MotionEvent.obtain(now, now, action, point.x, point.y, /* metaState= */ 1);
|
||||||
|
frameLayout.dispatchTouchEvent(event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class InternalFrameLayout extends FrameLayout {
|
||||||
|
|
||||||
|
private final CanvasRenderer canvasRenderer;
|
||||||
|
|
||||||
|
public InternalFrameLayout(Context context, View wrappedView, CanvasRenderer canvasRenderer) {
|
||||||
|
super(context);
|
||||||
|
this.canvasRenderer = canvasRenderer;
|
||||||
|
addView(wrappedView);
|
||||||
|
measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||||
|
int width = getMeasuredWidth();
|
||||||
|
int height = getMeasuredHeight();
|
||||||
|
Assertions.checkState(width > 0 && height > 0);
|
||||||
|
setLayoutParams(new FrameLayout.LayoutParams(width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispatchDraw(Canvas notUsed) {
|
||||||
|
@Nullable Canvas glCanvas = canvasRenderer.lockCanvas();
|
||||||
|
if (glCanvas == null) {
|
||||||
|
// This happens if Android tries to draw this View before GL initialization completes. We
|
||||||
|
// need to retry until the draw call happens after GL invalidation.
|
||||||
|
postInvalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the canvas first.
|
||||||
|
glCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
|
||||||
|
// Have Android render the child views.
|
||||||
|
super.dispatchDraw(glCanvas);
|
||||||
|
// Commit the changes.
|
||||||
|
canvasRenderer.unlockCanvasAndPost(glCanvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue