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