• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.test;
18 
19 import android.annotation.NonNull;
20 import android.app.Activity;
21 import android.graphics.Canvas;
22 import android.graphics.Color;
23 import android.graphics.Paint;
24 import android.graphics.Point;
25 import android.graphics.Rect;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.HandlerThread;
29 import android.view.SurfaceHolder;
30 import android.view.SurfaceView;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import android.view.WindowManager;
34 import android.view.WindowMetrics;
35 import android.widget.Button;
36 import android.widget.LinearLayout;
37 import android.widget.Switch;
38 import android.window.SurfaceSyncGroup;
39 
40 /**
41  * Test app that allows the user to resize the SurfaceView and have the new buffer sync with the
42  * main window. This tests that {@link SurfaceSyncGroup} is working correctly.
43  */
44 public class SurfaceViewSyncActivity extends Activity implements SurfaceHolder.Callback {
45     private static final String TAG = "SurfaceViewSyncActivity";
46 
47     private SurfaceView mSurfaceView;
48     private boolean mLastExpanded = true;
49 
50     private RenderingThread mRenderingThread;
51 
52     private Button mExpandButton;
53     private Switch mEnableSyncSwitch;
54 
55     private SurfaceSyncGroup mSyncGroup;
56 
57     @Override
onCreate(Bundle savedInstanceState)58     protected void onCreate(Bundle savedInstanceState) {
59         super.onCreate(savedInstanceState);
60         setContentView(R.layout.activity_surfaceview_sync);
61         mSurfaceView = findViewById(R.id.surface_view);
62         mSurfaceView.getHolder().addCallback(this);
63 
64         WindowManager windowManager = getWindowManager();
65         WindowMetrics metrics = windowManager.getCurrentWindowMetrics();
66         Rect bounds = metrics.getBounds();
67 
68         LinearLayout container = findViewById(R.id.container);
69         mExpandButton = findViewById(R.id.expand_sv);
70         mEnableSyncSwitch = findViewById(R.id.enable_sync_switch);
71         mExpandButton.setOnClickListener(view -> updateSurfaceViewSize(bounds, container));
72 
73         mRenderingThread = new RenderingThread(mSurfaceView.getHolder());
74     }
75 
updateSurfaceViewSize(Rect bounds, View container)76     private void updateSurfaceViewSize(Rect bounds, View container) {
77         if (mSyncGroup != null) {
78             return;
79         }
80 
81         final float height;
82         if (mLastExpanded) {
83             height = bounds.height() / 2f;
84             mExpandButton.setText("EXPAND SV");
85         } else {
86             height = bounds.height() / 1.5f;
87             mExpandButton.setText("COLLAPSE SV");
88         }
89         mLastExpanded = !mLastExpanded;
90 
91         if (mEnableSyncSwitch.isChecked()) {
92             mSyncGroup = new SurfaceSyncGroup(TAG);
93             mSyncGroup.add(container.getRootSurfaceControl(), null /* runnable */);
94         }
95 
96         ViewGroup.LayoutParams svParams = mSurfaceView.getLayoutParams();
97         svParams.height = (int) height;
98         mSurfaceView.setLayoutParams(svParams);
99     }
100 
101     @Override
surfaceCreated(@onNull SurfaceHolder holder)102     public void surfaceCreated(@NonNull SurfaceHolder holder) {
103         final Canvas canvas = holder.lockCanvas();
104         canvas.drawARGB(255, 255, 0, 0);
105         holder.unlockCanvasAndPost(canvas);
106         mRenderingThread.startRendering();
107         mRenderingThread.renderFrame(null, mSurfaceView.getWidth(), mSurfaceView.getHeight());
108     }
109 
110     @Override
surfaceChanged(@onNull SurfaceHolder holder, int format, int width, int height)111     public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
112         if (mEnableSyncSwitch.isChecked()) {
113             if (mSyncGroup == null) {
114                 mRenderingThread.renderFrame(null, width, height);
115                 return;
116             }
117             mSyncGroup.add(mSurfaceView, frameCallback ->
118                     mRenderingThread.renderFrame(frameCallback, width, height));
119             mSyncGroup.markSyncReady();
120             mSyncGroup = null;
121         } else {
122             mRenderingThread.renderFrame(null, width, height);
123         }
124     }
125 
126     @Override
surfaceDestroyed(@onNull SurfaceHolder holder)127     public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
128         mRenderingThread.stopRendering();
129     }
130 
131     private static class RenderingThread extends HandlerThread {
132         private final SurfaceHolder mSurfaceHolder;
133         private Handler mHandler;
134         private SurfaceSyncGroup.SurfaceViewFrameCallback mFrameCallback;
135         private final Point mSurfaceSize = new Point();
136 
137         int mColorValue = 0;
138         int mColorDelta = 10;
139         private final Paint mPaint = new Paint();
140 
RenderingThread(SurfaceHolder holder)141         RenderingThread(SurfaceHolder holder) {
142             super("RenderingThread");
143             mSurfaceHolder = holder;
144             mPaint.setColor(Color.BLACK);
145             mPaint.setTextSize(100);
146         }
147 
renderFrame(SurfaceSyncGroup.SurfaceViewFrameCallback frameCallback, int width, int height)148         public void renderFrame(SurfaceSyncGroup.SurfaceViewFrameCallback frameCallback, int width,
149                 int height) {
150             if (mHandler != null) {
151                 mHandler.post(() -> {
152                     mFrameCallback = frameCallback;
153                     mSurfaceSize.set(width, height);
154                     mRunnable.run();
155                 });
156             }
157         }
158 
159         private final Runnable mRunnable = new Runnable() {
160             @Override
161             public void run() {
162                 if (mFrameCallback != null) {
163                     mFrameCallback.onFrameStarted();
164                 }
165 
166                 try {
167                     // Long delay from start to finish to mimic slow draw
168                     Thread.sleep(1000);
169                 } catch (InterruptedException e) {
170                 }
171 
172                 mColorValue += mColorDelta;
173                 if (mColorValue > 245 || mColorValue < 10) {
174                     mColorDelta *= -1;
175                 }
176 
177                 Canvas c = mSurfaceHolder.lockCanvas();
178                 c.drawRGB(255, 0, 0);
179                 c.drawText("RENDERED CONTENT", 0, mSurfaceSize.y / 2, mPaint);
180                 mSurfaceHolder.unlockCanvasAndPost(c);
181                 mFrameCallback = null;
182             }
183         };
184 
startRendering()185         public void startRendering() {
186             start();
187             mHandler = new Handler(getLooper());
188         }
189 
stopRendering()190         public void stopRendering() {
191             if (mHandler != null) {
192                 mHandler.removeCallbacks(mRunnable);
193             }
194         }
195     }
196 }
197