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
17package com.android.incallui.videosurface.impl;
18
19import android.graphics.Point;
20import android.graphics.SurfaceTexture;
21import android.view.Surface;
22import android.view.TextureView;
23import android.view.View;
24import com.android.dialer.common.LogUtil;
25import com.android.incallui.videosurface.protocol.VideoSurfaceDelegate;
26import com.android.incallui.videosurface.protocol.VideoSurfaceTexture;
27import java.util.Locale;
28import 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 */
34public 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
44  public VideoSurfaceTextureImpl(@SurfaceType int surfaceType) {
45    this.surfaceType = surfaceType;
46  }
47
48  @Override
49  public void setDelegate(VideoSurfaceDelegate delegate) {
50    LogUtil.i("VideoSurfaceTextureImpl.setDelegate", "delegate: " + delegate + " " + toString());
51    this.delegate = delegate;
52  }
53
54  @Override
55  public int getSurfaceType() {
56    return surfaceType;
57  }
58
59  @Override
60  public Surface getSavedSurface() {
61    return savedSurface;
62  }
63
64  @Override
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
76  public Point getSurfaceDimensions() {
77    return surfaceDimensions;
78  }
79
80  @Override
81  public void setSourceVideoDimensions(Point sourceVideoDimensions) {
82    this.sourceVideoDimensions = sourceVideoDimensions;
83  }
84
85  @Override
86  public Point getSourceVideoDimensions() {
87    return sourceVideoDimensions;
88  }
89
90  @Override
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
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
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
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
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
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
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
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
232    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
233
234    @Override
235    public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
236  }
237
238  private class OnClickListener implements View.OnClickListener {
239    @Override
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