1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.incallui.videosurface.impl;
18 
19 import android.graphics.Point;
20 import android.graphics.SurfaceTexture;
21 import android.view.Surface;
22 import android.view.TextureView;
23 import android.view.View;
24 import com.android.dialer.common.LogUtil;
25 import com.android.incallui.videosurface.protocol.VideoSurfaceDelegate;
26 import com.android.incallui.videosurface.protocol.VideoSurfaceTexture;
27 import java.util.Locale;
28 import java.util.Objects;
29 
30 /**
31  * Represents a {@link TextureView} and its associated {@link SurfaceTexture} and {@link Surface}.
32  * Used to manage the lifecycle of these objects across device orientation changes.
33  */
34 public class VideoSurfaceTextureImpl implements VideoSurfaceTexture {
35   @SurfaceType private final int surfaceType;
36   private VideoSurfaceDelegate delegate;
37   private TextureView textureView;
38   private Surface savedSurface;
39   private SurfaceTexture savedSurfaceTexture;
40   private Point surfaceDimensions;
41   private Point sourceVideoDimensions;
42   private boolean isDoneWithSurface;
43 
VideoSurfaceTextureImpl(@urfaceType int surfaceType)44   public VideoSurfaceTextureImpl(@SurfaceType int surfaceType) {
45     this.surfaceType = surfaceType;
46   }
47 
48   @Override
setDelegate(VideoSurfaceDelegate delegate)49   public void setDelegate(VideoSurfaceDelegate delegate) {
50     LogUtil.i("VideoSurfaceTextureImpl.setDelegate", "delegate: " + delegate + " " + toString());
51     this.delegate = delegate;
52   }
53 
54   @Override
getSurfaceType()55   public int getSurfaceType() {
56     return surfaceType;
57   }
58 
59   @Override
getSavedSurface()60   public Surface getSavedSurface() {
61     return savedSurface;
62   }
63 
64   @Override
setSurfaceDimensions(Point surfaceDimensions)65   public void setSurfaceDimensions(Point surfaceDimensions) {
66     LogUtil.i(
67         "VideoSurfaceTextureImpl.setSurfaceDimensions",
68         "surfaceDimensions: " + surfaceDimensions + " " + toString());
69     this.surfaceDimensions = surfaceDimensions;
70     if (surfaceDimensions != null && savedSurfaceTexture != null) {
71       savedSurfaceTexture.setDefaultBufferSize(surfaceDimensions.x, surfaceDimensions.y);
72     }
73   }
74 
75   @Override
getSurfaceDimensions()76   public Point getSurfaceDimensions() {
77     return surfaceDimensions;
78   }
79 
80   @Override
setSourceVideoDimensions(Point sourceVideoDimensions)81   public void setSourceVideoDimensions(Point sourceVideoDimensions) {
82     this.sourceVideoDimensions = sourceVideoDimensions;
83   }
84 
85   @Override
getSourceVideoDimensions()86   public Point getSourceVideoDimensions() {
87     return sourceVideoDimensions;
88   }
89 
90   @Override
attachToTextureView(TextureView textureView)91   public void attachToTextureView(TextureView textureView) {
92     if (this.textureView == textureView) {
93       return;
94     }
95     LogUtil.i("VideoSurfaceTextureImpl.attachToTextureView", toString());
96 
97     if (this.textureView != null) {
98       this.textureView.setOnClickListener(null);
99       this.textureView.setSurfaceTextureListener(null);
100     }
101 
102     this.textureView = textureView;
103     textureView.setSurfaceTextureListener(new SurfaceTextureListener());
104     textureView.setOnClickListener(new OnClickListener());
105 
106     boolean areSameSurfaces = Objects.equals(savedSurfaceTexture, textureView.getSurfaceTexture());
107     LogUtil.i("VideoSurfaceTextureImpl.attachToTextureView", "areSameSurfaces: " + areSameSurfaces);
108     if (savedSurfaceTexture != null && !areSameSurfaces) {
109       textureView.setSurfaceTexture(savedSurfaceTexture);
110       if (surfaceDimensions != null && createSurface(surfaceDimensions.x, surfaceDimensions.y)) {
111         onSurfaceCreated();
112       }
113     }
114     isDoneWithSurface = false;
115   }
116 
117   @Override
setDoneWithSurface()118   public void setDoneWithSurface() {
119     LogUtil.i("VideoSurfaceTextureImpl.setDoneWithSurface", toString());
120     isDoneWithSurface = true;
121     if (textureView != null && textureView.isAvailable()) {
122       return;
123     }
124     if (savedSurface != null) {
125       onSurfaceReleased();
126       savedSurface.release();
127       savedSurface = null;
128     }
129     if (savedSurfaceTexture != null) {
130       savedSurfaceTexture.release();
131       savedSurfaceTexture = null;
132     }
133   }
134 
createSurface(int width, int height)135   private boolean createSurface(int width, int height) {
136     LogUtil.i(
137         "VideoSurfaceTextureImpl.createSurface",
138         "width: " + width + ", height: " + height + " " + toString());
139     savedSurfaceTexture.setDefaultBufferSize(width, height);
140     if (savedSurface != null) {
141       savedSurface.release();
142     }
143     savedSurface = new Surface(savedSurfaceTexture);
144     return true;
145   }
146 
onSurfaceCreated()147   private void onSurfaceCreated() {
148     if (delegate != null) {
149       delegate.onSurfaceCreated(this);
150     } else {
151       LogUtil.e("VideoSurfaceTextureImpl.onSurfaceCreated", "delegate is null. " + toString());
152     }
153   }
154 
onSurfaceReleased()155   private void onSurfaceReleased() {
156     if (delegate != null) {
157       delegate.onSurfaceReleased(this);
158     } else {
159       LogUtil.e("VideoSurfaceTextureImpl.onSurfaceReleased", "delegate is null. " + toString());
160     }
161   }
162 
163   @Override
toString()164   public String toString() {
165     return String.format(
166         Locale.US,
167         "VideoSurfaceTextureImpl<%s%s%s%s>",
168         (surfaceType == SURFACE_TYPE_LOCAL ? "local, " : "remote, "),
169         (savedSurface == null ? "no-surface, " : ""),
170         (savedSurfaceTexture == null ? "no-texture, " : ""),
171         (surfaceDimensions == null
172             ? "(-1 x -1)"
173             : (surfaceDimensions.x + " x " + surfaceDimensions.y)));
174   }
175 
176   private class SurfaceTextureListener implements TextureView.SurfaceTextureListener {
177     @Override
onSurfaceTextureAvailable(SurfaceTexture newSurfaceTexture, int width, int height)178     public void onSurfaceTextureAvailable(SurfaceTexture newSurfaceTexture, int width, int height) {
179       LogUtil.i(
180           "SurfaceTextureListener.onSurfaceTextureAvailable",
181           "newSurfaceTexture: "
182               + newSurfaceTexture
183               + " "
184               + VideoSurfaceTextureImpl.this.toString());
185 
186       // Where there is no saved {@link SurfaceTexture} available, use the newly created one.
187       // If a saved {@link SurfaceTexture} is available, we are re-creating after an
188       // orientation change.
189       boolean surfaceCreated;
190       if (savedSurfaceTexture == null) {
191         savedSurfaceTexture = newSurfaceTexture;
192         surfaceCreated = createSurface(width, height);
193       } else {
194         // A saved SurfaceTexture was found.
195         LogUtil.i(
196             "SurfaceTextureListener.onSurfaceTextureAvailable", "replacing with cached surface...");
197         textureView.setSurfaceTexture(savedSurfaceTexture);
198         surfaceCreated = true;
199       }
200 
201       // Inform the delegate that the surface is available.
202       if (surfaceCreated) {
203         onSurfaceCreated();
204       }
205     }
206 
207     @Override
onSurfaceTextureDestroyed(SurfaceTexture destroyedSurfaceTexture)208     public boolean onSurfaceTextureDestroyed(SurfaceTexture destroyedSurfaceTexture) {
209       LogUtil.i(
210           "SurfaceTextureListener.onSurfaceTextureDestroyed",
211           "destroyedSurfaceTexture: "
212               + destroyedSurfaceTexture
213               + " "
214               + VideoSurfaceTextureImpl.this.toString());
215       if (delegate != null) {
216         delegate.onSurfaceDestroyed(VideoSurfaceTextureImpl.this);
217       } else {
218         LogUtil.e("SurfaceTextureListener.onSurfaceTextureDestroyed", "delegate is null");
219       }
220 
221       if (isDoneWithSurface) {
222         onSurfaceReleased();
223         if (savedSurface != null) {
224           savedSurface.release();
225           savedSurface = null;
226         }
227       }
228       return isDoneWithSurface;
229     }
230 
231     @Override
onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)232     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
233 
234     @Override
onSurfaceTextureUpdated(SurfaceTexture surface)235     public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
236   }
237 
238   private class OnClickListener implements View.OnClickListener {
239     @Override
onClick(View view)240     public void onClick(View view) {
241       if (delegate != null) {
242         delegate.onSurfaceClick(VideoSurfaceTextureImpl.this);
243       } else {
244         LogUtil.e("OnClickListener.onClick", "delegate is null");
245       }
246     }
247   }
248 }
249