Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf3f72ab authored by Lyn Han's avatar Lyn Han Committed by Android (Google) Code Review
Browse files

Merge "Consolidate swipe state in SwipeHelper"

parents 34e6c6dc d1fad89b
Loading
Loading
Loading
Loading
+64 −47
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources;
import android.graphics.RectF;
import android.os.Handler;
@@ -78,9 +79,9 @@ public class SwipeHelper implements Gefingerpoken {

    private float mInitialTouchPos;
    private float mPerpendicularInitialTouchPos;
    private boolean mDragging;
    private boolean mIsSwiping;
    private boolean mSnappingChild;
    private View mCurrView;
    private View mTouchedView;
    private boolean mCanCurrViewBeDimissed;
    private float mDensityScale;
    private float mTranslation = 0;
@@ -95,14 +96,14 @@ public class SwipeHelper implements Gefingerpoken {

        @Override
        public void run() {
            if (mCurrView != null && !mLongPressSent) {
            if (mTouchedView != null && !mLongPressSent) {
                mLongPressSent = true;
                if (mCurrView instanceof ExpandableNotificationRow) {
                    mCurrView.getLocationOnScreen(mViewOffset);
                if (mTouchedView instanceof ExpandableNotificationRow) {
                    mTouchedView.getLocationOnScreen(mViewOffset);
                    final int x = (int) mDownLocation[0] - mViewOffset[0];
                    final int y = (int) mDownLocation[1] - mViewOffset[1];
                    mCurrView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
                    ((ExpandableNotificationRow) mCurrView).doLongClickCallback(x, y);
                    mTouchedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
                    ((ExpandableNotificationRow) mTouchedView).doLongClickCallback(x, y);
                }
            }
        }
@@ -281,10 +282,10 @@ public class SwipeHelper implements Gefingerpoken {

    @Override
    public boolean onInterceptTouchEvent(final MotionEvent ev) {
        if (mCurrView instanceof ExpandableNotificationRow) {
            NotificationMenuRowPlugin nmr = ((ExpandableNotificationRow) mCurrView).getProvider();
        if (mTouchedView instanceof ExpandableNotificationRow) {
            NotificationMenuRowPlugin nmr = ((ExpandableNotificationRow) mTouchedView).getProvider();
            if (nmr != null) {
                mMenuRowIntercepting = nmr.onInterceptTouchEvent(mCurrView, ev);
                mMenuRowIntercepting = nmr.onInterceptTouchEvent(mTouchedView, ev);
            }
        }
        final int action = ev.getAction();
@@ -292,19 +293,19 @@ public class SwipeHelper implements Gefingerpoken {
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mTouchAboveFalsingThreshold = false;
                mDragging = false;
                mIsSwiping = false;
                mSnappingChild = false;
                mLongPressSent = false;
                mVelocityTracker.clear();
                mCurrView = mCallback.getChildAtPosition(ev);
                mTouchedView = mCallback.getChildAtPosition(ev);

                if (mCurrView != null) {
                    onDownUpdate(mCurrView, ev);
                    mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
                if (mTouchedView != null) {
                    onDownUpdate(mTouchedView, ev);
                    mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mTouchedView);
                    mVelocityTracker.addMovement(ev);
                    mInitialTouchPos = getPos(ev);
                    mPerpendicularInitialTouchPos = getPerpendicularPos(ev);
                    mTranslation = getTranslation(mCurrView);
                    mTranslation = getTranslation(mTouchedView);
                    mDownLocation[0] = ev.getRawX();
                    mDownLocation[1] = ev.getRawY();
                    mHandler.postDelayed(mPerformLongPress, mLongPressTimeout);
@@ -312,7 +313,7 @@ public class SwipeHelper implements Gefingerpoken {
                break;

            case MotionEvent.ACTION_MOVE:
                if (mCurrView != null && !mLongPressSent) {
                if (mTouchedView != null && !mLongPressSent) {
                    mVelocityTracker.addMovement(ev);
                    float pos = getPos(ev);
                    float perpendicularPos = getPerpendicularPos(ev);
@@ -325,11 +326,11 @@ public class SwipeHelper implements Gefingerpoken {
                            : mPagingTouchSlop;
                    if (Math.abs(delta) > pagingTouchSlop
                            && Math.abs(delta) > Math.abs(deltaPerpendicular)) {
                        if (mCallback.canChildBeDragged(mCurrView)) {
                            mCallback.onBeginDrag(mCurrView);
                            mDragging = true;
                        if (mCallback.canChildBeDragged(mTouchedView)) {
                            mCallback.onBeginDrag(mTouchedView);
                            mIsSwiping = true;
                            mInitialTouchPos = getPos(ev);
                            mTranslation = getTranslation(mCurrView);
                            mTranslation = getTranslation(mTouchedView);
                        }
                        cancelLongPress();
                    } else if (ev.getClassification() == MotionEvent.CLASSIFICATION_DEEP_PRESS
@@ -343,16 +344,16 @@ public class SwipeHelper implements Gefingerpoken {

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                final boolean captured = (mDragging || mLongPressSent || mMenuRowIntercepting);
                mDragging = false;
                mCurrView = null;
                final boolean captured = (mIsSwiping || mLongPressSent || mMenuRowIntercepting);
                mIsSwiping = false;
                mTouchedView = null;
                mLongPressSent = false;
                mMenuRowIntercepting = false;
                cancelLongPress();
                if (captured) return true;
                break;
        }
        return mDragging || mLongPressSent || mMenuRowIntercepting;
        return mIsSwiping || mLongPressSent || mMenuRowIntercepting;
    }

    /**
@@ -451,6 +452,7 @@ public class SwipeHelper implements Gefingerpoken {
                }
                if (!mCancelled || wasRemoved) {
                    mCallback.onChildDismissed(animView);
                    resetSwipeState();
                }
                if (endAction != null) {
                    endAction.run();
@@ -501,6 +503,7 @@ public class SwipeHelper implements Gefingerpoken {
                    updateSwipeProgressFromOffset(animView, canBeDismissed);
                    onChildSnappedBack(animView, targetLeft);
                    mCallback.onChildSnappedBack(animView, targetLeft);
                    resetSwipeState();
                }
            }
        });
@@ -563,7 +566,7 @@ public class SwipeHelper implements Gefingerpoken {
     * @param targetLeft the target to snap to.
     */
    public void snapChildIfNeeded(final View view, boolean animate, float targetLeft) {
        if ((mDragging && mCurrView == view) || mSnappingChild) {
        if ((mIsSwiping && mTouchedView == view) || mSnappingChild) {
            return;
        }
        boolean needToSnap = false;
@@ -589,7 +592,7 @@ public class SwipeHelper implements Gefingerpoken {
            return true;
        }

        if (!mDragging && !mMenuRowIntercepting) {
        if (!mIsSwiping && !mMenuRowIntercepting) {
            if (mCallback.getChildAtPosition(ev) != null) {

                // We are dragging directly over a card, make sure that we also catch the gesture
@@ -610,7 +613,7 @@ public class SwipeHelper implements Gefingerpoken {
        switch (action) {
            case MotionEvent.ACTION_OUTSIDE:
            case MotionEvent.ACTION_MOVE:
                if (mCurrView != null) {
                if (mTouchedView != null) {
                    float delta = getPos(ev) - mInitialTouchPos;
                    float absDelta = Math.abs(delta);
                    if (absDelta >= getFalsingThreshold()) {
@@ -618,9 +621,10 @@ public class SwipeHelper implements Gefingerpoken {
                    }
                    // don't let items that can't be dismissed be dragged more than
                    // maxScrollDistance
                    if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(mCurrView,
                    if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(
                            mTouchedView,
                            delta > 0)) {
                        float size = getSize(mCurrView);
                        float size = getSize(mTouchedView);
                        float maxScrollDistance = MAX_SCROLL_SIZE_FRACTION * size;
                        if (absDelta >= size) {
                            delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
@@ -636,32 +640,30 @@ public class SwipeHelper implements Gefingerpoken {
                        }
                    }

                    setTranslation(mCurrView, mTranslation + delta);
                    updateSwipeProgressFromOffset(mCurrView, mCanCurrViewBeDimissed);
                    onMoveUpdate(mCurrView, ev, mTranslation + delta, delta);
                    setTranslation(mTouchedView, mTranslation + delta);
                    updateSwipeProgressFromOffset(mTouchedView, mCanCurrViewBeDimissed);
                    onMoveUpdate(mTouchedView, ev, mTranslation + delta, delta);
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mCurrView == null) {
                if (mTouchedView == null) {
                    break;
                }
                mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, getMaxVelocity());
                float velocity = getVelocity(mVelocityTracker);

                if (!handleUpEvent(ev, mCurrView, velocity, getTranslation(mCurrView))) {
                if (!handleUpEvent(ev, mTouchedView, velocity, getTranslation(mTouchedView))) {
                    if (isDismissGesture(ev)) {
                        // flingadingy
                        dismissChild(mCurrView, velocity,
                        dismissChild(mTouchedView, velocity,
                                !swipedFastEnough() /* useAccelerateInterpolator */);
                    } else {
                        // snappity
                        mCallback.onDragCancelled(mCurrView);
                        snapChild(mCurrView, 0 /* leftTarget */, velocity);
                        mCallback.onDragCancelled(mTouchedView);
                        snapChild(mTouchedView, 0 /* leftTarget */, velocity);
                    }
                    mCurrView = null;
                    mTouchedView = null;
                }
                mDragging = false;
                mIsSwiping = false;
                break;
        }
        return true;
@@ -689,17 +691,18 @@ public class SwipeHelper implements Gefingerpoken {
    }

    protected boolean swipedFarEnough() {
        float translation = getTranslation(mCurrView);
        float translation = getTranslation(mTouchedView);
        return DISMISS_IF_SWIPED_FAR_ENOUGH
                && Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(mCurrView);
                && Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(
                mTouchedView);
    }

    public boolean isDismissGesture(MotionEvent ev) {
        float translation = getTranslation(mCurrView);
        float translation = getTranslation(mTouchedView);
        return ev.getActionMasked() == MotionEvent.ACTION_UP
                && !mFalsingManager.isUnlockingDisabled()
                && !isFalseGesture() && (swipedFastEnough() || swipedFarEnough())
                && mCallback.canChildBeDismissedInDirection(mCurrView, translation > 0);
                && mCallback.canChildBeDismissedInDirection(mTouchedView, translation > 0);
    }

    /** Returns true if the gesture should be rejected. */
@@ -715,7 +718,7 @@ public class SwipeHelper implements Gefingerpoken {

    protected boolean swipedFastEnough() {
        float velocity = getVelocity(mVelocityTracker);
        float translation = getTranslation(mCurrView);
        float translation = getTranslation(mTouchedView);
        boolean ret = (Math.abs(velocity) > getEscapeVelocity())
                && (velocity > 0) == (translation > 0);
        return ret;
@@ -726,6 +729,20 @@ public class SwipeHelper implements Gefingerpoken {
        return false;
    }

    public boolean isSwiping() {
        return mIsSwiping;
    }

    @Nullable
    public View getSwipedView() {
        return mIsSwiping ? mTouchedView : null;
    }

    public void resetSwipeState() {
        mTouchedView = null;
        mIsSwiping = false;
    }

    public interface Callback {
        View getChildAtPosition(MotionEvent ev);

+0 −14
Original line number Diff line number Diff line
@@ -41,7 +41,6 @@ public class AmbientState {
    private static final float MAX_PULSE_HEIGHT = 100000f;

    private final SectionProvider mSectionProvider;
    private ArrayList<View> mDraggedViews = new ArrayList<>();
    private int mScrollY;
    private int mAnchorViewIndex;
    private int mAnchorViewY;
@@ -161,19 +160,6 @@ public class AmbientState {
        mAnchorViewY = anchorViewY;
    }

    /** Call when dragging begins. */
    public void onBeginDrag(View view) {
        mDraggedViews.add(view);
    }

    public void onDragFinished(View view) {
        mDraggedViews.remove(view);
    }

    public ArrayList<View> getDraggedViews() {
        return mDraggedViews;
    }

    /**
     * @param dimmed Whether we are in a dimmed state (on the lockscreen), where the backgrounds are
     *               translucent and everything is scaled back a bit.
+42 −59
Original line number Diff line number Diff line
@@ -353,21 +353,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
    private boolean mContinuousShadowUpdate;
    private boolean mContinuousBackgroundUpdate;
    private ViewTreeObserver.OnPreDrawListener mShadowUpdater
            = new ViewTreeObserver.OnPreDrawListener() {

        @Override
        public boolean onPreDraw() {
            = () -> {
                updateViewShadows();
                return true;
        }
            };
    private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> {
                updateBackground();
                return true;
            };
    private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
        @Override
        public int compare(ExpandableView view, ExpandableView otherView) {
    private Comparator<ExpandableView> mViewPositionComparator = (view, otherView) -> {
        float endY = view.getTranslationY() + view.getActualHeight();
        float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
        if (endY < otherEndY) {
@@ -378,7 +372,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
            // The two notifications end at the same location
            return 0;
        }
        }
    };
    private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() {
        @Override
@@ -415,8 +408,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
     */
    private float mBackgroundXFactor = 1f;

    private boolean mSwipingInProgress;

    private boolean mUsingLightTheme;
    private boolean mQsExpanded;
    private boolean mForwardScrollable;
@@ -2557,7 +2548,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
            if (child.getVisibility() != View.GONE
                    && !(child instanceof StackScrollerDecorView)
                    && child != mShelf
                    && !mAmbientState.getDraggedViews().contains(child)) {
                    && mSwipeHelper.getSwipedView() != child) {
                children.add(child);
            }
        }
@@ -3578,7 +3569,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
    @Override
    @ShadeViewRefactor(RefactorComponent.INPUT)
    public boolean onGenericMotionEvent(MotionEvent event) {
        if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
        if (!isScrollingEnabled()
                || !mIsExpanded
                || mSwipeHelper.isSwiping()
                || mExpandingNotification
                || mDisallowScrollingInThisMotion) {
            return false;
        }
@@ -4078,14 +4072,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        return false;
    }

    @ShadeViewRefactor(RefactorComponent.INPUT)
    void setSwipingInProgress(boolean swiping) {
        mSwipingInProgress = swiping;
        if (swiping) {
            requestDisallowInterceptTouchEvent(true);
        }
    }

    @Override
    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
    public void onWindowFocusChanged(boolean hasWindowFocus) {
@@ -4128,9 +4114,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
            mStatusBar.resetUserExpandedStates();
            clearTemporaryViews();
            clearUserLockedViews();
            ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
            if (draggedViews.size() > 0) {
                draggedViews.clear();
            if (mSwipeHelper.isSwiping()) {
                mSwipeHelper.resetSwipeState();
                updateContinuousShadowDrawing();
            }
        }
@@ -5197,17 +5182,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
            ExpandableView child = (ExpandableView) getTransientView(i);
            child.dump(fd, pw, args);
        }
        ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
        int draggedCount = draggedViews.size();
        pw.println("  Dragged Views: " + draggedCount);
        for (int i = 0; i < draggedCount; i++) {
            View view = draggedViews.get(i);
            if (view instanceof ExpandableView) {
                ExpandableView expandableView = (ExpandableView) view;
        View swipedView = mSwipeHelper.getSwipedView();
        pw.println("  Swiped view: " + swipedView);
        if (swipedView instanceof ExpandableView) {
            ExpandableView expandableView = (ExpandableView) swipedView;
            expandableView.dump(fd, pw, args);
        }
    }
    }

    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
    public boolean isFullyHidden() {
@@ -5523,13 +5504,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        mSwipedOutViews.add(v);
    }

    void addDraggedView(View view) {
        mAmbientState.onBeginDrag(view);
    void onSwipeBegin() {
        requestDisallowInterceptTouchEvent(true);
        updateFirstAndLastBackgroundViews();
        updateContinuousShadowDrawing();
        updateContinuousBackgroundDrawing();
        requestChildrenUpdate();
    }

    void removeDraggedView(View view) {
        mAmbientState.onDragFinished(view);
    void onSwipeEnd() {
        updateFirstAndLastBackgroundViews();
    }

@@ -5542,10 +5525,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        mAmbientState.setHasAlertEntries(numHeadsUp > 0);
    }

    boolean getSwipingInProgress() {
        return mSwipingInProgress;
    }

    public boolean getIsExpanded() {
        return mIsExpanded;
    }
@@ -5586,10 +5565,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        mTouchHandler = touchHandler;
    }

    boolean isSwipingInProgress() {
        return mSwipingInProgress;
    }

    boolean getCheckSnoozeLeaveBehind() {
        return mCheckForLeavebehind;
    }
@@ -5664,9 +5639,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        mSectionsManager.updateSectionBoundaries(reason);
    }

    boolean isSilkDismissEnabled() {
        return Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* enabled by default */) == 1;
    }

    void updateContinuousBackgroundDrawing() {
        if (isSilkDismissEnabled()) {
            return;
        }
        boolean continuousBackground = !mAmbientState.isFullyAwake()
                && !mAmbientState.getDraggedViews().isEmpty();
                && mSwipeHelper.isSwiping();
        if (continuousBackground != mContinuousBackgroundUpdate) {
            mContinuousBackgroundUpdate = continuousBackground;
            if (continuousBackground) {
@@ -5680,7 +5663,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
    void updateContinuousShadowDrawing() {
        boolean continuousShadowUpdate = mAnimationRunning
                || !mAmbientState.getDraggedViews().isEmpty();
                || mSwipeHelper.isSwiping();
        if (continuousShadowUpdate != mContinuousShadowUpdate) {
            if (continuousShadowUpdate) {
                getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
+8 −20
Original line number Diff line number Diff line
@@ -361,7 +361,6 @@ public class NotificationStackScrollLayoutController {

                @Override
                public void onDragCancelled(View v) {
                    mView.setSwipingInProgress(false);
                    mFalsingManager.onNotificationStopDismissing();
                }

@@ -392,14 +391,10 @@ public class NotificationStackScrollLayoutController {
                 */

                public void handleChildViewDismissed(View view) {
                    mView.setSwipingInProgress(false);
                    if (mView.getDismissAllInProgress()) {
                        return;
                    }

                    mView.removeDraggedView(view);
                    mView.updateContinuousShadowDrawing();

                    mView.onSwipeEnd();
                    if (view instanceof ExpandableNotificationRow) {
                        ExpandableNotificationRow row = (ExpandableNotificationRow) view;
                        if (row.isHeadsUp()) {
@@ -454,18 +449,12 @@ public class NotificationStackScrollLayoutController {
                @Override
                public void onBeginDrag(View v) {
                    mFalsingManager.onNotificationStartDismissing();
                    mView.setSwipingInProgress(true);
                    mView.addDraggedView(v);
                    mView.updateContinuousShadowDrawing();
                    mView.updateContinuousBackgroundDrawing();
                    mView.requestChildrenUpdate();
                    mView.onSwipeBegin();
                }

                @Override
                public void onChildSnappedBack(View animView, float targetLeft) {
                    mView.removeDraggedView(animView);
                    mView.updateContinuousShadowDrawing();
                    mView.updateContinuousBackgroundDrawing();
                    mView.onSwipeEnd();
                    if (animView instanceof ExpandableNotificationRow) {
                        ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
                        if (row.isPinned() && !canChildBeDismissed(row)
@@ -1536,12 +1525,12 @@ public class NotificationStackScrollLayoutController {

            NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
            boolean expandWantsIt = false;
            boolean swipingInProgress = mView.isSwipingInProgress();
            if (!swipingInProgress && !mView.getOnlyScrollingInThisMotion() && guts == null) {
            if (!mSwipeHelper.isSwiping()
                    && !mView.getOnlyScrollingInThisMotion() && guts == null) {
                expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev);
            }
            boolean scrollWantsIt = false;
            if (!swipingInProgress && !mView.isExpandingNotification()) {
            if (!mSwipeHelper.isSwiping() && !mView.isExpandingNotification()) {
                scrollWantsIt = mView.onInterceptTouchEventScroll(ev);
            }
            boolean swipeWantsIt = false;
@@ -1582,10 +1571,9 @@ public class NotificationStackScrollLayoutController {
                    || ev.getActionMasked() == MotionEvent.ACTION_UP;
            mView.handleEmptySpaceClick(ev);
            boolean expandWantsIt = false;
            boolean swipingInProgress = mView.getSwipingInProgress();
            boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion();
            boolean expandingNotification = mView.isExpandingNotification();
            if (mView.getIsExpanded() && !swipingInProgress && !onlyScrollingInThisMotion
            if (mView.getIsExpanded() && !mSwipeHelper.isSwiping() && !onlyScrollingInThisMotion
                    && guts == null) {
                ExpandHelper expandHelper = mView.getExpandHelper();
                if (isCancelOrUp) {
@@ -1600,7 +1588,7 @@ public class NotificationStackScrollLayoutController {
                }
            }
            boolean scrollerWantsIt = false;
            if (mView.isExpanded() && !swipingInProgress && !expandingNotification
            if (mView.isExpanded() && !mSwipeHelper.isSwiping() && !expandingNotification
                    && !mView.getDisallowScrollingInThisMotion()) {
                scrollerWantsIt = mView.onScrollTouch(ev);
            }