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.viewembed;
18 
19 import android.app.Activity;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.ServiceConnection;
24 import android.graphics.PixelFormat;
25 import android.graphics.Point;
26 import android.os.Bundle;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.util.Log;
30 import android.view.Gravity;
31 import android.view.SurfaceControlViewHost.SurfacePackage;
32 import android.view.SurfaceHolder;
33 import android.view.SurfaceView;
34 import android.view.ViewGroup;
35 import android.view.WindowManager;
36 import android.widget.Button;
37 import android.widget.FrameLayout;
38 import android.widget.Switch;
39 import android.window.SurfaceSyncGroup;
40 
41 public class SurfaceControlViewHostSyncTest extends Activity implements SurfaceHolder.Callback {
42     private static final String TAG = "SurfaceControlViewHostSyncTest";
43     private SurfaceView mSv;
44 
45     private final Object mLock = new Object();
46     private boolean mIsAttached;
47     private boolean mSurfaceCreated;
48 
49     private IAttachEmbeddedWindow mIAttachEmbeddedWindow;
50     private SurfacePackage mSurfacePackage;
51 
52     private final Point[] mSizes = new Point[]{new Point(500, 500), new Point(700, 400),
53             new Point(300, 800), new Point(200, 200)};
54     private int mLastSizeIndex = 0;
55 
56     private boolean mSync = true;
57 
58     private final ServiceConnection mConnection = new ServiceConnection() {
59         // Called when the connection with the service is established
60         public void onServiceConnected(ComponentName className, IBinder service) {
61             Log.d(TAG, "Service Connected");
62             synchronized (mLock) {
63                 mIAttachEmbeddedWindow = IAttachEmbeddedWindow.Stub.asInterface(service);
64             }
65             loadEmbedded();
66         }
67 
68         public void onServiceDisconnected(ComponentName className) {
69             Log.d(TAG, "Service Disconnected");
70             mIAttachEmbeddedWindow = null;
71         }
72     };
73 
onCreate(Bundle savedInstanceState)74     protected void onCreate(Bundle savedInstanceState) {
75         FrameLayout content = new FrameLayout(this);
76         super.onCreate(savedInstanceState);
77         mSv = new SurfaceView(this);
78         Button button = new Button(this);
79         Switch enableSyncButton = new Switch(this);
80         content.addView(mSv, new FrameLayout.LayoutParams(
81                 mSizes[0].x, mSizes[0].y, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
82         content.addView(button, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
83                 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM));
84         content.addView(enableSyncButton,
85                 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
86                         ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.RIGHT | Gravity.BOTTOM));
87         setContentView(content);
88 
89         mSv.setZOrderOnTop(false);
90         mSv.getHolder().addCallback(this);
91 
92         button.setText("Change Size");
93         enableSyncButton.setText("Enable Sync");
94         enableSyncButton.setChecked(true);
95         button.setOnClickListener(v -> {
96             resize();
97         });
98 
99         enableSyncButton.setOnCheckedChangeListener((buttonView, isChecked) -> {
100             mSync = isChecked;
101         });
102 
103         Intent intent = new Intent(this, EmbeddedWindowService.class);
104         intent.setAction(IAttachEmbeddedWindow.class.getName());
105         Log.d(TAG, "bindService");
106         bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
107     }
108 
resize()109     private void resize() {
110         if (mSurfacePackage == null) {
111             return;
112         }
113         Point size = mSizes[mLastSizeIndex % mSizes.length];
114 
115         Runnable svResizeRunnable = () -> {
116             mSv.getLayoutParams().width = size.x;
117             mSv.getLayoutParams().height = size.y;
118             mSv.requestLayout();
119         };
120 
121         Runnable resizeRunnable = () -> {
122             try {
123                 final WindowManager.LayoutParams lp =
124                         new WindowManager.LayoutParams(size.x, size.y,
125                                 WindowManager.LayoutParams.TYPE_APPLICATION, 0,
126                                 PixelFormat.TRANSPARENT);
127                 mIAttachEmbeddedWindow.relayout(lp);
128             } catch (RemoteException e) {
129             }
130         };
131 
132         if (mSync) {
133             SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG);
134             syncGroup.add(getWindow().getRootSurfaceControl(), svResizeRunnable);
135             syncGroup.add(mSurfacePackage, resizeRunnable);
136             syncGroup.markSyncReady();
137         } else {
138             svResizeRunnable.run();
139             resizeRunnable.run();
140         }
141 
142         mLastSizeIndex++;
143     }
144 
145     @Override
surfaceCreated(SurfaceHolder holder)146     public void surfaceCreated(SurfaceHolder holder) {
147         synchronized (mLock) {
148             mSurfaceCreated = true;
149         }
150         attachEmbedded();
151     }
152 
isReadyToAttach()153     private boolean isReadyToAttach() {
154         synchronized (mLock) {
155             if (!mSurfaceCreated) {
156                 Log.d(TAG, "surface is not created");
157             }
158             if (mIAttachEmbeddedWindow == null) {
159                 Log.d(TAG, "Service is not attached");
160             }
161             if (mIsAttached) {
162                 Log.d(TAG, "Already attached");
163             }
164 
165             return mSurfaceCreated && mIAttachEmbeddedWindow != null && !mIsAttached
166                     && mSurfacePackage != null;
167         }
168     }
169 
loadEmbedded()170     private void loadEmbedded() {
171         try {
172             mIAttachEmbeddedWindow.attachEmbedded(mSv.getHostToken(), mSizes[0].x, mSizes[0].y,
173                     new IAttachEmbeddedWindowCallback.Stub() {
174                         @Override
175                         public void onEmbeddedWindowAttached(SurfacePackage surfacePackage) {
176                             getMainThreadHandler().post(() -> {
177                                 mSurfacePackage = surfacePackage;
178                                 attachEmbedded();
179                             });
180                         }
181                     });
182             mLastSizeIndex++;
183         } catch (RemoteException e) {
184         }
185     }
186 
attachEmbedded()187     private void attachEmbedded() {
188         if (!isReadyToAttach()) {
189             return;
190         }
191 
192         synchronized (mLock) {
193             mIsAttached = true;
194         }
195         mSv.setChildSurfacePackage(mSurfacePackage);
196     }
197 
198     @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)199     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
200     }
201 
202     @Override
surfaceDestroyed(SurfaceHolder holder)203     public void surfaceDestroyed(SurfaceHolder holder) {
204         synchronized (mLock) {
205             mSurfaceCreated = false;
206         }
207     }
208 
209     @Override
onStart()210     protected void onStart() {
211         super.onStart();
212         Log.d(TAG, "onStart");
213         resize();
214     }
215 }
216