Merge "Fixed an issue where a notification may not be updated"

This commit is contained in:
Selim Cinek
2018-09-24 22:39:22 +00:00
committed by Android (Google) Code Review
3 changed files with 209 additions and 27 deletions

View File

@@ -29,18 +29,14 @@ import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.shared.system.SurfaceControlCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
/**
@@ -59,28 +55,28 @@ public class ActivityLaunchAnimator {
private final NotificationPanelView mNotificationPanel;
private final NotificationListContainer mNotificationContainer;
private final StatusBarWindowView mStatusBarWindow;
private final StatusBarStateController mStatusBarStateController;
private StatusBar mStatusBar;
private Callback mCallback;
private final Runnable mTimeoutRunnable = () -> {
setAnimationPending(false);
mStatusBar.collapsePanel(true /* animate */);
mCallback.onExpandAnimationTimedOut();
};
private boolean mAnimationPending;
private boolean mAnimationRunning;
private boolean mIsLaunchForActivity;
public ActivityLaunchAnimator(StatusBarWindowView statusBarWindow,
StatusBar statusBar,
Callback callback,
NotificationPanelView notificationPanel,
NotificationListContainer container) {
mNotificationPanel = notificationPanel;
mNotificationContainer = container;
mStatusBarWindow = statusBarWindow;
mStatusBar = statusBar;
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
mCallback = callback;
}
public RemoteAnimationAdapter getLaunchAnimation(
ExpandableNotificationRow sourceNotification, boolean occluded) {
if (mStatusBarStateController.getState() != StatusBarState.SHADE || occluded) {
if (!mCallback.areLaunchAnimationsEnabled() || occluded) {
return null;
}
AnimationRunner animationRunner = new AnimationRunner(sourceNotification);
@@ -92,10 +88,21 @@ public class ActivityLaunchAnimator {
return mAnimationPending;
}
public void setLaunchResult(int launchResult) {
/**
* Set the launch result the intent requested
*
* @param launchResult the launch result
* @param wasIntentActivity was this launch for an activity
*/
public void setLaunchResult(int launchResult, boolean wasIntentActivity) {
mIsLaunchForActivity = wasIntentActivity;
setAnimationPending((launchResult == ActivityManager.START_TASK_TO_FRONT
|| launchResult == ActivityManager.START_SUCCESS)
&& mStatusBarStateController.getState() == StatusBarState.SHADE);
&& mCallback.areLaunchAnimationsEnabled());
}
public boolean isLaunchForActivity() {
return mIsLaunchForActivity;
}
private void setAnimationPending(boolean pending) {
@@ -108,12 +115,16 @@ public class ActivityLaunchAnimator {
}
}
public boolean isAnimationRunning() {
return mAnimationRunning;
}
class AnimationRunner extends IRemoteAnimationRunner.Stub {
private final ExpandableNotificationRow mSourceNotification;
private final ExpandAnimationParameters mParams;
private final Rect mWindowCrop = new Rect();
private boolean mInstantCollapsePanel = true;
private boolean mIsFullScreenLaunch = true;
private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
public AnimationRunner(ExpandableNotificationRow sourceNofitication) {
@@ -136,10 +147,10 @@ public class ActivityLaunchAnimator {
}
setExpandAnimationRunning(true);
mInstantCollapsePanel = primary.position.y == 0
mIsFullScreenLaunch = primary.position.y == 0
&& primary.sourceContainerBounds.height()
>= mNotificationPanel.getHeight();
if (!mInstantCollapsePanel) {
if (!mIsFullScreenLaunch) {
mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);
}
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
@@ -192,9 +203,6 @@ public class ActivityLaunchAnimator {
@Override
public void onAnimationEnd(Animator animation) {
setExpandAnimationRunning(false);
if (mInstantCollapsePanel) {
mStatusBar.collapsePanel(false /* animate */);
}
invokeCallback(iRemoteAnimationFinishedCallback);
}
});
@@ -228,7 +236,9 @@ public class ActivityLaunchAnimator {
mSourceNotification.setExpandAnimationRunning(running);
mStatusBarWindow.setExpandAnimationRunning(running);
mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null);
mAnimationRunning = running;
if (!running) {
mCallback.onExpandAnimationFinished(mIsFullScreenLaunch);
applyParamsToNotification(null);
applyParamsToNotificationList(null);
}
@@ -257,7 +267,7 @@ public class ActivityLaunchAnimator {
public void onAnimationCancelled() throws RemoteException {
mSourceNotification.post(() -> {
setAnimationPending(false);
mStatusBar.onLaunchAnimationCancelled();
mCallback.onLaunchAnimationCancelled();
});
}
};
@@ -319,4 +329,30 @@ public class ActivityLaunchAnimator {
return startTranslationZ;
}
}
public interface Callback {
/**
* Called when the launch animation was cancelled.
*/
void onLaunchAnimationCancelled();
/**
* Called when the launch animation has timed out without starting an actual animation.
*/
void onExpandAnimationTimedOut();
/**
* Called when the expand animation has finished.
*
* @param launchIsFullScreen True if this launch was fullscreen, such that now the window
* fills the whole screen
*/
void onExpandAnimationFinished(boolean launchIsFullScreen);
/**
* Are animations currently enabled.
*/
boolean areLaunchAnimationsEnabled();
}
}

View File

@@ -255,7 +255,8 @@ public class StatusBar extends SystemUI implements DemoMode,
ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter,
StatusBarStateController.StateListener, AmbientPulseManager.OnAmbientChangedListener {
StatusBarStateController.StateListener, AmbientPulseManager.OnAmbientChangedListener,
ActivityLaunchAnimator.Callback {
public static final boolean MULTIUSER_DEBUG = false;
public static final boolean ENABLE_CHILD_NOTIFICATIONS
@@ -1906,6 +1907,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
@Override
public void onLaunchAnimationCancelled() {
if (!isCollapsing()) {
onClosingFinished();
@@ -1916,6 +1918,31 @@ public class StatusBar extends SystemUI implements DemoMode,
return mHeadsUpAppearanceController.shouldBeVisible();
}
@Override
public void onExpandAnimationFinished(boolean launchIsFullScreen) {
if (!isCollapsing()) {
onClosingFinished();
}
if (launchIsFullScreen) {
instantCollapseNotificationPanel();
}
}
@Override
public void onExpandAnimationTimedOut() {
if (isPresenterFullyCollapsed() && !isCollapsing()
&& !mActivityLaunchAnimator.isLaunchForActivity()) {
onClosingFinished();
} else {
collapsePanel(true /* animate */);
}
}
@Override
public boolean areLaunchAnimationsEnabled() {
return mState == StatusBarState.SHADE;
}
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -3353,7 +3380,9 @@ public class StatusBar extends SystemUI implements DemoMode,
}
public boolean isCollapsing() {
return mNotificationPanel.isCollapsing() || mActivityLaunchAnimator.isAnimationPending();
return mNotificationPanel.isCollapsing()
|| mActivityLaunchAnimator.isAnimationPending()
|| mActivityLaunchAnimator.isAnimationRunning();
}
public void addPostCollapseAction(Runnable r) {
@@ -4738,7 +4767,8 @@ public class StatusBar extends SystemUI implements DemoMode,
: notification.fullScreenIntent;
final String notificationKey = sbn.getKey();
final boolean afterKeyguardGone = intent.isActivity()
boolean isActivityIntent = intent.isActivity();
final boolean afterKeyguardGone = isActivityIntent
&& PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
final boolean wasOccluded = mIsOccluded;
@@ -4779,7 +4809,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// If we are launching a work activity and require to launch
// separate work challenge, we defer the activity action and cancel
// notification until work challenge is unlocked.
if (intent.isActivity()) {
if (isActivityIntent) {
final int userId = intent.getCreatorUserHandle().getIdentifier();
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
&& mKeyguardManager.isDeviceLocked(userId)) {
@@ -4815,7 +4845,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
null, null, getActivityOptions(adapter));
mActivityLaunchAnimator.setLaunchResult(launchResult);
mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
} catch (RemoteException | PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
// Just log the exception message.
@@ -4823,7 +4853,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// TODO: Dismiss Keyguard.
}
if (intent.isActivity()) {
if (isActivityIntent) {
mAssistManager.hideAssist();
}
}
@@ -4942,7 +4972,7 @@ public class StatusBar extends SystemUI implements DemoMode,
.startActivities(getActivityOptions(
mActivityLaunchAnimator.getLaunchAnimation(row, mIsOccluded)),
new UserHandle(UserHandle.getUserId(appUid)));
mActivityLaunchAnimator.setLaunchResult(launchResult);
mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */);
if (shouldCollapse()) {
// Putting it back on the main thread, since we're touching views
mStatusBarWindow.post(() -> animateCollapsePanels(

View File

@@ -0,0 +1,116 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.statusbar.notification;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.RemoteAnimationAdapter;
import android.view.View;
import android.widget.FrameLayout;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.stubbing.Answer;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class ActivityLaunchAnimatorTest extends SysuiTestCase {
private ActivityLaunchAnimator mLaunchAnimator;
private ActivityLaunchAnimator.Callback mCallback = mock(ActivityLaunchAnimator.Callback.class);
private StatusBarWindowView mStatusBarWindowView = mock(StatusBarWindowView.class);
private NotificationListContainer mNotificationContainer
= mock(NotificationListContainer.class);
private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class);
@Before
public void setUp() throws Exception {
when(mCallback.areLaunchAnimationsEnabled()).thenReturn(true);
mLaunchAnimator = new ActivityLaunchAnimator(
mStatusBarWindowView,
mCallback,
mock(NotificationPanelView.class),
mNotificationContainer);
}
@Test
public void testReturnsNullIfNotEnabled() {
when(mCallback.areLaunchAnimationsEnabled()).thenReturn(false);
RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
false /* occluded */);
Assert.assertTrue("The LaunchAnimator generated an animation even though animations are "
+ "disabled", launchAnimation == null);
}
@Test
public void testNotWorkingWhenOccluded() {
when(mCallback.areLaunchAnimationsEnabled()).thenReturn(false);
RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
true /* occluded */);
Assert.assertTrue("The LaunchAnimator generated an animation even though we're occluded",
launchAnimation == null);
}
@Test
public void testTimeoutCalled() {
RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
false /* occluded */);
Assert.assertTrue("No animation generated", launchAnimation != null);
executePostsImmediately(mStatusBarWindowView);
mLaunchAnimator.setLaunchResult(ActivityManager.START_SUCCESS,
true /* wasIntentActivity */);
verify(mCallback).onExpandAnimationTimedOut();
}
private void executePostsImmediately(View view) {
doAnswer((i) -> {
Runnable run = i.getArgument(0);
run.run();
return null;
}).when(view).post(any());
doAnswer((i) -> {
Runnable run = i.getArgument(0);
run.run();
return null;
}).when(view).postDelayed(any(), anyLong());
}
}