Use main executor to run remote animation from notification

The pending intent of notification may launch the activity indirectly,
e.g. service or receiver. Before the activity is started, the
notification may be canceled, then the notification view is detached.
And View#post will only put the runnable to a list that will be
executed until attach again. So in this case since the notification
view is removed, the remote animation is never executed that leads to
remote animation timeout and the activity jumps cut without animation.

This fix ensures that the handler to animate is valid.

Also fix a flickering issue that if activity doesn't draw fast enough
(ActivityLaunchAnimator#ANIMATION_DURATION) then the notification panel
has started to collapse. The notification view may have clip bottom
larger than its actual height, that causes negative crop (as no crop)
height of the animating surface. So the first frame will show fully
height and then start to animate from the height close to zero.

This fix ensures that the crop height is at least zero.

Fixes: 158207087
Test: atest ActivityLaunchAnimatorTest
Test: Send a notification which will start a service. The service
      cancels the notification and starts a activity with >500ms
      delay. Check the launch animation should run smoothly.

Change-Id: I78426db1ce295c633271fcaa685b47cc9f697761
This commit is contained in:
Riddle Hsu
2020-06-09 15:42:50 +08:00
parent ebb7ae6679
commit 4be21f8732
3 changed files with 17 additions and 8 deletions

View File

@@ -41,6 +41,8 @@ import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
import java.util.concurrent.Executor;
/**
* A class that allows activities to be launched in a seamless way where the notification
* transforms nicely into the starting window.
@@ -59,6 +61,7 @@ public class ActivityLaunchAnimator {
private final float mWindowCornerRadius;
private final NotificationShadeWindowViewController mNotificationShadeWindowViewController;
private final NotificationShadeDepthController mDepthController;
private final Executor mMainExecutor;
private Callback mCallback;
private final Runnable mTimeoutRunnable = () -> {
setAnimationPending(false);
@@ -73,12 +76,14 @@ public class ActivityLaunchAnimator {
Callback callback,
NotificationPanelViewController notificationPanel,
NotificationShadeDepthController depthController,
NotificationListContainer container) {
NotificationListContainer container,
Executor mainExecutor) {
mNotificationPanel = notificationPanel;
mNotificationContainer = container;
mDepthController = depthController;
mNotificationShadeWindowViewController = notificationShadeWindowViewController;
mCallback = callback;
mMainExecutor = mainExecutor;
mWindowCornerRadius = ScreenDecorationsUtils
.getWindowCornerRadius(mNotificationShadeWindowViewController.getView()
.getResources());
@@ -155,7 +160,7 @@ public class ActivityLaunchAnimator {
RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
throws RemoteException {
mSourceNotification.post(() -> {
mMainExecutor.execute(() -> {
RemoteAnimationTarget primary = getPrimaryRemoteAnimationTarget(
remoteAnimationTargets);
if (primary == null) {
@@ -191,8 +196,9 @@ public class ActivityLaunchAnimator {
}
}
int targetWidth = primary.sourceContainerBounds.width();
int notificationHeight = mSourceNotification.getActualHeight()
- mSourceNotification.getClipBottomAmount();
// If the notification panel is collapsed, the clip may be larger than the height.
int notificationHeight = Math.max(mSourceNotification.getActualHeight()
- mSourceNotification.getClipBottomAmount(), 0);
int notificationWidth = mSourceNotification.getWidth();
anim.setDuration(ANIMATION_DURATION);
anim.setInterpolator(Interpolators.LINEAR);
@@ -292,7 +298,7 @@ public class ActivityLaunchAnimator {
@Override
public void onAnimationCancelled() throws RemoteException {
mSourceNotification.post(() -> {
mMainExecutor.execute(() -> {
setAnimationPending(false);
mCallback.onLaunchAnimationCancelled();
});

View File

@@ -1275,7 +1275,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mActivityLaunchAnimator = new ActivityLaunchAnimator(
mNotificationShadeWindowViewController, this, mNotificationPanelViewController,
mNotificationShadeDepthControllerLazy.get(),
(NotificationListContainer) mStackScroller);
(NotificationListContainer) mStackScroller, mContext.getMainExecutor());
// TODO: inject this.
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,

View File

@@ -36,6 +36,8 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Assert;
import org.junit.Before;
@@ -51,6 +53,7 @@ import org.mockito.junit.MockitoRule;
@TestableLooper.RunWithLooper
public class ActivityLaunchAnimatorTest extends SysuiTestCase {
private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private ActivityLaunchAnimator mLaunchAnimator;
@Mock
private ActivityLaunchAnimator.Callback mCallback;
@@ -80,8 +83,8 @@ public class ActivityLaunchAnimatorTest extends SysuiTestCase {
mCallback,
mNotificationPanelViewController,
mNotificationShadeDepthController,
mNotificationContainer);
mNotificationContainer,
mExecutor);
}
@Test