1 /* 2 * Copyright (C) 2018 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 Licen 15 */ 16 17 18 package com.android.systemui.statusbar.notification.stack; 19 20 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE; 21 22 import android.animation.Animator; 23 import android.animation.AnimatorListenerAdapter; 24 import android.animation.ValueAnimator; 25 import android.content.res.Resources; 26 import android.graphics.Rect; 27 import android.os.Handler; 28 import android.service.notification.StatusBarNotification; 29 import android.view.MotionEvent; 30 import android.view.View; 31 import android.view.ViewConfiguration; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 import com.android.internal.jank.InteractionJankMonitor; 35 import com.android.systemui.SwipeHelper; 36 import com.android.systemui.dagger.qualifiers.Main; 37 import com.android.systemui.dump.DumpManager; 38 import com.android.systemui.flags.FeatureFlags; 39 import com.android.systemui.plugins.FalsingManager; 40 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; 41 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; 42 import com.android.systemui.statusbar.notification.SourceType; 43 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 44 import com.android.systemui.statusbar.notification.row.ExpandableView; 45 46 import java.lang.ref.WeakReference; 47 48 import javax.inject.Inject; 49 50 class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeActionHelper { 51 52 @VisibleForTesting 53 protected static final long COVER_MENU_DELAY = 4000; 54 private static final String TAG = "NotificationSwipeHelper"; 55 private static final SourceType SWIPE_DISMISS = SourceType.from("SwipeDismiss"); 56 private final Runnable mFalsingCheck; 57 private View mTranslatingParentView; 58 private View mMenuExposedView; 59 private final NotificationCallback mCallback; 60 private final NotificationMenuRowPlugin.OnMenuEventListener mMenuListener; 61 62 private static final long SWIPE_MENU_TIMING = 200; 63 64 // Hold a weak ref to the menu row so that it isn't accidentally retained in memory. The 65 // lifetime of the row should be the same as the ActivatableView, which is owned by the 66 // NotificationStackScrollLayout. If the notification isn't in the notification shade, then it 67 // isn't possible to swipe it and, so, this class doesn't need to "help." 68 private WeakReference<NotificationMenuRowPlugin> mCurrMenuRowRef; 69 private boolean mIsExpanded; 70 private boolean mPulsing; 71 private final NotificationRoundnessManager mNotificationRoundnessManager; 72 NotificationSwipeHelper( Resources resources, ViewConfiguration viewConfiguration, FalsingManager falsingManager, FeatureFlags featureFlags, NotificationCallback callback, NotificationMenuRowPlugin.OnMenuEventListener menuListener, NotificationRoundnessManager notificationRoundnessManager)73 NotificationSwipeHelper( 74 Resources resources, 75 ViewConfiguration viewConfiguration, 76 FalsingManager falsingManager, 77 FeatureFlags featureFlags, 78 NotificationCallback callback, 79 NotificationMenuRowPlugin.OnMenuEventListener menuListener, 80 NotificationRoundnessManager notificationRoundnessManager) { 81 super(callback, resources, viewConfiguration, falsingManager, featureFlags); 82 mNotificationRoundnessManager = notificationRoundnessManager; 83 mMenuListener = menuListener; 84 mCallback = callback; 85 mFalsingCheck = () -> resetExposedMenuView(true /* animate */, true /* force */); 86 } 87 getTranslatingParentView()88 public View getTranslatingParentView() { 89 return mTranslatingParentView; 90 } 91 clearTranslatingParentView()92 public void clearTranslatingParentView() { setTranslatingParentView(null); } 93 94 @VisibleForTesting setTranslatingParentView(View view)95 protected void setTranslatingParentView(View view) { mTranslatingParentView = view; } 96 setExposedMenuView(View view)97 public void setExposedMenuView(View view) { 98 mMenuExposedView = view; 99 } 100 clearExposedMenuView()101 public void clearExposedMenuView() { setExposedMenuView(null); } 102 clearCurrentMenuRow()103 public void clearCurrentMenuRow() { setCurrentMenuRow(null); } 104 getExposedMenuView()105 public View getExposedMenuView() { 106 return mMenuExposedView; 107 } 108 109 @VisibleForTesting setCurrentMenuRow(NotificationMenuRowPlugin menuRow)110 void setCurrentMenuRow(NotificationMenuRowPlugin menuRow) { 111 mCurrMenuRowRef = menuRow != null ? new WeakReference<>(menuRow) : null; 112 } 113 getCurrentMenuRow()114 public NotificationMenuRowPlugin getCurrentMenuRow() { 115 if (mCurrMenuRowRef == null) { 116 return null; 117 } 118 return mCurrMenuRowRef.get(); 119 } 120 121 @VisibleForTesting getHandler()122 protected Handler getHandler() { return mHandler; } 123 124 @VisibleForTesting getFalsingCheck()125 protected Runnable getFalsingCheck() { 126 return mFalsingCheck; 127 } 128 setIsExpanded(boolean isExpanded)129 public void setIsExpanded(boolean isExpanded) { 130 mIsExpanded = isExpanded; 131 } 132 133 @Override onChildSnappedBack(View animView, float targetLeft)134 protected void onChildSnappedBack(View animView, float targetLeft) { 135 super.onChildSnappedBack(animView, targetLeft); 136 137 final NotificationMenuRowPlugin menuRow = getCurrentMenuRow(); 138 if (menuRow != null && targetLeft == 0) { 139 menuRow.resetMenu(); 140 clearCurrentMenuRow(); 141 } 142 143 InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_SWIPE); 144 } 145 146 @Override onDownUpdate(View currView, MotionEvent ev)147 public void onDownUpdate(View currView, MotionEvent ev) { 148 mTranslatingParentView = currView; 149 NotificationMenuRowPlugin menuRow = getCurrentMenuRow(); 150 if (menuRow != null) { 151 menuRow.onTouchStart(); 152 } 153 clearCurrentMenuRow(); 154 getHandler().removeCallbacks(getFalsingCheck()); 155 156 // Slide back any notifications that might be showing a menu 157 resetExposedMenuView(true /* animate */, false /* force */); 158 159 if (currView instanceof SwipeableView) { 160 initializeRow((SwipeableView) currView); 161 } 162 } 163 164 @VisibleForTesting initializeRow(SwipeableView row)165 protected void initializeRow(SwipeableView row) { 166 if (row.hasFinishedInitialization()) { 167 final NotificationMenuRowPlugin menuRow = row.createMenu(); 168 setCurrentMenuRow(menuRow); 169 if (menuRow != null) { 170 menuRow.setMenuClickListener(mMenuListener); 171 menuRow.onTouchStart(); 172 } 173 } 174 } 175 swipedEnoughToShowMenu(NotificationMenuRowPlugin menuRow)176 private boolean swipedEnoughToShowMenu(NotificationMenuRowPlugin menuRow) { 177 return !swipedFarEnough() && menuRow.isSwipedEnoughToShowMenu(); 178 } 179 180 @Override onMoveUpdate(View view, MotionEvent ev, float translation, float delta)181 public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) { 182 getHandler().removeCallbacks(getFalsingCheck()); 183 NotificationMenuRowPlugin menuRow = getCurrentMenuRow(); 184 if (menuRow != null) { 185 menuRow.onTouchMove(delta); 186 } 187 } 188 189 @Override handleUpEvent(MotionEvent ev, View animView, float velocity, float translation)190 public boolean handleUpEvent(MotionEvent ev, View animView, float velocity, 191 float translation) { 192 NotificationMenuRowPlugin menuRow = getCurrentMenuRow(); 193 if (menuRow != null) { 194 menuRow.onTouchEnd(); 195 handleMenuRowSwipe(ev, animView, velocity, menuRow); 196 return true; 197 } 198 return false; 199 } 200 201 @Override updateSwipeProgressAlpha(View animView, float alpha)202 protected void updateSwipeProgressAlpha(View animView, float alpha) { 203 if (animView instanceof ExpandableNotificationRow) { 204 ((ExpandableNotificationRow) animView).setContentAlpha(alpha); 205 } 206 } 207 208 @VisibleForTesting handleMenuRowSwipe(MotionEvent ev, View animView, float velocity, NotificationMenuRowPlugin menuRow)209 protected void handleMenuRowSwipe(MotionEvent ev, View animView, float velocity, 210 NotificationMenuRowPlugin menuRow) { 211 if (!menuRow.shouldShowMenu()) { 212 // If the menu should not be shown, then there is no need to check if the a swipe 213 // should result in a snapping to the menu. As a result, just check if the swipe 214 // was enough to dismiss the notification. 215 if (isDismissGesture(ev)) { 216 dismiss(animView, velocity); 217 } else { 218 snapClosed(animView, velocity); 219 menuRow.onSnapClosed(); 220 } 221 return; 222 } 223 224 if (menuRow.isSnappedAndOnSameSide()) { 225 // Menu was snapped to previously and we're on the same side 226 handleSwipeFromOpenState(ev, animView, velocity, menuRow); 227 } else { 228 // Menu has not been snapped, or was snapped previously but is now on 229 // the opposite side. 230 handleSwipeFromClosedState(ev, animView, velocity, menuRow); 231 } 232 } 233 handleSwipeFromClosedState(MotionEvent ev, View animView, float velocity, NotificationMenuRowPlugin menuRow)234 private void handleSwipeFromClosedState(MotionEvent ev, View animView, float velocity, 235 NotificationMenuRowPlugin menuRow) { 236 boolean isDismissGesture = isDismissGesture(ev); 237 final boolean gestureTowardsMenu = menuRow.isTowardsMenu(velocity); 238 final boolean gestureFastEnough = getEscapeVelocity() <= Math.abs(velocity); 239 240 final double timeForGesture = ev.getEventTime() - ev.getDownTime(); 241 final boolean showMenuForSlowOnGoing = !menuRow.canBeDismissed() 242 && timeForGesture >= SWIPE_MENU_TIMING; 243 244 boolean isNonDismissGestureTowardsMenu = gestureTowardsMenu && !isDismissGesture; 245 boolean isSlowSwipe = !gestureFastEnough || showMenuForSlowOnGoing; 246 boolean slowSwipedFarEnough = swipedEnoughToShowMenu(menuRow) && isSlowSwipe; 247 boolean isFastNonDismissGesture = 248 gestureFastEnough && !gestureTowardsMenu && !isDismissGesture; 249 boolean isAbleToShowMenu = menuRow.shouldShowGutsOnSnapOpen() 250 || mIsExpanded && !mPulsing; 251 boolean isMenuRevealingGestureAwayFromMenu = slowSwipedFarEnough 252 || (isFastNonDismissGesture && isAbleToShowMenu); 253 int menuSnapTarget = menuRow.getMenuSnapTarget(); 254 boolean isNonFalseMenuRevealingGesture = 255 isMenuRevealingGestureAwayFromMenu && !isFalseGesture(); 256 if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture) 257 && menuSnapTarget != 0) { 258 // Menu has not been snapped to previously and this is menu revealing gesture 259 snapOpen(animView, menuSnapTarget, velocity); 260 menuRow.onSnapOpen(); 261 } else if (isDismissGesture && !gestureTowardsMenu) { 262 dismiss(animView, velocity); 263 menuRow.onDismiss(); 264 } else { 265 snapClosed(animView, velocity); 266 menuRow.onSnapClosed(); 267 } 268 } 269 handleSwipeFromOpenState(MotionEvent ev, View animView, float velocity, NotificationMenuRowPlugin menuRow)270 private void handleSwipeFromOpenState(MotionEvent ev, View animView, float velocity, 271 NotificationMenuRowPlugin menuRow) { 272 boolean isDismissGesture = isDismissGesture(ev); 273 274 final boolean withinSnapMenuThreshold = 275 menuRow.isWithinSnapMenuThreshold(); 276 277 if (withinSnapMenuThreshold && !isDismissGesture) { 278 // Haven't moved enough to unsnap from the menu 279 menuRow.onSnapOpen(); 280 snapOpen(animView, menuRow.getMenuSnapTarget(), velocity); 281 } else if (isDismissGesture && !menuRow.shouldSnapBack()) { 282 // Only dismiss if we're not moving towards the menu 283 dismiss(animView, velocity); 284 menuRow.onDismiss(); 285 } else { 286 snapClosed(animView, velocity); 287 menuRow.onSnapClosed(); 288 } 289 } 290 291 @Override onInterceptTouchEvent(MotionEvent ev)292 public boolean onInterceptTouchEvent(MotionEvent ev) { 293 final boolean previousIsSwiping = isSwiping(); 294 boolean ret = super.onInterceptTouchEvent(ev); 295 final View swipedView = getSwipedView(); 296 if (!previousIsSwiping && swipedView != null) { 297 InteractionJankMonitor.getInstance().begin(swipedView, 298 CUJ_NOTIFICATION_SHADE_ROW_SWIPE); 299 } 300 return ret; 301 } 302 onDismissChildWithAnimationFinished()303 protected void onDismissChildWithAnimationFinished() { 304 InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_SWIPE); 305 } 306 307 @Override dismissChild(final View view, float velocity, boolean useAccelerateInterpolator)308 public void dismissChild(final View view, float velocity, 309 boolean useAccelerateInterpolator) { 310 superDismissChild(view, velocity, useAccelerateInterpolator); 311 if (mCallback.shouldDismissQuickly()) { 312 // We don't want to quick-dismiss when it's a heads up as this might lead to closing 313 // of the panel early. 314 mCallback.handleChildViewDismissed(view); 315 } 316 mCallback.onDismiss(); 317 handleMenuCoveredOrDismissed(); 318 } 319 320 @Override prepareDismissAnimation(View view, Animator anim)321 protected void prepareDismissAnimation(View view, Animator anim) { 322 super.prepareDismissAnimation(view, anim); 323 324 if (view instanceof ExpandableNotificationRow 325 && mNotificationRoundnessManager.isClearAllInProgress()) { 326 ExpandableNotificationRow row = (ExpandableNotificationRow) view; 327 anim.addListener(new AnimatorListenerAdapter() { 328 @Override 329 public void onAnimationStart(Animator animation) { 330 row.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, SWIPE_DISMISS); 331 } 332 333 @Override 334 public void onAnimationCancel(Animator animation) { 335 row.requestRoundnessReset(SWIPE_DISMISS); 336 } 337 338 @Override 339 public void onAnimationEnd(Animator animation) { 340 row.requestRoundnessReset(SWIPE_DISMISS); 341 } 342 }); 343 } 344 } 345 346 @VisibleForTesting superDismissChild(final View view, float velocity, boolean useAccelerateInterpolator)347 protected void superDismissChild(final View view, float velocity, boolean useAccelerateInterpolator) { 348 super.dismissChild(view, velocity, useAccelerateInterpolator); 349 } 350 351 @VisibleForTesting superSnapChild(final View animView, final float targetLeft, float velocity)352 protected void superSnapChild(final View animView, final float targetLeft, float velocity) { 353 super.snapChild(animView, targetLeft, velocity); 354 } 355 356 @Override snapChild(final View animView, final float targetLeft, float velocity)357 protected void snapChild(final View animView, final float targetLeft, float velocity) { 358 if (animView instanceof SwipeableView) { 359 // only perform the snapback animation on views that are swipeable inside the shade. 360 superSnapChild(animView, targetLeft, velocity); 361 } 362 363 mCallback.onDragCancelled(animView); 364 if (targetLeft == 0) { 365 handleMenuCoveredOrDismissed(); 366 } 367 } 368 369 @Override snooze(StatusBarNotification sbn, SnoozeOption snoozeOption)370 public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) { 371 mCallback.onSnooze(sbn, snoozeOption); 372 } 373 374 @VisibleForTesting handleMenuCoveredOrDismissed()375 protected void handleMenuCoveredOrDismissed() { 376 View exposedMenuView = getExposedMenuView(); 377 if (exposedMenuView != null && exposedMenuView == mTranslatingParentView) { 378 clearExposedMenuView(); 379 } 380 } 381 382 @Override 383 @VisibleForTesting getViewTranslationAnimator(View view, float target, ValueAnimator.AnimatorUpdateListener listener)384 protected Animator getViewTranslationAnimator(View view, float target, 385 ValueAnimator.AnimatorUpdateListener listener) { 386 return super.getViewTranslationAnimator(view, target, listener); 387 } 388 389 @Override 390 @VisibleForTesting createTranslationAnimation(View view, float newPos, ValueAnimator.AnimatorUpdateListener listener)391 protected Animator createTranslationAnimation(View view, float newPos, 392 ValueAnimator.AnimatorUpdateListener listener) { 393 return super.createTranslationAnimation(view, newPos, listener); 394 } 395 396 @Override getTotalTranslationLength(View animView)397 protected float getTotalTranslationLength(View animView) { 398 return mCallback.getTotalTranslationLength(animView); 399 } 400 401 @Override setTranslation(View v, float translate)402 public void setTranslation(View v, float translate) { 403 if (v instanceof SwipeableView) { 404 ((SwipeableView) v).setTranslation(translate); 405 } 406 } 407 408 @Override getTranslation(View v)409 public float getTranslation(View v) { 410 if (v instanceof SwipeableView) { 411 return ((SwipeableView) v).getTranslation(); 412 } 413 else { 414 return 0f; 415 } 416 } 417 418 @Override 419 @VisibleForTesting swipedFastEnough()420 protected boolean swipedFastEnough() { 421 return super.swipedFastEnough(); 422 } 423 424 @Override 425 @VisibleForTesting swipedFarEnough()426 protected boolean swipedFarEnough() { 427 return super.swipedFarEnough(); 428 } 429 430 @Override dismiss(View animView, float velocity)431 public void dismiss(View animView, float velocity) { 432 dismissChild(animView, velocity, 433 !swipedFastEnough() /* useAccelerateInterpolator */); 434 } 435 436 @Override snapOpen(View animView, int targetLeft, float velocity)437 public void snapOpen(View animView, int targetLeft, float velocity) { 438 snapChild(animView, targetLeft, velocity); 439 } 440 441 @VisibleForTesting snapClosed(View animView, float velocity)442 protected void snapClosed(View animView, float velocity) { 443 snapChild(animView, 0, velocity); 444 } 445 446 @Override 447 @VisibleForTesting getEscapeVelocity()448 protected float getEscapeVelocity() { 449 return super.getEscapeVelocity(); 450 } 451 452 @Override getMinDismissVelocity()453 public float getMinDismissVelocity() { 454 return getEscapeVelocity(); 455 } 456 onMenuShown(View animView)457 public void onMenuShown(View animView) { 458 setExposedMenuView(getTranslatingParentView()); 459 mCallback.onDragCancelled(animView); 460 Handler handler = getHandler(); 461 462 // If we're on the lockscreen we want to false this. 463 if (mCallback.isAntiFalsingNeeded()) { 464 handler.removeCallbacks(getFalsingCheck()); 465 handler.postDelayed(getFalsingCheck(), COVER_MENU_DELAY); 466 } 467 } 468 469 @VisibleForTesting shouldResetMenu(boolean force)470 protected boolean shouldResetMenu(boolean force) { 471 if (mMenuExposedView == null 472 || (!force && mMenuExposedView == mTranslatingParentView)) { 473 // If no menu is showing or it's showing for this view we do nothing. 474 return false; 475 } 476 return true; 477 } 478 resetExposedMenuView(boolean animate, boolean force)479 public void resetExposedMenuView(boolean animate, boolean force) { 480 if (!shouldResetMenu(force)) { 481 return; 482 } 483 final View prevMenuExposedView = getExposedMenuView(); 484 if (animate) { 485 Animator anim = getViewTranslationAnimator(prevMenuExposedView, 486 0 /* leftTarget */, null /* updateListener */); 487 if (anim != null) { 488 anim.start(); 489 } 490 } else if (prevMenuExposedView instanceof SwipeableView) { 491 SwipeableView row = (SwipeableView) prevMenuExposedView; 492 if (!row.isRemoved()) { 493 row.resetTranslation(); 494 } 495 } 496 clearExposedMenuView(); 497 } 498 isTouchInView(MotionEvent ev, View view)499 public static boolean isTouchInView(MotionEvent ev, View view) { 500 if (view == null) { 501 return false; 502 } 503 final int height = (view instanceof ExpandableView) 504 ? ((ExpandableView) view).getActualHeight() 505 : view.getHeight(); 506 final int rx = (int) ev.getRawX(); 507 final int ry = (int) ev.getRawY(); 508 int[] temp = new int[2]; 509 view.getLocationOnScreen(temp); 510 final int x = temp[0]; 511 final int y = temp[1]; 512 Rect rect = new Rect(x, y, x + view.getWidth(), y + height); 513 boolean ret = rect.contains(rx, ry); 514 return ret; 515 } 516 setPulsing(boolean pulsing)517 public void setPulsing(boolean pulsing) { 518 mPulsing = pulsing; 519 } 520 521 public interface NotificationCallback extends SwipeHelper.Callback{ 522 /** 523 * @return if the view should be dismissed as soon as the touch is released, otherwise its 524 * removed when the animation finishes. 525 */ shouldDismissQuickly()526 boolean shouldDismissQuickly(); 527 handleChildViewDismissed(View view)528 void handleChildViewDismissed(View view); 529 onSnooze(StatusBarNotification sbn, SnoozeOption snoozeOption)530 void onSnooze(StatusBarNotification sbn, SnoozeOption snoozeOption); 531 onDismiss()532 void onDismiss(); 533 534 /** 535 * Get the total translation length where we want to swipe to when dismissing the view. By 536 * default this is the size of the view, but can also be larger. 537 * @param animView the view to ask about 538 */ getTotalTranslationLength(View animView)539 float getTotalTranslationLength(View animView); 540 } 541 542 static class Builder { 543 private final Resources mResources; 544 private final ViewConfiguration mViewConfiguration; 545 private final FalsingManager mFalsingManager; 546 private final FeatureFlags mFeatureFlags; 547 private NotificationCallback mNotificationCallback; 548 private NotificationMenuRowPlugin.OnMenuEventListener mOnMenuEventListener; 549 private DumpManager mDumpManager; 550 private NotificationRoundnessManager mNotificationRoundnessManager; 551 552 @Inject Builder(@ain Resources resources, ViewConfiguration viewConfiguration, DumpManager dumpManager, FalsingManager falsingManager, FeatureFlags featureFlags, NotificationRoundnessManager notificationRoundnessManager)553 Builder(@Main Resources resources, ViewConfiguration viewConfiguration, 554 DumpManager dumpManager, 555 FalsingManager falsingManager, FeatureFlags featureFlags, 556 NotificationRoundnessManager notificationRoundnessManager) { 557 mResources = resources; 558 mViewConfiguration = viewConfiguration; 559 mDumpManager = dumpManager; 560 mFalsingManager = falsingManager; 561 mFeatureFlags = featureFlags; 562 mNotificationRoundnessManager = notificationRoundnessManager; 563 } 564 setNotificationCallback(NotificationCallback notificationCallback)565 Builder setNotificationCallback(NotificationCallback notificationCallback) { 566 mNotificationCallback = notificationCallback; 567 return this; 568 } 569 setOnMenuEventListener( NotificationMenuRowPlugin.OnMenuEventListener onMenuEventListener)570 Builder setOnMenuEventListener( 571 NotificationMenuRowPlugin.OnMenuEventListener onMenuEventListener) { 572 mOnMenuEventListener = onMenuEventListener; 573 return this; 574 } 575 build()576 NotificationSwipeHelper build() { 577 NotificationSwipeHelper swipeHelper = new NotificationSwipeHelper( 578 mResources, mViewConfiguration, mFalsingManager, 579 mFeatureFlags, mNotificationCallback, mOnMenuEventListener, 580 mNotificationRoundnessManager); 581 mDumpManager.registerDumpable(swipeHelper); 582 return swipeHelper; 583 } 584 } 585 } 586