| /* |
| * Copyright (C) 2014 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.android.incallui.videosurface.impl; |
| |
| import android.graphics.Point; |
| import android.graphics.SurfaceTexture; |
| import android.view.Surface; |
| import android.view.TextureView; |
| import android.view.View; |
| import com.android.dialer.common.LogUtil; |
| import com.android.incallui.videosurface.protocol.VideoSurfaceDelegate; |
| import com.android.incallui.videosurface.protocol.VideoSurfaceTexture; |
| import java.util.Locale; |
| import java.util.Objects; |
| |
| /** |
| * Represents a {@link TextureView} and its associated {@link SurfaceTexture} and {@link Surface}. |
| * Used to manage the lifecycle of these objects across device orientation changes. |
| */ |
| public class VideoSurfaceTextureImpl implements VideoSurfaceTexture { |
| @SurfaceType private final int surfaceType; |
| private VideoSurfaceDelegate delegate; |
| private TextureView textureView; |
| private Surface savedSurface; |
| private SurfaceTexture savedSurfaceTexture; |
| private Point surfaceDimensions; |
| private Point sourceVideoDimensions; |
| private boolean isDoneWithSurface; |
| |
| public VideoSurfaceTextureImpl(@SurfaceType int surfaceType) { |
| this.surfaceType = surfaceType; |
| } |
| |
| @Override |
| public void setDelegate(VideoSurfaceDelegate delegate) { |
| LogUtil.i("VideoSurfaceTextureImpl.setDelegate", "delegate: " + delegate + " " + toString()); |
| this.delegate = delegate; |
| } |
| |
| @Override |
| public int getSurfaceType() { |
| return surfaceType; |
| } |
| |
| @Override |
| public Surface getSavedSurface() { |
| return savedSurface; |
| } |
| |
| @Override |
| public void setSurfaceDimensions(Point surfaceDimensions) { |
| LogUtil.i( |
| "VideoSurfaceTextureImpl.setSurfaceDimensions", |
| "surfaceDimensions: " + surfaceDimensions + " " + toString()); |
| this.surfaceDimensions = surfaceDimensions; |
| if (surfaceDimensions != null && savedSurfaceTexture != null) { |
| savedSurfaceTexture.setDefaultBufferSize(surfaceDimensions.x, surfaceDimensions.y); |
| } |
| } |
| |
| @Override |
| public Point getSurfaceDimensions() { |
| return surfaceDimensions; |
| } |
| |
| @Override |
| public void setSourceVideoDimensions(Point sourceVideoDimensions) { |
| this.sourceVideoDimensions = sourceVideoDimensions; |
| } |
| |
| @Override |
| public Point getSourceVideoDimensions() { |
| return sourceVideoDimensions; |
| } |
| |
| @Override |
| public void attachToTextureView(TextureView textureView) { |
| if (this.textureView == textureView) { |
| return; |
| } |
| LogUtil.i("VideoSurfaceTextureImpl.attachToTextureView", toString()); |
| |
| if (this.textureView != null) { |
| this.textureView.setOnClickListener(null); |
| // Don't clear the surface texture listener. This is important because our listener prevents |
| // the surface from being released so that it can be reused later. |
| } |
| |
| this.textureView = textureView; |
| textureView.setSurfaceTextureListener(new SurfaceTextureListener()); |
| textureView.setOnClickListener(new OnClickListener()); |
| |
| boolean areSameSurfaces = Objects.equals(savedSurfaceTexture, textureView.getSurfaceTexture()); |
| LogUtil.i("VideoSurfaceTextureImpl.attachToTextureView", "areSameSurfaces: " + areSameSurfaces); |
| if (savedSurfaceTexture != null && !areSameSurfaces) { |
| textureView.setSurfaceTexture(savedSurfaceTexture); |
| if (surfaceDimensions != null && createSurface(surfaceDimensions.x, surfaceDimensions.y)) { |
| onSurfaceCreated(); |
| } |
| } |
| isDoneWithSurface = false; |
| } |
| |
| @Override |
| public void setDoneWithSurface() { |
| LogUtil.i("VideoSurfaceTextureImpl.setDoneWithSurface", toString()); |
| isDoneWithSurface = true; |
| if (textureView != null && textureView.isAvailable()) { |
| return; |
| } |
| if (savedSurface != null) { |
| onSurfaceReleased(); |
| savedSurface.release(); |
| savedSurface = null; |
| } |
| if (savedSurfaceTexture != null) { |
| savedSurfaceTexture.release(); |
| savedSurfaceTexture = null; |
| } |
| } |
| |
| private boolean createSurface(int width, int height) { |
| LogUtil.i( |
| "VideoSurfaceTextureImpl.createSurface", |
| "width: " + width + ", height: " + height + " " + toString()); |
| if (savedSurfaceTexture != null) { |
| savedSurfaceTexture.setDefaultBufferSize(width, height); |
| savedSurface = new Surface(savedSurfaceTexture); |
| return true; |
| } |
| return false; |
| } |
| |
| private void onSurfaceCreated() { |
| if (delegate != null) { |
| delegate.onSurfaceCreated(this); |
| } else { |
| LogUtil.e("VideoSurfaceTextureImpl.onSurfaceCreated", "delegate is null. " + toString()); |
| } |
| } |
| |
| private void onSurfaceReleased() { |
| if (delegate != null) { |
| delegate.onSurfaceReleased(this); |
| } else { |
| LogUtil.e("VideoSurfaceTextureImpl.onSurfaceReleased", "delegate is null. " + toString()); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return String.format( |
| Locale.US, |
| "VideoSurfaceTextureImpl<%s%s%s%s>", |
| (surfaceType == SURFACE_TYPE_LOCAL ? "local, " : "remote, "), |
| (savedSurface == null ? "no-surface, " : ""), |
| (savedSurfaceTexture == null ? "no-texture, " : ""), |
| (surfaceDimensions == null |
| ? "(-1 x -1)" |
| : (surfaceDimensions.x + " x " + surfaceDimensions.y))); |
| } |
| |
| private class SurfaceTextureListener implements TextureView.SurfaceTextureListener { |
| @Override |
| public void onSurfaceTextureAvailable(SurfaceTexture newSurfaceTexture, int width, int height) { |
| LogUtil.i( |
| "SurfaceTextureListener.onSurfaceTextureAvailable", |
| "newSurfaceTexture: " |
| + newSurfaceTexture |
| + " " |
| + VideoSurfaceTextureImpl.this.toString()); |
| |
| // Where there is no saved {@link SurfaceTexture} available, use the newly created one. |
| // If a saved {@link SurfaceTexture} is available, we are re-creating after an |
| // orientation change. |
| boolean surfaceCreated; |
| if (savedSurfaceTexture == null) { |
| savedSurfaceTexture = newSurfaceTexture; |
| surfaceCreated = createSurface(width, height); |
| } else { |
| // A saved SurfaceTexture was found. |
| LogUtil.i( |
| "SurfaceTextureListener.onSurfaceTextureAvailable", "replacing with cached surface..."); |
| textureView.setSurfaceTexture(savedSurfaceTexture); |
| surfaceCreated = true; |
| } |
| |
| // Inform the delegate that the surface is available. |
| if (surfaceCreated) { |
| onSurfaceCreated(); |
| } |
| } |
| |
| @Override |
| public boolean onSurfaceTextureDestroyed(SurfaceTexture destroyedSurfaceTexture) { |
| LogUtil.i( |
| "SurfaceTextureListener.onSurfaceTextureDestroyed", |
| "destroyedSurfaceTexture: " |
| + destroyedSurfaceTexture |
| + " " |
| + VideoSurfaceTextureImpl.this.toString()); |
| if (delegate != null) { |
| delegate.onSurfaceDestroyed(VideoSurfaceTextureImpl.this); |
| } else { |
| LogUtil.e("SurfaceTextureListener.onSurfaceTextureDestroyed", "delegate is null"); |
| } |
| |
| if (isDoneWithSurface) { |
| onSurfaceReleased(); |
| if (savedSurface != null) { |
| savedSurface.release(); |
| savedSurface = null; |
| } |
| } |
| return isDoneWithSurface; |
| } |
| |
| @Override |
| public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {} |
| |
| @Override |
| public void onSurfaceTextureUpdated(SurfaceTexture surface) {} |
| } |
| |
| private class OnClickListener implements View.OnClickListener { |
| @Override |
| public void onClick(View view) { |
| if (delegate != null) { |
| delegate.onSurfaceClick(VideoSurfaceTextureImpl.this); |
| } else { |
| LogUtil.e("OnClickListener.onClick", "delegate is null"); |
| } |
| } |
| } |
| } |