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

Commit bf33d19c authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Remove VelocityTracker abstraction

For older hardware with noisy touchscreens, a custom version of velocity
tracker, NoisyVelocityTracker, was needed. Since the hardware keeps
improving, and with recent bugfixes in VelocityTracker, these additional
layers of abstractions are no longer needed.

Some more improvements:
- mVelocityTracker is now never null
- VelocityTracker already clears its state when receiving ACTION_DOWN,
so no need for additional object destruction / creation. Replace these
calls with VelocityTracker.clear() instead.

Test: manual interaction with notification panel (pulling down, flinging
at various points).
Bug: 113070322

Change-Id: I186a6b1c3fc91ec6c18bc0248d6e36f76c65e564
parent 5733d64c
Loading
Loading
Loading
Loading
+0 −135
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.systemui.statusbar.phone;

import android.util.Log;
import android.util.Pools;
import android.view.MotionEvent;

import java.util.ArrayDeque;
import java.util.Iterator;

/**
 * A very simple low-pass velocity filter for motion events for noisy touch screens.
 */
public class NoisyVelocityTracker implements VelocityTrackerInterface {

    private static final Pools.SynchronizedPool<NoisyVelocityTracker> sNoisyPool =
            new Pools.SynchronizedPool<>(2);

    private static final float DECAY = 0.75f;
    private static final boolean DEBUG = false;

    private final int MAX_EVENTS = 8;
    private ArrayDeque<MotionEventCopy> mEventBuf = new ArrayDeque<MotionEventCopy>(MAX_EVENTS);
    private float mVX, mVY = 0;

    private static class MotionEventCopy {
        public MotionEventCopy(float x2, float y2, long eventTime) {
            this.x = x2;
            this.y = y2;
            this.t = eventTime;
        }
        float x, y;
        long t;
    }

    public static NoisyVelocityTracker obtain() {
        NoisyVelocityTracker instance = sNoisyPool.acquire();
        return (instance != null) ? instance : new NoisyVelocityTracker();
    }

    private NoisyVelocityTracker() {
    }

    public void addMovement(MotionEvent event) {
        if (mEventBuf.size() == MAX_EVENTS) {
            mEventBuf.remove();
        }
        mEventBuf.add(new MotionEventCopy(event.getRawX(), event.getRawY(), event.getEventTime()));
    }

    public void computeCurrentVelocity(int units) {
        if (NoisyVelocityTracker.DEBUG) {
            Log.v("FlingTracker", "computing velocities for " + mEventBuf.size() + " events");
        }
        mVX = mVY = 0;
        MotionEventCopy last = null;
        int i = 0;
        float totalweight = 0f;
        float weight = 10f;
        for (final Iterator<MotionEventCopy> iter = mEventBuf.iterator();
                iter.hasNext();) {
            final MotionEventCopy event = iter.next();
            if (last != null) {
                final float dt = (float) (event.t - last.t) / units;
                final float dx = (event.x - last.x);
                final float dy = (event.y - last.y);
                if (NoisyVelocityTracker.DEBUG) {
                    Log.v("FlingTracker", String.format(
                            "   [%d] (t=%d %.1f,%.1f) dx=%.1f dy=%.1f dt=%f vx=%.1f vy=%.1f",
                            i, event.t, event.x, event.y,
                            dx, dy, dt,
                            (dx/dt),
                            (dy/dt)
                    ));
                }
                if (event.t == last.t) {
                    // Really not sure what to do with events that happened at the same time,
                    // so we'll skip subsequent events.
                    continue;
                }
                mVX += weight * dx / dt;
                mVY += weight * dy / dt;
                totalweight += weight;
                weight *= DECAY;
            }
            last = event;
            i++;
        }
        if (totalweight > 0) {
            mVX /= totalweight;
            mVY /= totalweight;
        } else {
            // so as not to contaminate the velocities with NaN
            mVX = mVY = 0;
        }

        if (NoisyVelocityTracker.DEBUG) {
            Log.v("FlingTracker", "computed: vx=" + mVX + " vy=" + mVY);
        }
    }

    public float getXVelocity() {
        if (Float.isNaN(mVX) || Float.isInfinite(mVX)) {
            mVX = 0;
        }
        return mVX;
    }

    public float getYVelocity() {
        if (Float.isNaN(mVY) || Float.isInfinite(mVX)) {
            mVY = 0;
        }
        return mVY;
    }

    public void recycle() {
        mEventBuf.clear();
        sNoisyPool.release(this);
    }
}
+15 −44
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.util.AttributeSet;
import android.util.Log;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
@@ -103,7 +104,7 @@ public abstract class PanelView extends FrameLayout {

    private ValueAnimator mHeightAnimator;
    private ObjectAnimator mPeekAnimator;
    private VelocityTrackerInterface mVelocityTracker;
    private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
    private FlingAnimationUtils mFlingAnimationUtils;
    private FlingAnimationUtils mFlingAnimationUtilsClosing;
    private FlingAnimationUtils mFlingAnimationUtilsDismissing;
@@ -226,10 +227,6 @@ public abstract class PanelView extends FrameLayout {
        mUnlockFalsingThreshold = res.getDimensionPixelSize(R.dimen.unlock_falsing_threshold);
    }

    private void trackMovement(MotionEvent event) {
        if (mVelocityTracker != null) mVelocityTracker.addMovement(event);
    }

    public void setTouchAndAnimationDisabled(boolean disabled) {
        mTouchDisabled = disabled;
        if (mTouchDisabled) {
@@ -310,10 +307,7 @@ public abstract class PanelView extends FrameLayout {
                mTouchAboveFalsingThreshold = false;
                mCollapsedAndHeadsUpOnDown = isFullyCollapsed()
                        && mHeadsUpManager.hasPinnedHeadsUp();
                if (mVelocityTracker == null) {
                    initVelocityTracker();
                }
                trackMovement(event);
                mVelocityTracker.addMovement(event);
                if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)
                        || mPeekAnimator != null) {
                    mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning)
@@ -347,7 +341,7 @@ public abstract class PanelView extends FrameLayout {
                }
                break;
            case MotionEvent.ACTION_MOVE:
                trackMovement(event);
                mVelocityTracker.addMovement(event);
                float h = y - mInitialTouchY;

                // If the panel was collapsed when touching, we only need to check for the
@@ -392,7 +386,7 @@ public abstract class PanelView extends FrameLayout {

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                trackMovement(event);
                mVelocityTracker.addMovement(event);
                endMotionEvent(event, x, y, false /* forceCancel */);
                break;
        }
@@ -458,14 +452,11 @@ public abstract class PanelView extends FrameLayout {
                || Math.abs(y - mInitialTouchY) > mTouchSlop
                || event.getActionMasked() == MotionEvent.ACTION_CANCEL
                || forceCancel) {
            float vel = 0f;
            float vectorVel = 0f;
            if (mVelocityTracker != null) {
            mVelocityTracker.computeCurrentVelocity(1000);
                vel = mVelocityTracker.getYVelocity();
                vectorVel = (float) Math.hypot(
            float vel = mVelocityTracker.getYVelocity();
            float vectorVel = (float) Math.hypot(
                    mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
            }

            boolean expand = flingExpands(vel, vectorVel, x, y)
                    || event.getActionMasked() == MotionEvent.ACTION_CANCEL
                    || forceCancel;
@@ -502,17 +493,11 @@ public abstract class PanelView extends FrameLayout {
            onTrackingStopped(expands);
        }

        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
        mVelocityTracker.clear();
        mPeekTouching = false;
    }

    protected float getCurrentExpandVelocity() {
        if (mVelocityTracker == null) {
            return 0;
        }
        mVelocityTracker.computeCurrentVelocity(1000);
        return mVelocityTracker.getYVelocity();
    }
@@ -588,8 +573,7 @@ public abstract class PanelView extends FrameLayout {
                mHasLayoutedSinceDown = false;
                mUpdateFlingOnLayout = false;
                mTouchAboveFalsingThreshold = false;
                initVelocityTracker();
                trackMovement(event);
                mVelocityTracker.addMovement(event);
                break;
            case MotionEvent.ACTION_POINTER_UP:
                final int upPointer = event.getPointerId(event.getActionIndex());
@@ -604,15 +588,12 @@ public abstract class PanelView extends FrameLayout {
            case MotionEvent.ACTION_POINTER_DOWN:
                if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
                    mMotionAborted = true;
                    if (mVelocityTracker != null) {
                        mVelocityTracker.recycle();
                        mVelocityTracker = null;
                    }
                    mVelocityTracker.clear();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                final float h = y - mInitialTouchY;
                trackMovement(event);
                mVelocityTracker.addMovement(event);
                if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
                    float hAbs = Math.abs(h);
                    if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop))
@@ -625,10 +606,7 @@ public abstract class PanelView extends FrameLayout {
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                mVelocityTracker.clear();
                break;
        }
        return false;
@@ -656,13 +634,6 @@ public abstract class PanelView extends FrameLayout {
        }
    }

    private void initVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
        }
        mVelocityTracker = VelocityTrackerFactory.obtain(getContext());
    }

    protected boolean isScrolledToBottom() {
        return true;
    }
+0 −72
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.systemui.statusbar.phone;

import android.util.Pools;
import android.view.MotionEvent;
import android.view.VelocityTracker;

/**
 * An implementation of {@link VelocityTrackerInterface} using the platform-standard
 * {@link VelocityTracker}.
 */
public class PlatformVelocityTracker implements VelocityTrackerInterface {

    private static final Pools.SynchronizedPool<PlatformVelocityTracker> sPool =
            new Pools.SynchronizedPool<>(2);

    private VelocityTracker mTracker;

    public static PlatformVelocityTracker obtain() {
        PlatformVelocityTracker tracker = sPool.acquire();
        if (tracker == null) {
            tracker = new PlatformVelocityTracker();
        }
        tracker.setTracker(VelocityTracker.obtain());
        return tracker;
    }

    public void setTracker(VelocityTracker tracker) {
        mTracker = tracker;
    }

    @Override
    public void addMovement(MotionEvent event) {
        mTracker.addMovement(event);
    }

    @Override
    public void computeCurrentVelocity(int units) {
        mTracker.computeCurrentVelocity(units);
    }

    @Override
    public float getXVelocity() {
        return mTracker.getXVelocity();
    }

    @Override
    public float getYVelocity() {
        return mTracker.getYVelocity();
    }

    @Override
    public void recycle() {
        mTracker.recycle();
        sPool.release(this);
    }
}
+0 −42
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.systemui.statusbar.phone;

import android.content.Context;

import com.android.systemui.R;

/**
 * A class to generate {@link VelocityTrackerInterface}, depending on the configuration.
 */
public class VelocityTrackerFactory {

    public static final String PLATFORM_IMPL = "platform";
    public static final String NOISY_IMPL = "noisy";

    public static VelocityTrackerInterface obtain(Context ctx) {
        String tracker = ctx.getResources().getString(R.string.velocity_tracker_impl);
        switch (tracker) {
            case NOISY_IMPL:
                return NoisyVelocityTracker.obtain();
            case PLATFORM_IMPL:
                return PlatformVelocityTracker.obtain();
            default:
                throw new IllegalStateException("Invalid tracker: " + tracker);
        }
    }
}
+0 −31
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.systemui.statusbar.phone;

import android.view.MotionEvent;

/**
 * An interface for a velocity tracker to delegate. To be implemented by different velocity tracking
 * algorithms.
 */
public interface VelocityTrackerInterface {
    public void addMovement(MotionEvent event);
    public void computeCurrentVelocity(int units);
    public float getXVelocity();
    public float getYVelocity();
    public void recycle();
}