1 /*
2  * Copyright 2024 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.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.Activity;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.ServiceConnection;
26 import android.graphics.Canvas;
27 import android.graphics.Color;
28 import android.graphics.Paint;
29 import android.graphics.Rect;
30 import android.os.Bundle;
31 import android.os.IBinder;
32 import android.os.RemoteException;
33 import android.util.Log;
34 import android.view.AttachedSurfaceControl;
35 import android.view.Choreographer;
36 import android.view.Gravity;
37 import android.view.Surface;
38 import android.view.SurfaceControl;
39 import android.view.SurfaceHolder;
40 import android.view.SurfaceView;
41 import android.view.ViewTreeObserver;
42 import android.view.WindowManager;
43 import android.widget.LinearLayout;
44 
45 /**
46  * Used to manually test that {@link android.view.SurfaceControlInputReceiver} API works.
47  */
48 public class SurfaceInputTestActivity extends Activity {
49 
50     private static final String TAG = "SurfaceInputTestActivity";
51     private SurfaceView mLocalSurfaceView;
52     private SurfaceView mRemoteSurfaceView;
53     private IAttachEmbeddedWindow mIAttachEmbeddedWindow;
54     private SurfaceControl mParentSurfaceControl;
55 
56     private SurfaceControl mLocalSurfaceControl;
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             mIAttachEmbeddedWindow = IAttachEmbeddedWindow.Stub.asInterface(service);
63             loadEmbedded();
64         }
65 
66         public void onServiceDisconnected(ComponentName className) {
67             Log.d(TAG, "Service Disconnected");
68             mIAttachEmbeddedWindow = null;
69         }
70     };
71 
72     @Override
onAttachedToWindow()73     public void onAttachedToWindow() {
74         super.onAttachedToWindow();
75         ViewTreeObserver viewTreeObserver = getWindow().getDecorView().getViewTreeObserver();
76         viewTreeObserver.addOnPreDrawListener(
77                 new ViewTreeObserver.OnPreDrawListener() {
78                     @Override
79                     public boolean onPreDraw() {
80                         addLocalChildSurfaceControl(getWindow().getRootSurfaceControl());
81                         viewTreeObserver.removeOnPreDrawListener(this);
82                         return true;
83                     }
84                 });
85     }
86 
87     @Override
onCreate(@ullable Bundle savedInstanceState)88     protected void onCreate(@Nullable Bundle savedInstanceState) {
89         super.onCreate(savedInstanceState);
90         LinearLayout content = new LinearLayout(this);
91         mLocalSurfaceView = new SurfaceView(this);
92         content.addView(mLocalSurfaceView, new LinearLayout.LayoutParams(
93                 500, 500, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
94 
95         mRemoteSurfaceView = new SurfaceView(this);
96         content.addView(mRemoteSurfaceView, new LinearLayout.LayoutParams(
97                 500, 500, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
98 
99         setContentView(content);
100 
101         mLocalSurfaceView.setZOrderOnTop(true);
102         mLocalSurfaceView.getHolder().addCallback(mLocalSurfaceViewCallback);
103 
104         mRemoteSurfaceView.setZOrderOnTop(true);
105         mRemoteSurfaceView.getHolder().addCallback(mRemoteSurfaceViewHolder);
106 
107         Intent intent = new Intent(this, EmbeddedWindowService.class);
108         intent.setAction(IAttachEmbeddedWindow.class.getName());
109         Log.d(TAG, "bindService");
110         bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
111     }
112 
113     @Override
onDestroy()114     protected void onDestroy() {
115         super.onDestroy();
116         if (mLocalSurfaceControl != null) {
117             getWindowManager().unregisterSurfaceControlInputReceiver(mLocalSurfaceControl);
118             new SurfaceControl.Transaction().remove(mLocalSurfaceControl).apply();
119         }
120     }
121 
addLocalChildSurfaceControl(AttachedSurfaceControl attachedSurfaceControl)122     private void addLocalChildSurfaceControl(AttachedSurfaceControl attachedSurfaceControl) {
123         mLocalSurfaceControl = new SurfaceControl.Builder().setName("LocalSC")
124                 .setBufferSize(100, 100).build();
125         attachedSurfaceControl.buildReparentTransaction(mLocalSurfaceControl)
126                 .setVisibility(mLocalSurfaceControl, true)
127                 .setCrop(mLocalSurfaceControl, new Rect(0, 0, 100, 100))
128                 .setPosition(mLocalSurfaceControl, 250, 1000)
129                 .setLayer(mLocalSurfaceControl, 1).apply();
130 
131         Paint paint = new Paint();
132         paint.setColor(Color.WHITE);
133         paint.setTextSize(20);
134 
135         Surface surface = new Surface(mLocalSurfaceControl);
136         Canvas c = surface.lockCanvas(null);
137         c.drawColor(Color.GREEN);
138         c.drawText("Local SC", 0, 0, paint);
139         surface.unlockCanvasAndPost(c);
140         WindowManager wm = getSystemService(WindowManager.class);
141         wm.registerBatchedSurfaceControlInputReceiver(
142                 attachedSurfaceControl.getInputTransferToken(), mLocalSurfaceControl,
143                 Choreographer.getInstance(), event -> {
144                     Log.d(TAG, "onInputEvent-sc " + event);
145                     return false;
146                 });
147     }
148 
149     private final SurfaceHolder.Callback mLocalSurfaceViewCallback = new SurfaceHolder.Callback() {
150         @Override
151         public void surfaceCreated(@NonNull SurfaceHolder holder) {
152             Paint paint = new Paint();
153             paint.setColor(Color.WHITE);
154             paint.setTextSize(40);
155 
156             Canvas c = holder.lockCanvas();
157             c.drawColor(Color.RED);
158             c.drawText("Local", 250, 250, paint);
159             holder.unlockCanvasAndPost(c);
160 
161             WindowManager wm = getSystemService(WindowManager.class);
162             wm.registerBatchedSurfaceControlInputReceiver(
163                     mLocalSurfaceView.getRootSurfaceControl().getInputTransferToken(),
164                     mLocalSurfaceView.getSurfaceControl(),
165                     Choreographer.getInstance(), event -> {
166                         Log.d(TAG, "onInputEvent-local " + event);
167                         return false;
168                     });
169         }
170 
171         @Override
172         public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
173                 int height) {
174 
175         }
176 
177         @Override
178         public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
179             getWindowManager().unregisterSurfaceControlInputReceiver(
180                     mLocalSurfaceView.getSurfaceControl());
181         }
182     };
183 
184     private final SurfaceHolder.Callback mRemoteSurfaceViewHolder = new SurfaceHolder.Callback() {
185         @Override
186         public void surfaceCreated(@NonNull SurfaceHolder holder) {
187             mParentSurfaceControl = mRemoteSurfaceView.getSurfaceControl();
188             loadEmbedded();
189         }
190 
191         @Override
192         public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
193                 int height) {
194         }
195 
196         @Override
197         public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
198             if (mIAttachEmbeddedWindow != null) {
199                 try {
200                     mIAttachEmbeddedWindow.tearDownEmbeddedSurfaceControl();
201                 } catch (RemoteException e) {
202                     Log.e(TAG, "Failed to tear down embedded SurfaceControl", e);
203                 }
204             }
205         }
206     };
207 
loadEmbedded()208     private void loadEmbedded() {
209         if (mParentSurfaceControl == null || mIAttachEmbeddedWindow == null) {
210             return;
211         }
212         try {
213             mIAttachEmbeddedWindow.attachEmbeddedSurfaceControl(mParentSurfaceControl,
214                     getDisplayId(),
215                     mRemoteSurfaceView.getRootSurfaceControl().getInputTransferToken());
216         } catch (RemoteException e) {
217             Log.e(TAG, "Failed to load embedded SurfaceControl", e);
218         }
219     }
220 }
221