• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 
17 package com.android.systemui.statusbar.notification.row;
18 
19 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
20 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
21 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
22 import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
23 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
24 
25 import android.net.Uri;
26 import android.os.UserHandle;
27 import android.provider.Settings;
28 import android.util.Log;
29 import android.view.View;
30 import android.view.ViewGroup;
31 
32 import androidx.annotation.NonNull;
33 import androidx.annotation.Nullable;
34 import androidx.annotation.VisibleForTesting;
35 
36 import com.android.internal.logging.MetricsLogger;
37 import com.android.internal.logging.UiEventLogger;
38 import com.android.internal.statusbar.IStatusBarService;
39 import com.android.systemui.Flags;
40 import com.android.systemui.flags.FeatureFlagsClassic;
41 import com.android.systemui.plugins.FalsingManager;
42 import com.android.systemui.plugins.PluginManager;
43 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
44 import com.android.systemui.plugins.statusbar.StatusBarStateController;
45 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
46 import com.android.systemui.statusbar.SmartReplyController;
47 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
48 import com.android.systemui.statusbar.notification.FeedbackIcon;
49 import com.android.systemui.statusbar.notification.collection.EntryAdapterFactory;
50 import com.android.systemui.statusbar.notification.collection.PipelineEntry;
51 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
52 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
53 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
54 import com.android.systemui.statusbar.notification.collection.render.NodeController;
55 import com.android.systemui.statusbar.notification.collection.render.NotifViewController;
56 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
57 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
58 import com.android.systemui.statusbar.notification.row.dagger.AppName;
59 import com.android.systemui.statusbar.notification.row.dagger.NotificationKey;
60 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
61 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
62 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
63 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
64 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger;
65 import com.android.systemui.statusbar.phone.KeyguardBypassController;
66 import com.android.systemui.statusbar.policy.SmartReplyConstants;
67 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
68 import com.android.systemui.util.time.SystemClock;
69 import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor;
70 
71 import com.google.android.msdl.data.model.MSDLToken;
72 import com.google.android.msdl.domain.MSDLPlayer;
73 
74 import java.util.List;
75 
76 import javax.inject.Inject;
77 import javax.inject.Named;
78 
79 /**
80  * Controller for {@link ExpandableNotificationRow}.
81  */
82 @NotificationRowScope
83 public class ExpandableNotificationRowController implements NotifViewController {
84     private static final String TAG = "NotifRowController";
85 
86     static final Uri BUBBLES_SETTING_URI =
87             Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BUBBLES);
88     private static final String BUBBLES_SETTING_ENABLED_VALUE = "1";
89     private final ExpandableNotificationRow mView;
90     private final NotificationListContainer mListContainer;
91     private final RemoteInputViewSubcomponent.Factory mRemoteInputViewSubcomponentFactory;
92     private final ActivatableNotificationViewController mActivatableNotificationViewController;
93     private final PluginManager mPluginManager;
94     private final SystemClock mClock;
95     private final String mAppName;
96     private final String mNotificationKey;
97     private final ColorUpdateLogger mColorUpdateLogger;
98     private final KeyguardBypassController mKeyguardBypassController;
99     private final GroupMembershipManager mGroupMembershipManager;
100     private final GroupExpansionManager mGroupExpansionManager;
101     private final RowContentBindStage mRowContentBindStage;
102     private final NotificationRowStatsLogger mStatsLogger;
103     private final NotificationRowLogger mLogBufferLogger;
104     private final HeadsUpManager mHeadsUpManager;
105     private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener;
106     private final StatusBarStateController mStatusBarStateController;
107     private final MetricsLogger mMetricsLogger;
108     private final NotificationChildrenContainerLogger mChildrenContainerLogger;
109     private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener;
110     private final NotificationGutsManager mNotificationGutsManager;
111     private final OnUserInteractionCallback mOnUserInteractionCallback;
112     private final FalsingManager mFalsingManager;
113     private final NotificationRebindingTracker mNotificationRebindingTracker;
114     private final FeatureFlagsClassic mFeatureFlags;
115     private final boolean mAllowLongPress;
116     private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
117     private final SmartReplyConstants mSmartReplyConstants;
118     private final SmartReplyController mSmartReplyController;
119     private final ExpandableNotificationRowDragController mDragController;
120     private final NotificationDismissibilityProvider mDismissibilityProvider;
121     private final IStatusBarService mStatusBarService;
122     private final UiEventLogger mUiEventLogger;
123     private final MSDLPlayer mMSDLPlayer;
124     private final NotificationSettingsController mSettingsController;
125     private final EntryAdapterFactory mEntryAdapterFactory;
126     private final WindowRootViewBlurInteractor mWindowRootViewBlurInteractor;
127 
128     @VisibleForTesting
129     final NotificationSettingsController.Listener mSettingsListener =
130             new NotificationSettingsController.Listener() {
131                 @Override
132                 public void onSettingChanged(Uri setting, int userId, String value) {
133                     if (BUBBLES_SETTING_URI.equals(setting)) {
134                         if (NotificationBundleUi.isEnabled()
135                                 && mView.getEntryAdapter().getSbn() == null) {
136                             // only valid for notification rows
137                             return;
138                         }
139                         final int viewUserId = NotificationBundleUi.isEnabled()
140                             ? mView.getEntryAdapter().getSbn().getUserId()
141                             : mView.getEntryLegacy().getSbn().getUserId();
142                         if (viewUserId == UserHandle.USER_ALL || viewUserId == userId) {
143                             mView.getPrivateLayout().setBubblesEnabledForUser(
144                                     BUBBLES_SETTING_ENABLED_VALUE.equals(value));
145                         }
146                     }
147                 }
148             };
149     private final ExpandableNotificationRow.ExpandableNotificationRowLogger mLoggerCallback =
150             new ExpandableNotificationRow.ExpandableNotificationRowLogger() {
151                 @Override
152                 public void logNotificationExpansion(String key, int location, boolean userAction,
153                         boolean expanded) {
154                     mStatsLogger.onNotificationExpansionChanged(key, expanded, location,
155                             userAction);
156                 }
157 
158                 @Override
159                 public void logKeepInParentChildDetached(
160                         String child,
161                         String oldParent
162                 ) {
163                     mLogBufferLogger.logKeepInParentChildDetached(child, oldParent);
164                 }
165 
166                 @Override
167                 public void logSkipAttachingKeepInParentChild(
168                         String child,
169                         String newParent
170                 ) {
171                     mLogBufferLogger.logSkipAttachingKeepInParentChild(child, newParent);
172                 }
173 
174                 @Override
175                 public void logRemoveTransientFromContainer(
176                         String childEntry,
177                         String containerEntry
178                 ) {
179                     mLogBufferLogger.logRemoveTransientFromContainer(childEntry, containerEntry);
180                 }
181 
182                 @Override
183                 public void logRemoveTransientFromNssl(
184                         String childEntry
185                 ) {
186                     mLogBufferLogger.logRemoveTransientFromNssl(childEntry);
187                 }
188 
189                 @Override
190                 public void logRemoveTransientFromViewGroup(
191                         String childEntry,
192                         ViewGroup containerView
193                 ) {
194                     mLogBufferLogger.logRemoveTransientFromViewGroup(childEntry, containerView);
195                 }
196 
197                 @Override
198                 public void logAddTransientRow(
199                         String childEntry,
200                         String containerEntry,
201                         int index
202                 ) {
203                     mLogBufferLogger.logAddTransientRow(childEntry, containerEntry, index);
204                 }
205 
206                 @Override
207                 public void logRemoveTransientRow(
208                         String childEntry,
209                         String containerEntry
210                 ) {
211                     mLogBufferLogger.logRemoveTransientRow(childEntry, containerEntry);
212                 }
213 
214                 @Override
215                 public void logResetAllContentAlphas(
216                         String entry
217                 ) {
218                     mLogBufferLogger.logResetAllContentAlphas(entry);
219                 }
220 
221                 @Override
222                 public void logSkipResetAllContentAlphas(
223                         String entry
224                 ) {
225                     mLogBufferLogger.logSkipResetAllContentAlphas(entry);
226                 }
227 
228                 @Override
229                 public void logStartAppearAnimation(String entry, boolean isAppear) {
230                     mLogBufferLogger.logStartAppearAnimation(entry, isAppear);
231                 }
232 
233                 @Override
234                 public void logCancelAppearDrawing(String entry, boolean wasDrawing) {
235                     mLogBufferLogger.logCancelAppearDrawing(entry, wasDrawing);
236                 }
237 
238                 @Override
239                 public void logAppearAnimationStarted(String entry, boolean isAppear) {
240                     mLogBufferLogger.logAppearAnimationStarted(entry, isAppear);
241                 }
242 
243                 @Override
244                 public void logAppearAnimationSkipped(String entry, boolean isAppear) {
245                     mLogBufferLogger.logAppearAnimationSkipped(entry, isAppear);
246                 }
247 
248                 @Override
249                 public void logAppearAnimationFinished(String entry, boolean isAppear,
250                         boolean cancelled) {
251                     mLogBufferLogger.logAppearAnimationFinished(entry, isAppear, cancelled);
252                 }
253             };
254 
255 
256     @Inject
ExpandableNotificationRowController( ExpandableNotificationRow view, ActivatableNotificationViewController activatableNotificationViewController, RemoteInputViewSubcomponent.Factory rivSubcomponentFactory, MetricsLogger metricsLogger, ColorUpdateLogger colorUpdateLogger, NotificationRowLogger logBufferLogger, NotificationChildrenContainerLogger childrenContainerLogger, NotificationListContainer listContainer, SmartReplyConstants smartReplyConstants, SmartReplyController smartReplyController, PluginManager pluginManager, SystemClock clock, @AppName String appName, @NotificationKey String notificationKey, KeyguardBypassController keyguardBypassController, GroupMembershipManager groupMembershipManager, GroupExpansionManager groupExpansionManager, RowContentBindStage rowContentBindStage, NotificationRowStatsLogger statsLogger, HeadsUpManager headsUpManager, ExpandableNotificationRow.OnExpandClickListener onExpandClickListener, StatusBarStateController statusBarStateController, NotificationGutsManager notificationGutsManager, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, OnUserInteractionCallback onUserInteractionCallback, FalsingManager falsingManager, FeatureFlagsClassic featureFlags, PeopleNotificationIdentifier peopleNotificationIdentifier, NotificationSettingsController settingsController, ExpandableNotificationRowDragController dragController, NotificationDismissibilityProvider dismissibilityProvider, IStatusBarService statusBarService, UiEventLogger uiEventLogger, MSDLPlayer msdlPlayer, NotificationRebindingTracker notificationRebindingTracker, EntryAdapterFactory entryAdapterFactory, WindowRootViewBlurInteractor windowRootViewBlurInteractor)257     public ExpandableNotificationRowController(
258             ExpandableNotificationRow view,
259             ActivatableNotificationViewController activatableNotificationViewController,
260             RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
261             MetricsLogger metricsLogger,
262             ColorUpdateLogger colorUpdateLogger,
263             NotificationRowLogger logBufferLogger,
264             NotificationChildrenContainerLogger childrenContainerLogger,
265             NotificationListContainer listContainer,
266             SmartReplyConstants smartReplyConstants,
267             SmartReplyController smartReplyController,
268             PluginManager pluginManager,
269             SystemClock clock,
270             @AppName String appName,
271             @NotificationKey String notificationKey,
272             KeyguardBypassController keyguardBypassController,
273             GroupMembershipManager groupMembershipManager,
274             GroupExpansionManager groupExpansionManager,
275             RowContentBindStage rowContentBindStage,
276             NotificationRowStatsLogger statsLogger,
277             HeadsUpManager headsUpManager,
278             ExpandableNotificationRow.OnExpandClickListener onExpandClickListener,
279             StatusBarStateController statusBarStateController,
280             NotificationGutsManager notificationGutsManager,
281             @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
282             OnUserInteractionCallback onUserInteractionCallback,
283             FalsingManager falsingManager,
284             FeatureFlagsClassic featureFlags,
285             PeopleNotificationIdentifier peopleNotificationIdentifier,
286             NotificationSettingsController settingsController,
287             ExpandableNotificationRowDragController dragController,
288             NotificationDismissibilityProvider dismissibilityProvider,
289             IStatusBarService statusBarService,
290             UiEventLogger uiEventLogger,
291             MSDLPlayer msdlPlayer,
292             NotificationRebindingTracker notificationRebindingTracker,
293             EntryAdapterFactory entryAdapterFactory,
294             WindowRootViewBlurInteractor windowRootViewBlurInteractor) {
295         mView = view;
296         mListContainer = listContainer;
297         mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory;
298         mActivatableNotificationViewController = activatableNotificationViewController;
299         mPluginManager = pluginManager;
300         mClock = clock;
301         mAppName = appName;
302         mNotificationKey = notificationKey;
303         mKeyguardBypassController = keyguardBypassController;
304         mGroupMembershipManager = groupMembershipManager;
305         mGroupExpansionManager = groupExpansionManager;
306         mRowContentBindStage = rowContentBindStage;
307         mStatsLogger = statsLogger;
308         mHeadsUpManager = headsUpManager;
309         mOnExpandClickListener = onExpandClickListener;
310         mStatusBarStateController = statusBarStateController;
311         mNotificationGutsManager = notificationGutsManager;
312         mOnUserInteractionCallback = onUserInteractionCallback;
313         mFalsingManager = falsingManager;
314         mNotificationRebindingTracker = notificationRebindingTracker;
315         mOnFeedbackClickListener = mNotificationGutsManager::openGuts;
316         mAllowLongPress = allowLongPress;
317         mFeatureFlags = featureFlags;
318         mPeopleNotificationIdentifier = peopleNotificationIdentifier;
319         mSettingsController = settingsController;
320         mDragController = dragController;
321         mMetricsLogger = metricsLogger;
322         mChildrenContainerLogger = childrenContainerLogger;
323         mColorUpdateLogger = colorUpdateLogger;
324         mLogBufferLogger = logBufferLogger;
325         mSmartReplyConstants = smartReplyConstants;
326         mSmartReplyController = smartReplyController;
327         mDismissibilityProvider = dismissibilityProvider;
328         mStatusBarService = statusBarService;
329         mUiEventLogger = uiEventLogger;
330         mMSDLPlayer = msdlPlayer;
331         mEntryAdapterFactory = entryAdapterFactory;
332         mWindowRootViewBlurInteractor = windowRootViewBlurInteractor;
333     }
334 
335     /**
336      * Initialize the controller.
337      */
init(PipelineEntry entry)338     public void init(PipelineEntry entry) {
339         mActivatableNotificationViewController.init();
340         mView.initialize(
341                 mEntryAdapterFactory.create(entry),
342                 entry,
343                 mRemoteInputViewSubcomponentFactory,
344                 mAppName,
345                 mNotificationKey,
346                 mLoggerCallback,
347                 mKeyguardBypassController,
348                 mGroupMembershipManager,
349                 mGroupExpansionManager,
350                 mHeadsUpManager,
351                 mRowContentBindStage,
352                 mOnExpandClickListener,
353                 mOnFeedbackClickListener,
354                 mFalsingManager,
355                 mStatusBarStateController,
356                 mPeopleNotificationIdentifier,
357                 mOnUserInteractionCallback,
358                 mNotificationGutsManager,
359                 mDismissibilityProvider,
360                 mMetricsLogger,
361                 mChildrenContainerLogger,
362                 mColorUpdateLogger,
363                 mSmartReplyConstants,
364                 mSmartReplyController,
365                 mStatusBarService,
366                 mUiEventLogger,
367                 mNotificationRebindingTracker
368         );
369         mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
370         if (mAllowLongPress) {
371             if (mFeatureFlags.isEnabled(
372                     com.android.systemui.flags.Flags.NOTIFICATION_DRAG_TO_CONTENTS)) {
373                 mView.setDragController(mDragController);
374             }
375 
376             mView.setLongPressListener((v, x, y, item) -> {
377                 if (com.android.systemui.Flags.msdlFeedback()) {
378                     mMSDLPlayer.playToken(MSDLToken.LONG_PRESS, null);
379                 }
380                 if (mView.isSummaryWithChildren()) {
381                     mView.expandNotification();
382                     return true;
383                 }
384                 return mNotificationGutsManager.openGuts(v, x, y, item);
385             });
386         }
387         if (ENABLE_REMOTE_INPUT) {
388             mView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
389         }
390 
391         mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
392             @Override
393             public void onViewAttachedToWindow(View v) {
394                 if (NotificationBundleUi.isEnabled()) {
395                     mView.setInitializationTime(mClock.elapsedRealtime());
396                     if (mView.getEntryAdapter().getSbn() != null) {
397                         mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
398                     }
399                 } else {
400                     mView.getEntryLegacy().setInitializationTime(mClock.elapsedRealtime());
401                     mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
402                 }
403                 mPluginManager.addPluginListener(mView,
404                         NotificationMenuRowPlugin.class, false /* Allow multiple */);
405                 if (!SceneContainerFlag.isEnabled()) {
406                     mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD);
407                     mStatusBarStateController.addCallback(mStatusBarStateListener);
408                 }
409 
410             }
411 
412             @Override
413             public void onViewDetachedFromWindow(View v) {
414                 mPluginManager.removePluginListener(mView);
415                 if (!SceneContainerFlag.isEnabled()) {
416                     mStatusBarStateController.removeCallback(mStatusBarStateListener);
417                 }
418                 mSettingsController.removeCallback(BUBBLES_SETTING_URI, mSettingsListener);
419             }
420         });
421 
422         if (Flags.notificationRowTransparency()) {
423             collectFlow(mView, mWindowRootViewBlurInteractor.isBlurCurrentlySupported(),
424                     mView::setIsBlurSupported);
425         }
426     }
427 
428     private final StatusBarStateController.StateListener mStatusBarStateListener =
429             new StatusBarStateController.StateListener() {
430                 @Override
431                 public void onStateChanged(int newState) {
432                     mView.setOnKeyguard(newState == KEYGUARD);
433                 }
434             };
435 
436     @Override
437     @NonNull
getNodeLabel()438     public String getNodeLabel() {
439         return NotificationBundleUi.isEnabled()
440                 ? mView.getLoggingKey()
441                 : logKey(mView.getEntryLegacy());
442     }
443 
444     @Override
445     @NonNull
getView()446     public View getView() {
447         return mView;
448     }
449 
450     @Override
getChildAt(int index)451     public View getChildAt(int index) {
452         return mView.getChildNotificationAt(index);
453     }
454 
455     @Override
addChildAt(NodeController child, int index)456     public void addChildAt(NodeController child, int index) {
457         ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView();
458 
459         mView.addChildNotification((ExpandableNotificationRow) child.getView(), index);
460         mListContainer.notifyGroupChildAdded(childView);
461         childView.setChangingPosition(false);
462     }
463 
464     @Override
moveChildTo(NodeController child, int index)465     public void moveChildTo(NodeController child, int index) {
466         ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView();
467         childView.setChangingPosition(true);
468         mView.removeChildNotification(childView);
469         mView.addChildNotification(childView, index);
470         childView.setChangingPosition(false);
471     }
472 
473     @Override
removeChild(NodeController child, boolean isTransfer)474     public void removeChild(NodeController child, boolean isTransfer) {
475         ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView();
476 
477         if (isTransfer) {
478             childView.setChangingPosition(true);
479         }
480         mView.removeChildNotification(childView);
481         if (!isTransfer) {
482             mListContainer.notifyGroupChildRemoved(childView, mView.getChildrenContainer());
483         }
484     }
485 
486     @Override
onViewAdded()487     public void onViewAdded() {
488     }
489 
490     @Override
onViewMoved()491     public void onViewMoved() {
492     }
493 
494     @Override
onViewRemoved()495     public void onViewRemoved() {
496     }
497 
498     @Override
getChildCount()499     public int getChildCount() {
500         final List<ExpandableNotificationRow> mChildren = mView.getAttachedChildren();
501         return mChildren != null ? mChildren.size() : 0;
502     }
503 
504     @Override
setUntruncatedChildCount(int childCount)505     public void setUntruncatedChildCount(int childCount) {
506         if (mView.isSummaryWithChildren()) {
507             mView.setUntruncatedChildCount(childCount);
508         } else {
509             Log.w(TAG, "Called setUntruncatedChildCount(" + childCount + ") on a leaf row");
510         }
511     }
512 
513     @Override
setNotificationGroupWhen(long whenMillis)514     public void setNotificationGroupWhen(long whenMillis) {
515         if (mView.isSummaryWithChildren()) {
516             mView.setNotificationGroupWhen(whenMillis);
517         } else {
518             Log.w(TAG, "Called setNotificationTime(" + whenMillis + ") on a leaf row");
519         }
520     }
521 
522     @Override
setSystemExpanded(boolean systemExpanded)523     public void setSystemExpanded(boolean systemExpanded) {
524         mView.setSystemExpanded(systemExpanded);
525     }
526 
527     @Override
setLastAudibleMs(long lastAudibleMs)528     public void setLastAudibleMs(long lastAudibleMs) {
529         mView.setLastAudiblyAlertedMs(lastAudibleMs);
530     }
531 
532     @Override
setFeedbackIcon(@ullable FeedbackIcon icon)533     public void setFeedbackIcon(@Nullable FeedbackIcon icon) {
534         mView.setFeedbackIcon(icon);
535     }
536 
537     @Override
offerToKeepInParentForAnimation()538     public boolean offerToKeepInParentForAnimation() {
539         //If the User dismissed the notification's parent, we want to keep it attached until the
540         //dismiss animation is ongoing. Therefore we don't want to remove it in the ShadeViewDiffer.
541         if (mView.isParentDismissed()) {
542             mView.setKeepInParentForDismissAnimation(true);
543             return true;
544         }
545 
546         //Otherwise the view system doesn't do the removal, so we rely on the ShadeViewDiffer
547         return false;
548     }
549 
550     @Override
removeFromParentIfKeptForAnimation()551     public boolean removeFromParentIfKeptForAnimation() {
552         ExpandableNotificationRow parent = mView.getNotificationParent();
553         if (mView.keepInParentForDismissAnimation() && parent != null) {
554             parent.removeChildNotification(mView);
555             return true;
556         }
557 
558         return false;
559     }
560 
561     @Override
resetKeepInParentForAnimation()562     public void resetKeepInParentForAnimation() {
563         mView.setKeepInParentForDismissAnimation(false);
564     }
565 }
566