Merge "Synchronize bubble activity rendering status and its visibility change." into qt-r1-dev

This commit is contained in:
Issei Suzuki
2019-05-21 13:13:49 +00:00
committed by Android (Google) Code Review
20 changed files with 297 additions and 15 deletions

View File

@@ -120,6 +120,7 @@ public class ActivityView extends ViewGroup {
mActivityTaskManager = ActivityTaskManager.getService();
mSurfaceView = new SurfaceView(context);
mSurfaceView.setAlpha(0f);
mSurfaceCallback = new SurfaceCallback();
mSurfaceView.getHolder().addCallback(mSurfaceCallback);
addView(mSurfaceView);
@@ -346,6 +347,16 @@ public class ActivityView extends ViewGroup {
mSurfaceView.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */);
}
@Override
public void setAlpha(float alpha) {
mSurfaceView.setAlpha(alpha);
}
@Override
public float getAlpha() {
return mSurfaceView.getAlpha();
}
@Override
public boolean gatherTransparentRegion(Region region) {
// The tap exclude region may be affected by any view on top of it, so we detect the

View File

@@ -169,4 +169,12 @@ oneway interface ITaskStackListener {
* @param taskInfo info about the task which received the back press
*/
void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo);
/*
* Called when contents are drawn for the first time on a display which can only contain one
* task.
*
* @param displayId the id of the display on which contents are drawn.
*/
void onSingleTaskDisplayDrawn(int displayId);
}

View File

@@ -173,4 +173,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo)
throws RemoteException {
}
@Override
public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
}
}

View File

@@ -27,6 +27,7 @@ import android.content.res.Configuration;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.HardwareRenderer;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
@@ -201,6 +202,29 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
/**
* A callback which reflects an alpha value of this view onto the underlying surfaces.
*
* <p class="note"><strong>Note:</strong> This doesn't have to be defined as a member variable,
* but can be defined as an inline lambda when calling ViewRootImpl#registerRtFrameCallback().
* However when we do so, the callback is triggered only for a few times and stops working for
* some reason. It's suspected that there is a problem around garbage collection, and until
* the cause is fixed, we will keep this callback in a member variable.</p>
*/
private HardwareRenderer.FrameDrawingCallback mSetSurfaceAlphaCallback = frame -> {
final ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
// In this case, the alpha value is reflected on the screen in #updateSurface() later.
return;
}
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setAlpha(mSurfaceControl, getAlpha());
t.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface, frame);
t.setEarlyWakeup();
t.apply();
};
public SurfaceView(Context context) {
this(context, null);
}
@@ -288,6 +312,17 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
updateSurface();
}
@Override
public void setAlpha(float alpha) {
super.setAlpha(alpha);
final ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot == null) {
return;
}
viewRoot.registerRtFrameCallback(mSetSurfaceAlphaCallback);
invalidate();
}
private void performDrawFinished() {
if (mPendingReportDraws > 0) {
mDrawFinished = true;
@@ -647,6 +682,13 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
}
updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl());
// Alpha value change is handled in setAlpha() directly using a local
// transaction. However it can happen that setAlpha() is called while
// local transactions cannot be applied, so the value is stored in a View
// but not yet reflected on the Surface.
mSurfaceControl.setAlpha(getAlpha());
mBackgroundControl.setAlpha(getAlpha());
// While creating the surface, we will set it's initial
// geometry. Outside of that though, we should generally
// leave it to the RenderThread.

View File

@@ -260,6 +260,13 @@ public interface WindowManager extends ViewManager {
*/
int TRANSIT_TASK_CHANGE_WINDOWING_MODE = 27;
/**
* A display which can only contain one task is being shown because the first activity is
* started or it's being turned on.
* @hide
*/
int TRANSIT_SHOW_SINGLE_TASK_DISPLAY = 28;
/**
* @hide
*/
@@ -287,7 +294,8 @@ public interface WindowManager extends ViewManager {
TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
TRANSIT_CRASHING_ACTIVITY_CLOSE,
TRANSIT_TASK_CHANGE_WINDOWING_MODE
TRANSIT_TASK_CHANGE_WINDOWING_MODE,
TRANSIT_SHOW_SINGLE_TASK_DISPLAY
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionType {}

View File

@@ -64,6 +64,14 @@ public abstract class TaskStackChangeListener {
onActivityLaunchOnSecondaryDisplayRerouted();
}
/**
* Called when contents are drawn for the first time on a display which can only contain one
* task.
*
* @param displayId the id of the display on which contents are drawn.
*/
public void onSingleTaskDisplayDrawn(int displayId) { }
public void onTaskProfileLocked(int taskId, int userId) { }
public void onTaskCreated(int taskId, ComponentName componentName) { }
public void onTaskRemoved(int taskId) { }

View File

@@ -196,11 +196,18 @@ public class TaskStackChangeListeners extends TaskStackListener {
}
@Override
public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken)
throws RemoteException {
mHandler.obtainMessage(H.ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId, 0 /* unused */,
activityToken).sendToTarget();
}
@Override
public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
mHandler.obtainMessage(H.ON_SINGLE_TASK_DISPLAY_DRAWN, displayId,
0 /* unused */).sendToTarget();
}
private final class H extends Handler {
private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -220,6 +227,7 @@ public class TaskStackChangeListeners extends TaskStackListener {
private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16;
private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17;
private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18;
private static final int ON_SINGLE_TASK_DISPLAY_DRAWN = 19;
public H(Looper looper) {
@@ -356,6 +364,12 @@ public class TaskStackChangeListeners extends TaskStackListener {
}
break;
}
case ON_SINGLE_TASK_DISPLAY_DRAWN: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onSingleTaskDisplayDrawn(msg.arg1);
}
break;
}
}
}
}

View File

@@ -16,6 +16,8 @@
package com.android.systemui.bubbles;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import android.content.Context;
@@ -129,6 +131,20 @@ class Bubble {
mInflated = true;
}
/**
* Set visibility of bubble in the expanded state.
*
* @param visibility {@code true} if the expanded bubble should be visible on the screen.
*
* Note that this contents visibility doesn't affect visibility at {@link android.view.View},
* and setting {@code false} actually means rendering the expanded view in transparent.
*/
void setContentVisibility(boolean visibility) {
if (expandedView != null) {
expandedView.setContentVisibility(visibility);
}
}
void setDismissed() {
entry.setBubbleDismissed(true);
// TODO: move this somewhere where it can be guaranteed not to run until safe from flicker
@@ -167,6 +183,13 @@ class Bubble {
return mLastAccessed;
}
/**
* @return the display id of the virtual display on which bubble contents is drawn.
*/
int getDisplayId() {
return expandedView != null ? expandedView.getVirtualDisplayId() : INVALID_DISPLAY;
}
/**
* Should be invoked whenever a Bubble is accessed (selected while expanded).
*/

View File

@@ -603,17 +603,23 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
* status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
*/
public int getExpandedDisplayId(Context context) {
final Bubble bubble = getExpandedBubble(context);
return bubble != null ? bubble.getDisplayId() : INVALID_DISPLAY;
}
@Nullable
private Bubble getExpandedBubble(Context context) {
if (mStackView == null) {
return INVALID_DISPLAY;
return null;
}
boolean defaultDisplay = context.getDisplay() != null
final boolean defaultDisplay = context.getDisplay() != null
&& context.getDisplay().getDisplayId() == DEFAULT_DISPLAY;
Bubble b = mStackView.getExpandedBubble();
if (defaultDisplay && b != null && isStackExpanded()
final Bubble expandedBubble = mStackView.getExpandedBubble();
if (defaultDisplay && expandedBubble != null && isStackExpanded()
&& !mStatusBarWindowController.getPanelExpanded()) {
return b.expandedView.getVirtualDisplayId();
return expandedBubble;
}
return INVALID_DISPLAY;
return null;
}
@VisibleForTesting
@@ -730,6 +736,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mBubbleData.setExpanded(false);
}
}
@Override
public void onSingleTaskDisplayDrawn(int displayId) {
final Bubble expandedBubble = getExpandedBubble(mContext);
if (expandedBubble != null && expandedBubble.getDisplayId() == displayId) {
expandedBubble.setContentVisibility(true);
}
}
}
private static boolean shouldAutoBubbleMessages(Context context) {

View File

@@ -185,6 +185,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
true /* singleTaskInstance */);
setContentVisibility(false);
addView(mActivityView);
// Expanded stack layout, top to bottom:
@@ -238,6 +240,22 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
}
}
/**
* Set visibility of contents in the expanded state.
*
* @param visibility {@code true} if the contents should be visible on the screen.
*
* Note that this contents visibility doesn't affect visibility at {@link android.view.View},
* and setting {@code false} actually means rendering the contents in transparent.
*/
void setContentVisibility(boolean visibility) {
final float alpha = visibility ? 1f : 0f;
mPointerView.setAlpha(alpha);
if (mActivityView != null) {
mActivityView.setAlpha(alpha);
}
}
/**
* Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
* This should be done post-move and post-animation.
@@ -310,6 +328,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
parent.removeView(mNotifRow);
}
addView(mNotifRow, 1 /* index */);
mPointerView.setAlpha(1f);
}
}
@@ -336,12 +355,12 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
removeView(mNotifRow);
mNotifRow = null;
}
setContentVisibility(false);
mActivityView.setVisibility(VISIBLE);
} else {
// Hide activity view if we had it previously
mActivityView.setVisibility(GONE);
mNotifRow = mEntry.getRow();
}
updateView();
}
@@ -440,6 +459,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
mActivityView.onLocationChanged();
} else if (mNotifRow != null) {
applyRowState(mNotifRow);
mPointerView.setAlpha(1f);
}
updateHeight();
}

View File

@@ -740,12 +740,16 @@ public class BubbleStackView extends FrameLayout {
}
final Bubble previouslySelected = mExpandedBubble;
mExpandedBubble = bubbleToSelect;
if (mIsExpanded) {
// Make the container of the expanded view transparent before removing the expanded view
// from it. Otherwise a punch hole created by {@link android.view.SurfaceView} in the
// expanded view becomes visible on the screen. See b/126856255
mExpandedViewContainer.setAlpha(0.0f);
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
if (previouslySelected != null) {
previouslySelected.setContentVisibility(false);
}
updateExpandedBubble();
updatePointerPosition();
requestUpdate();
@@ -774,6 +778,14 @@ public class BubbleStackView extends FrameLayout {
}
if (wasExpanded) {
// Collapse the stack
mExpandedViewContainer.setAlpha(0.0f);
// TODO: In order to prevent flicker, code below should be executed after the alpha
// value set on the mExpandedViewContainer is reflected on the screen. However, we
// cannot just postpone the execution like #setSelectedBubble(), since some of member
// variables referred by the code are overridden before the execution.
if (mExpandedBubble != null) {
mExpandedBubble.setContentVisibility(false);
}
animateExpansion(false /* expand */);
logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
} else {
@@ -931,14 +943,10 @@ public class BubbleStackView extends FrameLayout {
if (shouldExpand) {
mExpandedViewContainer.setTranslationX(xStart);
mExpandedViewContainer.setTranslationY(yStart);
mExpandedViewContainer.setAlpha(0f);
}
mExpandedViewXAnim.animateToFinalPosition(shouldExpand ? 0f : xStart);
mExpandedViewYAnim.animateToFinalPosition(shouldExpand ? yDest : yStart);
mExpandedViewContainer.animate()
.setDuration(100)
.alpha(shouldExpand ? 1f : 0f);
}
}

View File

@@ -40,6 +40,7 @@ import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
@@ -3194,6 +3195,8 @@ class ActivityStack extends ConfigurationContainer {
if (newTask) {
if (r.mLaunchTaskBehind) {
transit = TRANSIT_TASK_OPEN_BEHIND;
} else if (getDisplay().isSingleTaskInstance()) {
transit = TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
} else {
// If a new task is being launched, then mark the existing top activity as
// supporting picture-in-picture while pausing only if the starting activity

View File

@@ -34,7 +34,6 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.voice.IVoiceInteractionSession;
import android.util.Pair;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
@@ -188,6 +187,13 @@ public abstract class ActivityTaskManagerInternal {
*/
public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
/**
* Notify listeners that contents are drawn for the first time on a single task display.
*
* @param displayId An ID of the display on which contents are drawn.
*/
public abstract void notifySingleTaskDisplayDrawn(int displayId);
/**
* Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
*

View File

@@ -6087,13 +6087,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
public void notifyAppTransitionStarting(SparseIntArray reasons,
long timestamp) {
synchronized (mGlobalLock) {
mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
reasons, timestamp);
}
}
@Override
public void notifySingleTaskDisplayDrawn(int displayId) {
mTaskChangeNotificationController.notifySingleTaskDisplayDrawn(displayId);
}
@Override
public void notifyAppTransitionFinished() {
synchronized (mGlobalLock) {

View File

@@ -29,6 +29,7 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPE
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
@@ -2052,6 +2053,9 @@ public class AppTransition implements Dump {
case TRANSIT_CRASHING_ACTIVITY_CLOSE: {
return "TRANSIT_CRASHING_ACTIVITY_CLOSE";
}
case TRANSIT_SHOW_SINGLE_TASK_DISPLAY: {
return "TRANSIT_SHOW_SINGLE_TASK_DISPLAY";
}
default: {
return "<UNKNOWN: " + transition + ">";
}

View File

@@ -28,6 +28,7 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_W
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
@@ -211,6 +212,12 @@ public class AppTransitionController {
mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
SystemClock.uptimeMillis());
if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
mService.mAtmInternal.notifySingleTaskDisplayDrawn(mDisplayContent.getDisplayId());
});
}
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
mDisplayContent.pendingLayoutChanges |=

View File

@@ -35,6 +35,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
@@ -1217,6 +1218,15 @@ class RootActivityContainer extends ConfigurationContainer
if (displayShouldSleep) {
stack.goToSleepIfPossible(false /* shuttingDown */);
} else {
// When the display which can only contain one task turns on, start a special
// transition. {@link AppTransitionController#handleAppTransitionReady} later
// picks up the transition, and schedules
// {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is
// triggered after contents are drawn on the display.
if (display.isSingleTaskInstance()) {
display.mDisplayContent.prepareAppTransition(
TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false);
}
stack.awakeFromSleepingLocked();
if (stack.isFocusedStackOnDisplay()
&& !mStackSupervisor.getKeyguardController()

View File

@@ -54,6 +54,7 @@ class TaskChangeNotificationController {
private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20;
private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21;
private static final int NOTIFY_SINGLE_TASK_DISPLAY_DRAWN = 22;
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -154,6 +155,10 @@ class TaskChangeNotificationController {
l.onSizeCompatModeActivityChanged(m.arg1, (IBinder) m.obj);
};
private final TaskStackConsumer mNotifySingleTaskDisplayDrawn = (l, m) -> {
l.onSingleTaskDisplayDrawn(m.arg1);
};
@FunctionalInterface
public interface TaskStackConsumer {
void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -233,6 +238,9 @@ class TaskChangeNotificationController {
case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
break;
case NOTIFY_SINGLE_TASK_DISPLAY_DRAWN:
forAllRemoteListeners(mNotifySingleTaskDisplayDrawn, msg);
break;
}
}
}
@@ -477,4 +485,14 @@ class TaskChangeNotificationController {
forAllLocalListeners(mNotifyBackPressedOnTaskRoot, msg);
msg.sendToTarget();
}
/**
* Notify listeners that contents are drawn for the first time on a single task display.
*/
void notifySingleTaskDisplayDrawn(int displayId) {
final Message msg = mHandler.obtainMessage(NOTIFY_SINGLE_TASK_DISPLAY_DRAWN,
displayId, 0 /* unused */);
forAllLocalListeners(mNotifySingleTaskDisplayDrawn, msg);
msg.sendToTarget();
}
}

View File

@@ -49,6 +49,9 @@
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityViewTestActivity" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityInActivityView"
android:resizeableActivity="true" />
<activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
android:showWhenLocked="true" />
</application>

View File

@@ -16,6 +16,8 @@
package com.android.server.wm;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
@@ -26,18 +28,22 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityTaskManager;
import android.app.ActivityView;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
import android.app.TaskStackListener;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.support.test.uiautomator.UiDevice;
import android.text.TextUtils;
import android.view.ViewGroup;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
@@ -231,6 +237,40 @@ public class TaskStackChangedListenerTest {
assertTrue(activity.mOnDetachedFromWindowCalled);
}
@Test
public void testTaskOnSingleTaskDisplayDrawn() throws Exception {
final Instrumentation instrumentation = getInstrumentation();
final CountDownLatch activityViewReadyLatch = new CountDownLatch(1);
final CountDownLatch singleTaskDisplayDrawnLatch = new CountDownLatch(1);
registerTaskStackChangedListener(new TaskStackListener() {
@Override
public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
singleTaskDisplayDrawnLatch.countDown();
}
});
final ActivityViewTestActivity activity =
(ActivityViewTestActivity) startTestActivity(ActivityViewTestActivity.class);
final ActivityView activityView = activity.getActivityView();
activityView.setCallback(new ActivityView.StateCallback() {
@Override
public void onActivityViewReady(ActivityView view) {
activityViewReadyLatch.countDown();
}
@Override
public void onActivityViewDestroyed(ActivityView view) {
}
});
waitForCallback(activityViewReadyLatch);
final Context context = instrumentation.getContext();
Intent intent = new Intent(context, ActivityInActivityView.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
activityView.startActivity(intent);
waitForCallback(singleTaskDisplayDrawnLatch);
}
/**
* Starts the provided activity and returns the started instance.
*/
@@ -369,4 +409,29 @@ public class TaskStackChangedListenerTest {
mOnDetachedFromWindowCountDownLatch = countDownLatch;
}
}
public static class ActivityViewTestActivity extends TestActivity {
private ActivityView mActivityView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivityView = new ActivityView(this, null /* attrs */, 0 /* defStyle */,
true /* singleTaskInstance */);
setContentView(mActivityView);
ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
layoutParams.width = MATCH_PARENT;
layoutParams.height = MATCH_PARENT;
mActivityView.requestLayout();
}
ActivityView getActivityView() {
return mActivityView;
}
}
// Activity that has {@link android.R.attr#resizeableActivity} attribute set to {@code true}
public static class ActivityInActivityView extends TestActivity {}
}