blob: 2c435a27cbce5f9a735ce75764004daccf599978 [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
19import android.annotation.NonNull;
Mathew Inwood61e8ae62018-08-14 14:17:44 +010020import android.annotation.UnsupportedAppUsage;
Andrii Kuliancf8f6832018-01-23 19:43:30 -080021import android.app.ActivityManager.StackInfo;
Andrii Kuliand3134692017-06-26 14:57:02 -070022import android.content.Context;
23import android.content.Intent;
24import android.hardware.display.DisplayManager;
25import android.hardware.display.VirtualDisplay;
26import android.hardware.input.InputManager;
27import android.os.RemoteException;
Brad Stenninga1dbe9c2018-05-02 08:29:28 -070028import android.os.UserHandle;
Andrii Kuliand3134692017-06-26 14:57:02 -070029import android.util.AttributeSet;
30import android.util.DisplayMetrics;
31import android.util.Log;
Andrii Kulianf0379de2018-03-14 16:24:07 -070032import android.view.IWindowManager;
Andrii Kuliand3134692017-06-26 14:57:02 -070033import android.view.InputDevice;
Andrii Kuliand3134692017-06-26 14:57:02 -070034import android.view.MotionEvent;
35import android.view.Surface;
chaviwff2e7d82018-11-02 11:11:27 -070036import android.view.SurfaceControl;
Andrii Kuliand3134692017-06-26 14:57:02 -070037import android.view.SurfaceHolder;
chaviwff2e7d82018-11-02 11:11:27 -070038import android.view.SurfaceSession;
Andrii Kuliand3134692017-06-26 14:57:02 -070039import android.view.SurfaceView;
40import android.view.ViewGroup;
41import android.view.WindowManager;
Andrii Kulian4b6599e2018-01-15 17:24:08 -080042import android.view.WindowManagerGlobal;
Andrii Kuliand3134692017-06-26 14:57:02 -070043
44import dalvik.system.CloseGuard;
45
Andrii Kuliancf8f6832018-01-23 19:43:30 -080046import java.util.List;
47
Andrii Kuliand3134692017-06-26 14:57:02 -070048/**
49 * Activity container that allows launching activities into itself and does input forwarding.
50 * <p>Creation of this view is only allowed to callers who have
51 * {@link android.Manifest.permission#INJECT_EVENTS} permission.
52 * <p>Activity launching into this container is restricted by the same rules that apply to launching
53 * on VirtualDisplays.
54 * @hide
55 */
56public class ActivityView extends ViewGroup {
57
58 private static final String DISPLAY_NAME = "ActivityViewVirtualDisplay";
59 private static final String TAG = "ActivityView";
60
61 private VirtualDisplay mVirtualDisplay;
62 private final SurfaceView mSurfaceView;
chaviwff2e7d82018-11-02 11:11:27 -070063
64 /**
65 * This is the root surface for the VirtualDisplay. The VirtualDisplay child surfaces will be
66 * re-parented to this surface. This will also be a child of the SurfaceView's SurfaceControl.
67 */
68 private SurfaceControl mRootSurfaceControl;
Andrii Kuliand3134692017-06-26 14:57:02 -070069
70 private final SurfaceCallback mSurfaceCallback;
71 private StateCallback mActivityViewCallback;
72
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070073 private IActivityTaskManager mActivityTaskManager;
Andrii Kuliand3134692017-06-26 14:57:02 -070074 private IInputForwarder mInputForwarder;
Andrii Kulian4b6599e2018-01-15 17:24:08 -080075 // Temp container to store view coordinates on screen.
76 private final int[] mLocationOnScreen = new int[2];
Andrii Kuliand3134692017-06-26 14:57:02 -070077
Andrii Kuliancf8f6832018-01-23 19:43:30 -080078 private TaskStackListener mTaskStackListener;
79
Andrii Kuliand3134692017-06-26 14:57:02 -070080 private final CloseGuard mGuard = CloseGuard.get();
81 private boolean mOpened; // Protected by mGuard.
82
chaviwff2e7d82018-11-02 11:11:27 -070083 private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
84 private Surface mTmpSurface = new Surface();
85
Mathew Inwood61e8ae62018-08-14 14:17:44 +010086 @UnsupportedAppUsage
Andrii Kuliand3134692017-06-26 14:57:02 -070087 public ActivityView(Context context) {
88 this(context, null /* attrs */);
89 }
90
91 public ActivityView(Context context, AttributeSet attrs) {
92 this(context, attrs, 0 /* defStyle */);
93 }
94
95 public ActivityView(Context context, AttributeSet attrs, int defStyle) {
96 super(context, attrs, defStyle);
97
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070098 mActivityTaskManager = ActivityTaskManager.getService();
Andrii Kuliand3134692017-06-26 14:57:02 -070099 mSurfaceView = new SurfaceView(context);
100 mSurfaceCallback = new SurfaceCallback();
101 mSurfaceView.getHolder().addCallback(mSurfaceCallback);
102 addView(mSurfaceView);
103
104 mOpened = true;
105 mGuard.open("release");
106 }
107
108 /** Callback that notifies when the container is ready or destroyed. */
109 public abstract static class StateCallback {
110 /**
111 * Called when the container is ready for launching activities. Calling
112 * {@link #startActivity(Intent)} prior to this callback will result in an
113 * {@link IllegalStateException}.
114 *
115 * @see #startActivity(Intent)
116 */
117 public abstract void onActivityViewReady(ActivityView view);
118 /**
119 * Called when the container can no longer launch activities. Calling
120 * {@link #startActivity(Intent)} after this callback will result in an
121 * {@link IllegalStateException}.
122 *
123 * @see #startActivity(Intent)
124 */
125 public abstract void onActivityViewDestroyed(ActivityView view);
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700126 /**
127 * Called when a task is moved to the front of the stack inside the container.
128 * This is a filtered version of {@link TaskStackListener}
129 */
130 public void onTaskMovedToFront(ActivityManager.StackInfo stackInfo) { }
Andrii Kuliand3134692017-06-26 14:57:02 -0700131 }
132
133 /**
134 * Set the callback to be notified about state changes.
135 * <p>This class must finish initializing before {@link #startActivity(Intent)} can be called.
136 * <p>Note: If the instance was ready prior to this call being made, then
137 * {@link StateCallback#onActivityViewReady(ActivityView)} will be called from within
138 * this method call.
139 *
140 * @param callback The callback to report events to.
141 *
142 * @see StateCallback
143 * @see #startActivity(Intent)
144 */
145 public void setCallback(StateCallback callback) {
146 mActivityViewCallback = callback;
147
148 if (mVirtualDisplay != null && mActivityViewCallback != null) {
149 mActivityViewCallback.onActivityViewReady(this);
150 }
151 }
152
153 /**
154 * Launch a new activity into this container.
155 * <p>Activity resolved by the provided {@link Intent} must have
156 * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
157 * launched here. Also, if activity is not owned by the owner of this container, it must allow
158 * embedding and the caller must have permission to embed.
159 * <p>Note: This class must finish initializing and
160 * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
161 * this method can be called.
162 *
163 * @param intent Intent used to launch an activity.
164 *
165 * @see StateCallback
166 * @see #startActivity(PendingIntent)
167 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100168 @UnsupportedAppUsage
Andrii Kuliand3134692017-06-26 14:57:02 -0700169 public void startActivity(@NonNull Intent intent) {
170 final ActivityOptions options = prepareActivityOptions();
171 getContext().startActivity(intent, options.toBundle());
172 }
173
174 /**
175 * Launch a new activity into this container.
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700176 * <p>Activity resolved by the provided {@link Intent} must have
177 * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
178 * launched here. Also, if activity is not owned by the owner of this container, it must allow
179 * embedding and the caller must have permission to embed.
180 * <p>Note: This class must finish initializing and
181 * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
182 * this method can be called.
183 *
184 * @param intent Intent used to launch an activity.
185 * @param user The UserHandle of the user to start this activity for.
186 *
187 *
188 * @see StateCallback
189 * @see #startActivity(PendingIntent)
190 */
191 public void startActivity(@NonNull Intent intent, UserHandle user) {
192 final ActivityOptions options = prepareActivityOptions();
193 getContext().startActivityAsUser(intent, options.toBundle(), user);
194 }
195
196 /**
197 * Launch a new activity into this container.
Andrii Kuliand3134692017-06-26 14:57:02 -0700198 * <p>Activity resolved by the provided {@link PendingIntent} must have
199 * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
200 * launched here. Also, if activity is not owned by the owner of this container, it must allow
201 * embedding and the caller must have permission to embed.
202 * <p>Note: This class must finish initializing and
203 * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
204 * this method can be called.
205 *
206 * @param pendingIntent Intent used to launch an activity.
207 *
208 * @see StateCallback
209 * @see #startActivity(Intent)
210 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100211 @UnsupportedAppUsage
Andrii Kuliand3134692017-06-26 14:57:02 -0700212 public void startActivity(@NonNull PendingIntent pendingIntent) {
213 final ActivityOptions options = prepareActivityOptions();
214 try {
215 pendingIntent.send(null /* context */, 0 /* code */, null /* intent */,
216 null /* onFinished */, null /* handler */, null /* requiredPermission */,
217 options.toBundle());
218 } catch (PendingIntent.CanceledException e) {
219 throw new RuntimeException(e);
220 }
221 }
222
223 /**
224 * Check if container is ready to launch and create {@link ActivityOptions} to target the
225 * virtual display.
226 */
227 private ActivityOptions prepareActivityOptions() {
228 if (mVirtualDisplay == null) {
229 throw new IllegalStateException(
230 "Trying to start activity before ActivityView is ready.");
231 }
232 final ActivityOptions options = ActivityOptions.makeBasic();
233 options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
234 return options;
235 }
236
237 /**
238 * Release this container. Activity launching will no longer be permitted.
239 * <p>Note: Calling this method is allowed after
240 * {@link StateCallback#onActivityViewReady(ActivityView)} callback was triggered and before
241 * {@link StateCallback#onActivityViewDestroyed(ActivityView)}.
242 *
243 * @see StateCallback
244 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100245 @UnsupportedAppUsage
Andrii Kuliand3134692017-06-26 14:57:02 -0700246 public void release() {
247 if (mVirtualDisplay == null) {
248 throw new IllegalStateException(
249 "Trying to release container that is not initialized.");
250 }
251 performRelease();
252 }
253
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800254 /**
255 * Triggers an update of {@link ActivityView}'s location on screen to properly set touch exclude
256 * regions and avoid focus switches by touches on this view.
257 */
258 public void onLocationChanged() {
259 updateLocation();
260 }
261
Andrii Kuliand3134692017-06-26 14:57:02 -0700262 @Override
263 public void onLayout(boolean changed, int l, int t, int r, int b) {
264 mSurfaceView.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */);
265 }
266
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800267 /** Send current location and size to the WM to set tap exclude region for this view. */
268 private void updateLocation() {
269 try {
270 getLocationOnScreen(mLocationOnScreen);
271 WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
272 mLocationOnScreen[0], mLocationOnScreen[1], getWidth(), getHeight());
273 } catch (RemoteException e) {
274 e.rethrowAsRuntimeException();
275 }
276 }
277
Andrii Kuliand3134692017-06-26 14:57:02 -0700278 @Override
279 public boolean onTouchEvent(MotionEvent event) {
280 return injectInputEvent(event) || super.onTouchEvent(event);
281 }
282
283 @Override
284 public boolean onGenericMotionEvent(MotionEvent event) {
285 if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
286 if (injectInputEvent(event)) {
287 return true;
288 }
289 }
290 return super.onGenericMotionEvent(event);
291 }
292
chaviw1afc65d2018-11-30 16:04:01 -0800293 private boolean injectInputEvent(MotionEvent event) {
Andrii Kuliand3134692017-06-26 14:57:02 -0700294 if (mInputForwarder != null) {
295 try {
chaviw1afc65d2018-11-30 16:04:01 -0800296 // The touch event that the ActivityView gets is in View space, but the event needs
297 // to get forwarded in screen space. This offsets the touch event by the location
298 // the ActivityView is on screen and sends it to the input forwarder.
299 getLocationOnScreen(mLocationOnScreen);
300 event.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
Andrii Kuliand3134692017-06-26 14:57:02 -0700301 return mInputForwarder.forwardEvent(event);
302 } catch (RemoteException e) {
303 e.rethrowAsRuntimeException();
304 }
305 }
306 return false;
307 }
308
309 private class SurfaceCallback implements SurfaceHolder.Callback {
310 @Override
311 public void surfaceCreated(SurfaceHolder surfaceHolder) {
chaviwff2e7d82018-11-02 11:11:27 -0700312 mTmpSurface = new Surface();
Andrii Kuliand3134692017-06-26 14:57:02 -0700313 if (mVirtualDisplay == null) {
chaviwff2e7d82018-11-02 11:11:27 -0700314 initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface()));
Andrii Kuliand3134692017-06-26 14:57:02 -0700315 if (mVirtualDisplay != null && mActivityViewCallback != null) {
316 mActivityViewCallback.onActivityViewReady(ActivityView.this);
317 }
318 } else {
chaviwff2e7d82018-11-02 11:11:27 -0700319 // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
320 // whether it has a surface. Setting a fake surface here so DisplayManager will
321 // consider this display on.
322 mVirtualDisplay.setSurface(mTmpSurface);
323 mTmpTransaction.reparent(mRootSurfaceControl,
324 mSurfaceView.getSurfaceControl().getHandle()).apply();
Andrii Kuliand3134692017-06-26 14:57:02 -0700325 }
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800326 updateLocation();
Andrii Kuliand3134692017-06-26 14:57:02 -0700327 }
328
329 @Override
330 public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
331 if (mVirtualDisplay != null) {
332 mVirtualDisplay.resize(width, height, getBaseDisplayDensity());
333 }
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800334 updateLocation();
Andrii Kuliand3134692017-06-26 14:57:02 -0700335 }
336
337 @Override
338 public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
chaviwff2e7d82018-11-02 11:11:27 -0700339 mTmpSurface.release();
340 mTmpSurface = null;
Andrii Kuliand3134692017-06-26 14:57:02 -0700341 if (mVirtualDisplay != null) {
342 mVirtualDisplay.setSurface(null);
343 }
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800344 cleanTapExcludeRegion();
Andrii Kuliand3134692017-06-26 14:57:02 -0700345 }
346 }
347
chaviwff2e7d82018-11-02 11:11:27 -0700348 private void initVirtualDisplay(SurfaceSession surfaceSession) {
Andrii Kuliand3134692017-06-26 14:57:02 -0700349 if (mVirtualDisplay != null) {
350 throw new IllegalStateException("Trying to initialize for the second time.");
351 }
352
353 final int width = mSurfaceView.getWidth();
354 final int height = mSurfaceView.getHeight();
355 final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
chaviwff2e7d82018-11-02 11:11:27 -0700356
357 // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
358 // whether it has a surface. Setting a fake surface here so DisplayManager will consider
359 // this display on.
Andrii Kuliand3134692017-06-26 14:57:02 -0700360 mVirtualDisplay = displayManager.createVirtualDisplay(
361 DISPLAY_NAME + "@" + System.identityHashCode(this),
chaviwff2e7d82018-11-02 11:11:27 -0700362 width, height, getBaseDisplayDensity(), mTmpSurface,
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700363 DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
364 | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
Andrii Kuliand3134692017-06-26 14:57:02 -0700365 if (mVirtualDisplay == null) {
366 Log.e(TAG, "Failed to initialize ActivityView");
367 return;
368 }
369
Andrii Kulianf0379de2018-03-14 16:24:07 -0700370 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
371 final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
chaviwff2e7d82018-11-02 11:11:27 -0700372
373 mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
374 .setContainerLayer(true)
375 .setName(DISPLAY_NAME)
376 .build();
377
Andrii Kulianf0379de2018-03-14 16:24:07 -0700378 try {
chaviwff2e7d82018-11-02 11:11:27 -0700379 wm.reparentDisplayContent(displayId, mRootSurfaceControl.getHandle());
Andrii Kulianf0379de2018-03-14 16:24:07 -0700380 wm.dontOverrideDisplayInfo(displayId);
381 } catch (RemoteException e) {
382 e.rethrowAsRuntimeException();
383 }
chaviwff2e7d82018-11-02 11:11:27 -0700384
385 mTmpTransaction.show(mRootSurfaceControl).apply();
Andrii Kulianf0379de2018-03-14 16:24:07 -0700386 mInputForwarder = InputManager.getInstance().createInputForwarder(displayId);
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700387 mTaskStackListener = new TaskStackListenerImpl();
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800388 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700389 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800390 } catch (RemoteException e) {
391 Log.e(TAG, "Failed to register task stack listener", e);
392 }
Andrii Kuliand3134692017-06-26 14:57:02 -0700393 }
394
395 private void performRelease() {
396 if (!mOpened) {
397 return;
398 }
399
400 mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
401
402 if (mInputForwarder != null) {
403 mInputForwarder = null;
404 }
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800405 cleanTapExcludeRegion();
Andrii Kuliand3134692017-06-26 14:57:02 -0700406
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800407 if (mTaskStackListener != null) {
408 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700409 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800410 } catch (RemoteException e) {
411 Log.e(TAG, "Failed to unregister task stack listener", e);
412 }
413 mTaskStackListener = null;
414 }
415
Andrii Kuliand3134692017-06-26 14:57:02 -0700416 final boolean displayReleased;
417 if (mVirtualDisplay != null) {
418 mVirtualDisplay.release();
419 mVirtualDisplay = null;
420 displayReleased = true;
421 } else {
422 displayReleased = false;
423 }
424
chaviwff2e7d82018-11-02 11:11:27 -0700425 if (mTmpSurface != null) {
426 mTmpSurface.release();
427 mTmpSurface = null;
Andrii Kuliand3134692017-06-26 14:57:02 -0700428 }
429
430 if (displayReleased && mActivityViewCallback != null) {
431 mActivityViewCallback.onActivityViewDestroyed(this);
432 }
433
434 mGuard.close();
435 mOpened = false;
436 }
437
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800438 /** Report to server that tap exclude region on hosting display should be cleared. */
439 private void cleanTapExcludeRegion() {
440 // Update tap exclude region with an empty rect to clean the state on server.
441 try {
442 WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
443 0 /* left */, 0 /* top */, 0 /* width */, 0 /* height */);
444 } catch (RemoteException e) {
445 e.rethrowAsRuntimeException();
446 }
447 }
448
Andrii Kuliand3134692017-06-26 14:57:02 -0700449 /** Get density of the hosting display. */
450 private int getBaseDisplayDensity() {
451 final WindowManager wm = mContext.getSystemService(WindowManager.class);
452 final DisplayMetrics metrics = new DisplayMetrics();
453 wm.getDefaultDisplay().getMetrics(metrics);
454 return metrics.densityDpi;
455 }
456
457 @Override
458 protected void finalize() throws Throwable {
459 try {
460 if (mGuard != null) {
461 mGuard.warnIfOpen();
462 performRelease();
463 }
464 } finally {
465 super.finalize();
466 }
467 }
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800468
469 /**
470 * A task change listener that detects background color change of the topmost stack on our
471 * virtual display and updates the background of the surface view. This background will be shown
472 * when surface view is resized, but the app hasn't drawn its content in new size yet.
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700473 * It also calls StateCallback.onTaskMovedToFront to notify interested parties that the stack
474 * associated with the {@link ActivityView} has had a Task moved to the front. This is useful
475 * when needing to also bring the host Activity to the foreground at the same time.
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800476 */
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700477 private class TaskStackListenerImpl extends TaskStackListener {
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800478
479 @Override
480 public void onTaskDescriptionChanged(int taskId, ActivityManager.TaskDescription td)
481 throws RemoteException {
482 if (mVirtualDisplay == null) {
483 return;
484 }
485
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700486 StackInfo stackInfo = getTopMostStackInfo();
487 if (stackInfo == null) {
488 return;
489 }
490 // Found the topmost stack on target display. Now check if the topmost task's
491 // description changed.
492 if (taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
493 mSurfaceView.setResizeBackgroundColor(td.getBackgroundColor());
494 }
495 }
496
497 @Override
498 public void onTaskMovedToFront(int taskId) throws RemoteException {
499 if (mActivityViewCallback != null) {
500 StackInfo stackInfo = getTopMostStackInfo();
501 // if StackInfo was null or unrelated to the "move to front" then there's no use
502 // notifying the callback
503 if (stackInfo != null
504 && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
505 mActivityViewCallback.onTaskMovedToFront(stackInfo);
506 }
507 }
508 }
509
510 private StackInfo getTopMostStackInfo() throws RemoteException {
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800511 // Find the topmost task on our virtual display - it will define the background
512 // color of the surface view during resizing.
513 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700514 final List<StackInfo> stackInfoList = mActivityTaskManager.getAllStackInfos();
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800515
516 // Iterate through stacks from top to bottom.
517 final int stackCount = stackInfoList.size();
518 for (int i = 0; i < stackCount; i++) {
519 final StackInfo stackInfo = stackInfoList.get(i);
520 // Only look for stacks on our virtual display.
521 if (stackInfo.displayId != displayId) {
522 continue;
523 }
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700524 // Found the topmost stack on target display.
525 return stackInfo;
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800526 }
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700527 return null;
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800528 }
529 }
Andrii Kuliand3134692017-06-26 14:57:02 -0700530}