mirror of
https://github.com/samsonjs/media.git
synced 2026-03-31 10:25:48 +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.os.Bundle;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import androidx.annotation.BinderThread;
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.Nullable;
|
||||
|
|
@ -30,9 +32,9 @@ import androidx.annotation.UiThread;
|
|||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
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.SceneRenderer;
|
||||
import com.google.android.exoplayer2.ui.spherical.ViewRenderer;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.vr.ndk.base.DaydreamApi;
|
||||
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
|
||||
// the old Android default theme rather than a modern theme. Override this with a custom theme.
|
||||
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.setVrButtonListener(v -> exit());
|
||||
|
||||
sceneRenderer = new SceneRenderer();
|
||||
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
|
||||
gvrView.setEGLConfigChooser(
|
||||
8, 8, 8, 8, // RGBA bits.
|
||||
|
|
@ -104,7 +106,7 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
|||
new ControllerManager(/* context= */ this, new ControllerManagerEventListener());
|
||||
Controller controller = controllerManager.getController();
|
||||
ControllerEventListener controllerEventListener =
|
||||
new ControllerEventListener(controller, pointerRenderer, glViewGroup);
|
||||
new ControllerEventListener(controller, pointerRenderer, viewRenderer);
|
||||
controller.setEventListener(controllerEventListener);
|
||||
}
|
||||
|
||||
|
|
@ -231,14 +233,14 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
|||
|
||||
private final SceneRenderer sceneRenderer;
|
||||
private final PointerRenderer pointerRenderer;
|
||||
private final GlViewGroup glViewGroup;
|
||||
private final ViewRenderer viewRenderer;
|
||||
private final float[] viewProjectionMatrix;
|
||||
|
||||
public Renderer(
|
||||
SceneRenderer sceneRenderer, PointerRenderer pointerRenderer, GlViewGroup glViewGroup) {
|
||||
SceneRenderer sceneRenderer, PointerRenderer pointerRenderer, ViewRenderer viewRenderer) {
|
||||
this.sceneRenderer = sceneRenderer;
|
||||
this.pointerRenderer = pointerRenderer;
|
||||
this.glViewGroup = glViewGroup;
|
||||
this.viewRenderer = viewRenderer;
|
||||
viewProjectionMatrix = new float[16];
|
||||
}
|
||||
|
||||
|
|
@ -250,8 +252,8 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
|||
Matrix.multiplyMM(
|
||||
viewProjectionMatrix, 0, eye.getPerspective(Z_NEAR, Z_FAR), 0, eye.getEyeView(), 0);
|
||||
sceneRenderer.drawFrame(viewProjectionMatrix, eye.getType() == Eye.Type.RIGHT);
|
||||
if (glViewGroup.isVisible()) {
|
||||
glViewGroup.getRenderer().draw(viewProjectionMatrix);
|
||||
if (viewRenderer.isVisible()) {
|
||||
viewRenderer.draw(viewProjectionMatrix);
|
||||
pointerRenderer.draw(viewProjectionMatrix);
|
||||
}
|
||||
}
|
||||
|
|
@ -262,7 +264,7 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
|||
@Override
|
||||
public void onSurfaceCreated(EGLConfig config) {
|
||||
onSurfaceTextureAvailable(sceneRenderer.init());
|
||||
glViewGroup.getRenderer().init();
|
||||
viewRenderer.init();
|
||||
pointerRenderer.init();
|
||||
}
|
||||
|
||||
|
|
@ -271,7 +273,7 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
|||
|
||||
@Override
|
||||
public void onRendererShutdown() {
|
||||
glViewGroup.getRenderer().shutdown();
|
||||
viewRenderer.shutdown();
|
||||
pointerRenderer.shutdown();
|
||||
sceneRenderer.shutdown();
|
||||
}
|
||||
|
|
@ -281,16 +283,16 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
|||
|
||||
private final Controller controller;
|
||||
private final PointerRenderer pointerRenderer;
|
||||
private final GlViewGroup glViewGroup;
|
||||
private final ViewRenderer viewRenderer;
|
||||
private final float[] controllerOrientationMatrix;
|
||||
private boolean clickButtonDown;
|
||||
private boolean appButtonDown;
|
||||
|
||||
public ControllerEventListener(
|
||||
Controller controller, PointerRenderer pointerRenderer, GlViewGroup glViewGroup) {
|
||||
Controller controller, PointerRenderer pointerRenderer, ViewRenderer viewRenderer) {
|
||||
this.controller = controller;
|
||||
this.pointerRenderer = pointerRenderer;
|
||||
this.glViewGroup = glViewGroup;
|
||||
this.viewRenderer = viewRenderer;
|
||||
controllerOrientationMatrix = new float[16];
|
||||
}
|
||||
|
||||
|
|
@ -319,7 +321,7 @@ public abstract class GvrPlayerActivity extends GvrActivity {
|
|||
}
|
||||
|
||||
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) {
|
||||
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