blob: 3bf659b663b0f5b853fa4855c7c27d8b73c5250e [file] [log] [blame]
Andrii Kuliand3134692017-06-26 14:57:02 -07001/**
2 * Copyright (c) 2017 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 android.app;
18
chaviwda4c6942018-11-07 15:52:56 -080019import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
20import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
21import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
Mady Mellor390bff42019-04-05 15:09:01 -070022import static android.view.Display.INVALID_DISPLAY;
chaviwda4c6942018-11-07 15:52:56 -080023
Andrii Kuliand3134692017-06-26 14:57:02 -070024import android.annotation.NonNull;
Wale Ogunwale691af682019-02-11 03:09:10 -080025import android.annotation.TestApi;
Andrii Kuliancf8f6832018-01-23 19:43:30 -080026import android.app.ActivityManager.StackInfo;
Mark Renouf7b71a5d2019-01-23 17:12:34 -050027import android.content.ComponentName;
Andrii Kuliand3134692017-06-26 14:57:02 -070028import android.content.Context;
29import android.content.Intent;
Issei Suzukia5dbf522019-02-01 17:58:15 +010030import android.graphics.Insets;
Yohei Yukawab4f328a2019-05-02 08:41:27 -070031import android.graphics.Matrix;
Tiger Huang2b210c22019-03-18 21:21:26 +080032import android.graphics.Region;
Andrii Kuliand3134692017-06-26 14:57:02 -070033import android.hardware.display.DisplayManager;
34import android.hardware.display.VirtualDisplay;
Mark Renouf041d7262019-02-06 12:09:41 -050035import android.hardware.input.InputManager;
Andrii Kuliand3134692017-06-26 14:57:02 -070036import android.os.RemoteException;
Mark Renouf041d7262019-02-06 12:09:41 -050037import android.os.SystemClock;
Brad Stenninga1dbe9c2018-05-02 08:29:28 -070038import android.os.UserHandle;
Andrii Kuliand3134692017-06-26 14:57:02 -070039import android.util.AttributeSet;
40import android.util.DisplayMetrics;
41import android.util.Log;
Andrii Kulianf0379de2018-03-14 16:24:07 -070042import android.view.IWindowManager;
Mark Renouf041d7262019-02-06 12:09:41 -050043import android.view.InputDevice;
44import android.view.KeyCharacterMap;
45import android.view.KeyEvent;
chaviwff2e7d82018-11-02 11:11:27 -070046import android.view.SurfaceControl;
Andrii Kuliand3134692017-06-26 14:57:02 -070047import android.view.SurfaceHolder;
chaviwff2e7d82018-11-02 11:11:27 -070048import android.view.SurfaceSession;
Andrii Kuliand3134692017-06-26 14:57:02 -070049import android.view.SurfaceView;
chaviwd44449d2019-01-02 16:33:12 -080050import android.view.View;
Andrii Kuliand3134692017-06-26 14:57:02 -070051import android.view.ViewGroup;
Tiger Huang2b210c22019-03-18 21:21:26 +080052import android.view.ViewParent;
Andrii Kuliand3134692017-06-26 14:57:02 -070053import android.view.WindowManager;
Andrii Kulian4b6599e2018-01-15 17:24:08 -080054import android.view.WindowManagerGlobal;
Yohei Yukawab4f328a2019-05-02 08:41:27 -070055import android.view.inputmethod.InputMethodManager;
Andrii Kuliand3134692017-06-26 14:57:02 -070056
57import dalvik.system.CloseGuard;
58
Andrii Kuliancf8f6832018-01-23 19:43:30 -080059import java.util.List;
60
Andrii Kuliand3134692017-06-26 14:57:02 -070061/**
Tiger Huang04dc4cc2019-01-17 18:41:41 +080062 * Activity container that allows launching activities into itself.
Andrii Kuliand3134692017-06-26 14:57:02 -070063 * <p>Activity launching into this container is restricted by the same rules that apply to launching
64 * on VirtualDisplays.
65 * @hide
66 */
Wale Ogunwale691af682019-02-11 03:09:10 -080067@TestApi
Andrii Kuliand3134692017-06-26 14:57:02 -070068public class ActivityView extends ViewGroup {
69
70 private static final String DISPLAY_NAME = "ActivityViewVirtualDisplay";
71 private static final String TAG = "ActivityView";
72
73 private VirtualDisplay mVirtualDisplay;
74 private final SurfaceView mSurfaceView;
chaviwff2e7d82018-11-02 11:11:27 -070075
76 /**
77 * This is the root surface for the VirtualDisplay. The VirtualDisplay child surfaces will be
78 * re-parented to this surface. This will also be a child of the SurfaceView's SurfaceControl.
79 */
80 private SurfaceControl mRootSurfaceControl;
Andrii Kuliand3134692017-06-26 14:57:02 -070081
82 private final SurfaceCallback mSurfaceCallback;
83 private StateCallback mActivityViewCallback;
84
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070085 private IActivityTaskManager mActivityTaskManager;
Tiger Huang04dc4cc2019-01-17 18:41:41 +080086 // Temp container to store view coordinates in window.
87 private final int[] mLocationInWindow = new int[2];
Andrii Kuliand3134692017-06-26 14:57:02 -070088
Tiger Huang2b210c22019-03-18 21:21:26 +080089 // The latest tap exclude region that we've sent to WM.
90 private final Region mTapExcludeRegion = new Region();
91
Andrii Kuliancf8f6832018-01-23 19:43:30 -080092 private TaskStackListener mTaskStackListener;
93
Andrii Kuliand3134692017-06-26 14:57:02 -070094 private final CloseGuard mGuard = CloseGuard.get();
95 private boolean mOpened; // Protected by mGuard.
96
chaviwff2e7d82018-11-02 11:11:27 -070097 private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
chaviwff2e7d82018-11-02 11:11:27 -070098
Wale Ogunwale9e737db2018-12-17 15:42:37 -080099 /** The ActivityView is only allowed to contain one task. */
100 private final boolean mSingleTaskInstance;
101
Issei Suzukia5dbf522019-02-01 17:58:15 +0100102 private Insets mForwardedInsets;
103
Andrii Kuliand3134692017-06-26 14:57:02 -0700104 public ActivityView(Context context) {
105 this(context, null /* attrs */);
106 }
107
108 public ActivityView(Context context, AttributeSet attrs) {
109 this(context, attrs, 0 /* defStyle */);
110 }
111
112 public ActivityView(Context context, AttributeSet attrs, int defStyle) {
Wale Ogunwale9e737db2018-12-17 15:42:37 -0800113 this(context, attrs, defStyle, false /*singleTaskInstance*/);
114 }
115
116 public ActivityView(
117 Context context, AttributeSet attrs, int defStyle, boolean singleTaskInstance) {
Andrii Kuliand3134692017-06-26 14:57:02 -0700118 super(context, attrs, defStyle);
Wale Ogunwale9e737db2018-12-17 15:42:37 -0800119 mSingleTaskInstance = singleTaskInstance;
Andrii Kuliand3134692017-06-26 14:57:02 -0700120
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700121 mActivityTaskManager = ActivityTaskManager.getService();
Andrii Kuliand3134692017-06-26 14:57:02 -0700122 mSurfaceView = new SurfaceView(context);
Issei Suzukicac2a502019-04-16 16:52:50 +0200123 mSurfaceView.setAlpha(0f);
Andrii Kuliand3134692017-06-26 14:57:02 -0700124 mSurfaceCallback = new SurfaceCallback();
125 mSurfaceView.getHolder().addCallback(mSurfaceCallback);
126 addView(mSurfaceView);
127
128 mOpened = true;
129 mGuard.open("release");
130 }
131
132 /** Callback that notifies when the container is ready or destroyed. */
133 public abstract static class StateCallback {
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500134
Andrii Kuliand3134692017-06-26 14:57:02 -0700135 /**
136 * Called when the container is ready for launching activities. Calling
137 * {@link #startActivity(Intent)} prior to this callback will result in an
138 * {@link IllegalStateException}.
139 *
140 * @see #startActivity(Intent)
141 */
142 public abstract void onActivityViewReady(ActivityView view);
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500143
Andrii Kuliand3134692017-06-26 14:57:02 -0700144 /**
145 * Called when the container can no longer launch activities. Calling
146 * {@link #startActivity(Intent)} after this callback will result in an
147 * {@link IllegalStateException}.
148 *
149 * @see #startActivity(Intent)
150 */
151 public abstract void onActivityViewDestroyed(ActivityView view);
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500152
153 /**
154 * Called when a task is created inside the container.
155 * This is a filtered version of {@link TaskStackListener}
156 */
157 public void onTaskCreated(int taskId, ComponentName componentName) { }
158
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700159 /**
160 * Called when a task is moved to the front of the stack inside the container.
161 * This is a filtered version of {@link TaskStackListener}
162 */
Wale Ogunwale691af682019-02-11 03:09:10 -0800163 public void onTaskMovedToFront(int taskId) { }
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500164
165 /**
166 * Called when a task is about to be removed from the stack inside the container.
167 * This is a filtered version of {@link TaskStackListener}
168 */
169 public void onTaskRemovalStarted(int taskId) { }
Andrii Kuliand3134692017-06-26 14:57:02 -0700170 }
171
172 /**
173 * Set the callback to be notified about state changes.
174 * <p>This class must finish initializing before {@link #startActivity(Intent)} can be called.
175 * <p>Note: If the instance was ready prior to this call being made, then
176 * {@link StateCallback#onActivityViewReady(ActivityView)} will be called from within
177 * this method call.
178 *
179 * @param callback The callback to report events to.
180 *
181 * @see StateCallback
182 * @see #startActivity(Intent)
183 */
184 public void setCallback(StateCallback callback) {
185 mActivityViewCallback = callback;
186
187 if (mVirtualDisplay != null && mActivityViewCallback != null) {
188 mActivityViewCallback.onActivityViewReady(this);
189 }
190 }
191
192 /**
Mark Renouf34d04f32019-05-13 15:53:18 -0400193 * Sets the corner radius for the Activity displayed here. The corners will be
194 * cropped from the window painted by the contained Activity.
195 *
196 * @param cornerRadius the radius for the corners, in pixels
197 * @hide
198 */
199 public void setCornerRadius(float cornerRadius) {
200 mSurfaceView.setCornerRadius(cornerRadius);
201 }
202
203 /**
Andrii Kuliand3134692017-06-26 14:57:02 -0700204 * Launch a new activity into this container.
205 * <p>Activity resolved by the provided {@link Intent} must have
206 * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
207 * launched here. Also, if activity is not owned by the owner of this container, it must allow
208 * embedding and the caller must have permission to embed.
209 * <p>Note: This class must finish initializing and
210 * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
211 * this method can be called.
212 *
213 * @param intent Intent used to launch an activity.
214 *
215 * @see StateCallback
216 * @see #startActivity(PendingIntent)
217 */
218 public void startActivity(@NonNull Intent intent) {
219 final ActivityOptions options = prepareActivityOptions();
220 getContext().startActivity(intent, options.toBundle());
221 }
222
223 /**
224 * Launch a new activity into this container.
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700225 * <p>Activity resolved by the provided {@link Intent} must have
226 * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
227 * launched here. Also, if activity is not owned by the owner of this container, it must allow
228 * embedding and the caller must have permission to embed.
229 * <p>Note: This class must finish initializing and
230 * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
231 * this method can be called.
232 *
233 * @param intent Intent used to launch an activity.
234 * @param user The UserHandle of the user to start this activity for.
235 *
236 *
237 * @see StateCallback
238 * @see #startActivity(PendingIntent)
239 */
240 public void startActivity(@NonNull Intent intent, UserHandle user) {
241 final ActivityOptions options = prepareActivityOptions();
242 getContext().startActivityAsUser(intent, options.toBundle(), user);
243 }
244
245 /**
246 * Launch a new activity into this container.
Andrii Kuliand3134692017-06-26 14:57:02 -0700247 * <p>Activity resolved by the provided {@link PendingIntent} must have
248 * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
249 * launched here. Also, if activity is not owned by the owner of this container, it must allow
250 * embedding and the caller must have permission to embed.
251 * <p>Note: This class must finish initializing and
252 * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
253 * this method can be called.
254 *
255 * @param pendingIntent Intent used to launch an activity.
256 *
257 * @see StateCallback
258 * @see #startActivity(Intent)
259 */
260 public void startActivity(@NonNull PendingIntent pendingIntent) {
261 final ActivityOptions options = prepareActivityOptions();
262 try {
263 pendingIntent.send(null /* context */, 0 /* code */, null /* intent */,
264 null /* onFinished */, null /* handler */, null /* requiredPermission */,
265 options.toBundle());
266 } catch (PendingIntent.CanceledException e) {
267 throw new RuntimeException(e);
268 }
269 }
270
271 /**
Mady Mellor60101c92019-04-11 19:04:00 -0700272 * Launch a new activity into this container.
273 * <p>Activity resolved by the provided {@link PendingIntent} must have
274 * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
275 * launched here. Also, if activity is not owned by the owner of this container, it must allow
276 * embedding and the caller must have permission to embed.
277 * <p>Note: This class must finish initializing and
278 * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
279 * this method can be called.
280 *
281 * @param pendingIntent Intent used to launch an activity.
282 * @param options options for the activity
283 *
284 * @see StateCallback
285 * @see #startActivity(Intent)
286 */
287 public void startActivity(@NonNull PendingIntent pendingIntent,
288 @NonNull ActivityOptions options) {
289 options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
290 try {
291 pendingIntent.send(null /* context */, 0 /* code */, null /* intent */,
292 null /* onFinished */, null /* handler */, null /* requiredPermission */,
293 options.toBundle());
294 } catch (PendingIntent.CanceledException e) {
295 throw new RuntimeException(e);
296 }
297 }
298
299 /**
Andrii Kuliand3134692017-06-26 14:57:02 -0700300 * Check if container is ready to launch and create {@link ActivityOptions} to target the
301 * virtual display.
302 */
303 private ActivityOptions prepareActivityOptions() {
304 if (mVirtualDisplay == null) {
305 throw new IllegalStateException(
306 "Trying to start activity before ActivityView is ready.");
307 }
308 final ActivityOptions options = ActivityOptions.makeBasic();
309 options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
310 return options;
311 }
312
313 /**
314 * Release this container. Activity launching will no longer be permitted.
315 * <p>Note: Calling this method is allowed after
316 * {@link StateCallback#onActivityViewReady(ActivityView)} callback was triggered and before
317 * {@link StateCallback#onActivityViewDestroyed(ActivityView)}.
318 *
319 * @see StateCallback
320 */
321 public void release() {
322 if (mVirtualDisplay == null) {
323 throw new IllegalStateException(
324 "Trying to release container that is not initialized.");
325 }
326 performRelease();
327 }
328
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800329 /**
Tiger Huang2b210c22019-03-18 21:21:26 +0800330 * Triggers an update of {@link ActivityView}'s location in window to properly set tap exclude
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800331 * regions and avoid focus switches by touches on this view.
332 */
333 public void onLocationChanged() {
Tiger Huangd8ec9382019-04-18 14:35:09 -0700334 updateLocationAndTapExcludeRegion();
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800335 }
336
Yohei Yukawab4f328a2019-05-02 08:41:27 -0700337 private void clearActivityViewGeometryForIme() {
338 if (mVirtualDisplay == null) {
339 return;
340 }
341 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
342 mContext.getSystemService(InputMethodManager.class).reportActivityView(displayId, null);
343 }
344
Andrii Kuliand3134692017-06-26 14:57:02 -0700345 @Override
346 public void onLayout(boolean changed, int l, int t, int r, int b) {
347 mSurfaceView.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */);
348 }
349
Tiger Huang2b210c22019-03-18 21:21:26 +0800350 @Override
Issei Suzukicac2a502019-04-16 16:52:50 +0200351 public void setAlpha(float alpha) {
352 mSurfaceView.setAlpha(alpha);
353 }
354
355 @Override
356 public float getAlpha() {
357 return mSurfaceView.getAlpha();
358 }
359
360 @Override
Tiger Huang2b210c22019-03-18 21:21:26 +0800361 public boolean gatherTransparentRegion(Region region) {
362 // The tap exclude region may be affected by any view on top of it, so we detect the
363 // possible change by monitoring this function.
Tiger Huangd8ec9382019-04-18 14:35:09 -0700364 updateLocationAndTapExcludeRegion();
Tiger Huang2b210c22019-03-18 21:21:26 +0800365 return super.gatherTransparentRegion(region);
366 }
367
Tiger Huangd8ec9382019-04-18 14:35:09 -0700368 /**
369 * Sends current location in window and tap exclude region to WM for this view.
370 */
371 private void updateLocationAndTapExcludeRegion() {
372 if (mVirtualDisplay == null || !isAttachedToWindow()) {
Mark Renouf3856b4f2019-02-13 16:43:18 -0500373 return;
374 }
Tiger Huangd8ec9382019-04-18 14:35:09 -0700375 try {
376 int x = mLocationInWindow[0];
377 int y = mLocationInWindow[1];
378 getLocationInWindow(mLocationInWindow);
379 if (x != mLocationInWindow[0] || y != mLocationInWindow[1]) {
380 x = mLocationInWindow[0];
381 y = mLocationInWindow[1];
Yohei Yukawab4f328a2019-05-02 08:41:27 -0700382 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
Tiger Huangd8ec9382019-04-18 14:35:09 -0700383 WindowManagerGlobal.getWindowSession().updateDisplayContentLocation(
Yohei Yukawab4f328a2019-05-02 08:41:27 -0700384 getWindow(), x, y, displayId);
385
386 // Also report this geometry information to InputMethodManagerService.
387 // TODO(b/115693908): Unify this logic into the above WMS-based one.
388 final Matrix matrix = new Matrix();
389 matrix.set(getMatrix());
390 matrix.postTranslate(x, y);
391 mContext.getSystemService(InputMethodManager.class)
392 .reportActivityView(displayId, matrix);
Tiger Huangd8ec9382019-04-18 14:35:09 -0700393 }
394 updateTapExcludeRegion(x, y);
395 } catch (RemoteException e) {
396 e.rethrowAsRuntimeException();
397 }
398 }
399
400 /** Computes and sends current tap exclude region to WM for this view. */
401 private void updateTapExcludeRegion(int x, int y) throws RemoteException {
Tiger Huang2b210c22019-03-18 21:21:26 +0800402 if (!canReceivePointerEvents()) {
403 cleanTapExcludeRegion();
404 return;
405 }
Tiger Huangd8ec9382019-04-18 14:35:09 -0700406 mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight());
Tiger Huang2b210c22019-03-18 21:21:26 +0800407
Tiger Huangd8ec9382019-04-18 14:35:09 -0700408 // There might be views on top of us. We need to subtract those areas from the tap
409 // exclude region.
410 final ViewParent parent = getParent();
411 if (parent != null) {
412 parent.subtractObscuredTouchableRegion(mTapExcludeRegion, this);
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800413 }
Tiger Huangd8ec9382019-04-18 14:35:09 -0700414
415 WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
416 mTapExcludeRegion);
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800417 }
418
Andrii Kuliand3134692017-06-26 14:57:02 -0700419 private class SurfaceCallback implements SurfaceHolder.Callback {
420 @Override
421 public void surfaceCreated(SurfaceHolder surfaceHolder) {
422 if (mVirtualDisplay == null) {
Robert Carr5fea55b2018-12-10 13:05:52 -0800423 initVirtualDisplay(new SurfaceSession());
Andrii Kuliand3134692017-06-26 14:57:02 -0700424 if (mVirtualDisplay != null && mActivityViewCallback != null) {
425 mActivityViewCallback.onActivityViewReady(ActivityView.this);
426 }
427 } else {
chaviwff2e7d82018-11-02 11:11:27 -0700428 mTmpTransaction.reparent(mRootSurfaceControl,
Robert Carr10584fa2019-01-14 15:55:19 -0800429 mSurfaceView.getSurfaceControl()).apply();
Andrii Kuliand3134692017-06-26 14:57:02 -0700430 }
chaviwda4c6942018-11-07 15:52:56 -0800431
432 if (mVirtualDisplay != null) {
433 mVirtualDisplay.setDisplayState(true);
434 }
435
Tiger Huangd8ec9382019-04-18 14:35:09 -0700436 updateLocationAndTapExcludeRegion();
Andrii Kuliand3134692017-06-26 14:57:02 -0700437 }
438
439 @Override
440 public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
441 if (mVirtualDisplay != null) {
442 mVirtualDisplay.resize(width, height, getBaseDisplayDensity());
443 }
Tiger Huangd8ec9382019-04-18 14:35:09 -0700444 updateLocationAndTapExcludeRegion();
Andrii Kuliand3134692017-06-26 14:57:02 -0700445 }
446
447 @Override
448 public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Andrii Kuliand3134692017-06-26 14:57:02 -0700449 if (mVirtualDisplay != null) {
chaviwda4c6942018-11-07 15:52:56 -0800450 mVirtualDisplay.setDisplayState(false);
Andrii Kuliand3134692017-06-26 14:57:02 -0700451 }
Yohei Yukawab4f328a2019-05-02 08:41:27 -0700452 clearActivityViewGeometryForIme();
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800453 cleanTapExcludeRegion();
Andrii Kuliand3134692017-06-26 14:57:02 -0700454 }
455 }
456
chaviwd44449d2019-01-02 16:33:12 -0800457 @Override
458 protected void onVisibilityChanged(View changedView, int visibility) {
459 super.onVisibilityChanged(changedView, visibility);
460 mSurfaceView.setVisibility(visibility);
461 }
462
Mark Renouf041d7262019-02-06 12:09:41 -0500463 /**
Mady Mellor390bff42019-04-05 15:09:01 -0700464 * @return the display id of the virtual display.
465 */
466 public int getVirtualDisplayId() {
467 if (mVirtualDisplay != null) {
468 return mVirtualDisplay.getDisplay().getDisplayId();
469 }
470 return INVALID_DISPLAY;
471 }
472
473 /**
Mark Renouf041d7262019-02-06 12:09:41 -0500474 * Injects a pair of down/up key events with keycode {@link KeyEvent#KEYCODE_BACK} to the
475 * virtual display.
476 */
477 public void performBackPress() {
478 if (mVirtualDisplay == null) {
479 return;
480 }
481 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
482 final InputManager im = InputManager.getInstance();
483 im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, displayId),
484 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
485 im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, displayId),
486 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
487 }
488
489 private static KeyEvent createKeyEvent(int action, int code, int displayId) {
490 long when = SystemClock.uptimeMillis();
491 final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
492 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
493 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
494 InputDevice.SOURCE_KEYBOARD);
495 ev.setDisplayId(displayId);
496 return ev;
497 }
498
chaviwff2e7d82018-11-02 11:11:27 -0700499 private void initVirtualDisplay(SurfaceSession surfaceSession) {
Andrii Kuliand3134692017-06-26 14:57:02 -0700500 if (mVirtualDisplay != null) {
501 throw new IllegalStateException("Trying to initialize for the second time.");
502 }
503
504 final int width = mSurfaceView.getWidth();
505 final int height = mSurfaceView.getHeight();
506 final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
chaviwff2e7d82018-11-02 11:11:27 -0700507
Andrii Kuliand3134692017-06-26 14:57:02 -0700508 mVirtualDisplay = displayManager.createVirtualDisplay(
chaviwda4c6942018-11-07 15:52:56 -0800509 DISPLAY_NAME + "@" + System.identityHashCode(this), width, height,
510 getBaseDisplayDensity(), null,
511 VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
512 | VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
Andrii Kuliand3134692017-06-26 14:57:02 -0700513 if (mVirtualDisplay == null) {
514 Log.e(TAG, "Failed to initialize ActivityView");
515 return;
516 }
517
Andrii Kulianf0379de2018-03-14 16:24:07 -0700518 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
519 final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
chaviwff2e7d82018-11-02 11:11:27 -0700520
521 mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
Chavi Weingarten6ef9cc62019-02-07 16:28:45 +0000522 .setContainerLayer()
Robert Carr5fea55b2018-12-10 13:05:52 -0800523 .setParent(mSurfaceView.getSurfaceControl())
chaviwff2e7d82018-11-02 11:11:27 -0700524 .setName(DISPLAY_NAME)
525 .build();
526
Andrii Kulianf0379de2018-03-14 16:24:07 -0700527 try {
Issei Suzukia5dbf522019-02-01 17:58:15 +0100528 // TODO: Find a way to consolidate these calls to the server.
Tiger Huangd8ec9382019-04-18 14:35:09 -0700529 WindowManagerGlobal.getWindowSession().reparentDisplayContent(
530 getWindow(), mRootSurfaceControl, displayId);
Andrii Kulianf0379de2018-03-14 16:24:07 -0700531 wm.dontOverrideDisplayInfo(displayId);
Wale Ogunwale9e737db2018-12-17 15:42:37 -0800532 if (mSingleTaskInstance) {
533 mActivityTaskManager.setDisplayToSingleTaskInstance(displayId);
534 }
Issei Suzukia5dbf522019-02-01 17:58:15 +0100535 wm.setForwardedInsets(displayId, mForwardedInsets);
Andrii Kulianf0379de2018-03-14 16:24:07 -0700536 } catch (RemoteException e) {
537 e.rethrowAsRuntimeException();
538 }
chaviwff2e7d82018-11-02 11:11:27 -0700539
540 mTmpTransaction.show(mRootSurfaceControl).apply();
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700541 mTaskStackListener = new TaskStackListenerImpl();
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800542 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700543 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800544 } catch (RemoteException e) {
545 Log.e(TAG, "Failed to register task stack listener", e);
546 }
Andrii Kuliand3134692017-06-26 14:57:02 -0700547 }
548
549 private void performRelease() {
550 if (!mOpened) {
551 return;
552 }
553
554 mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
555
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800556 cleanTapExcludeRegion();
Andrii Kuliand3134692017-06-26 14:57:02 -0700557
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800558 if (mTaskStackListener != null) {
559 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700560 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800561 } catch (RemoteException e) {
562 Log.e(TAG, "Failed to unregister task stack listener", e);
563 }
564 mTaskStackListener = null;
565 }
566
Andrii Kuliand3134692017-06-26 14:57:02 -0700567 final boolean displayReleased;
568 if (mVirtualDisplay != null) {
569 mVirtualDisplay.release();
570 mVirtualDisplay = null;
571 displayReleased = true;
572 } else {
573 displayReleased = false;
574 }
575
Andrii Kuliand3134692017-06-26 14:57:02 -0700576 if (displayReleased && mActivityViewCallback != null) {
577 mActivityViewCallback.onActivityViewDestroyed(this);
578 }
579
580 mGuard.close();
581 mOpened = false;
582 }
583
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800584 /** Report to server that tap exclude region on hosting display should be cleared. */
585 private void cleanTapExcludeRegion() {
Tiger Huang2b210c22019-03-18 21:21:26 +0800586 if (!isAttachedToWindow() || mTapExcludeRegion.isEmpty()) {
Mark Renouf3856b4f2019-02-13 16:43:18 -0500587 return;
588 }
Tiger Huang2b210c22019-03-18 21:21:26 +0800589 // Update tap exclude region with a null region to clean the state on server.
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800590 try {
591 WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
Tiger Huang2b210c22019-03-18 21:21:26 +0800592 null /* region */);
593 mTapExcludeRegion.setEmpty();
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800594 } catch (RemoteException e) {
595 e.rethrowAsRuntimeException();
596 }
597 }
598
Andrii Kuliand3134692017-06-26 14:57:02 -0700599 /** Get density of the hosting display. */
600 private int getBaseDisplayDensity() {
601 final WindowManager wm = mContext.getSystemService(WindowManager.class);
602 final DisplayMetrics metrics = new DisplayMetrics();
603 wm.getDefaultDisplay().getMetrics(metrics);
604 return metrics.densityDpi;
605 }
606
607 @Override
608 protected void finalize() throws Throwable {
609 try {
610 if (mGuard != null) {
611 mGuard.warnIfOpen();
612 performRelease();
613 }
614 } finally {
615 super.finalize();
616 }
617 }
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800618
619 /**
Issei Suzukia5dbf522019-02-01 17:58:15 +0100620 * Set forwarded insets on the virtual display.
621 *
622 * @see IWindowManager#setForwardedInsets
623 */
624 public void setForwardedInsets(Insets insets) {
625 mForwardedInsets = insets;
626 if (mVirtualDisplay == null) {
627 return;
628 }
629 try {
630 final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
631 wm.setForwardedInsets(mVirtualDisplay.getDisplay().getDisplayId(), mForwardedInsets);
632 } catch (RemoteException e) {
633 e.rethrowAsRuntimeException();
634 }
635 }
636
637 /**
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800638 * A task change listener that detects background color change of the topmost stack on our
639 * virtual display and updates the background of the surface view. This background will be shown
640 * when surface view is resized, but the app hasn't drawn its content in new size yet.
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700641 * It also calls StateCallback.onTaskMovedToFront to notify interested parties that the stack
642 * associated with the {@link ActivityView} has had a Task moved to the front. This is useful
643 * when needing to also bring the host Activity to the foreground at the same time.
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800644 */
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700645 private class TaskStackListenerImpl extends TaskStackListener {
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800646
647 @Override
Mark Renoufc808f062019-02-07 15:20:37 -0500648 public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo)
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800649 throws RemoteException {
Mark Renoufc808f062019-02-07 15:20:37 -0500650 if (mVirtualDisplay == null
651 || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) {
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800652 return;
653 }
654
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700655 StackInfo stackInfo = getTopMostStackInfo();
656 if (stackInfo == null) {
657 return;
658 }
659 // Found the topmost stack on target display. Now check if the topmost task's
660 // description changed.
Mark Renoufc808f062019-02-07 15:20:37 -0500661 if (taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
662 mSurfaceView.setResizeBackgroundColor(
663 taskInfo.taskDescription.getBackgroundColor());
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700664 }
665 }
666
667 @Override
Mark Renoufc808f062019-02-07 15:20:37 -0500668 public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
669 throws RemoteException {
670 if (mActivityViewCallback == null || mVirtualDisplay == null
671 || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) {
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500672 return;
673 }
674
675 StackInfo stackInfo = getTopMostStackInfo();
676 // if StackInfo was null or unrelated to the "move to front" then there's no use
677 // notifying the callback
678 if (stackInfo != null
Mark Renoufc808f062019-02-07 15:20:37 -0500679 && taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
Wale Ogunwale691af682019-02-11 03:09:10 -0800680 mActivityViewCallback.onTaskMovedToFront(taskInfo.taskId);
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500681 }
682 }
683
684 @Override
685 public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
Mark Renoufc808f062019-02-07 15:20:37 -0500686 if (mActivityViewCallback == null || mVirtualDisplay == null) {
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500687 return;
688 }
689
690 StackInfo stackInfo = getTopMostStackInfo();
691 // if StackInfo was null or unrelated to the task creation then there's no use
692 // notifying the callback
693 if (stackInfo != null
694 && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
695 mActivityViewCallback.onTaskCreated(taskId, componentName);
696 }
697 }
698
699 @Override
Mark Renoufc808f062019-02-07 15:20:37 -0500700 public void onTaskRemovalStarted(ActivityManager.RunningTaskInfo taskInfo)
701 throws RemoteException {
702 if (mActivityViewCallback == null || mVirtualDisplay == null
703 || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) {
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500704 return;
705 }
Mark Renoufc808f062019-02-07 15:20:37 -0500706 mActivityViewCallback.onTaskRemovalStarted(taskInfo.taskId);
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700707 }
708
709 private StackInfo getTopMostStackInfo() throws RemoteException {
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800710 // Find the topmost task on our virtual display - it will define the background
711 // color of the surface view during resizing.
712 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700713 final List<StackInfo> stackInfoList = mActivityTaskManager.getAllStackInfos();
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800714
715 // Iterate through stacks from top to bottom.
716 final int stackCount = stackInfoList.size();
717 for (int i = 0; i < stackCount; i++) {
718 final StackInfo stackInfo = stackInfoList.get(i);
719 // Only look for stacks on our virtual display.
720 if (stackInfo.displayId != displayId) {
721 continue;
722 }
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700723 // Found the topmost stack on target display.
724 return stackInfo;
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800725 }
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700726 return null;
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800727 }
728 }
Andrii Kuliand3134692017-06-26 14:57:02 -0700729}