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

Commit b7b61dda authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Make velocity tracker used in panel dependent on config.

The velocity tracker used in PanelView can now be configured with
a boolean value. The default is to use tha platform-standard velocity
tracker, but can be overriden to use our own velocity tracker for
noisy touch screens instead.

Change-Id: I375f4e2675ddbaa003acd6f085065bb3fe59ebf4
parent e660694c
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -131,5 +131,9 @@
    <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
     card. -->
    <integer name="keyguard_max_notification_count">4</integer>

    <!-- Defines the implementation of the velocity tracker to be used for the panel expansion. Can
         be 'platform' or 'noisy' (i.e. for noisy touch screens). -->
    <string name="velocity_tracker_impl" translatable="false">platform</string>
</resources>
+135 −0
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.getX(), event.getY(), 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);
    }
}
+5 −119
Original line number Diff line number Diff line
@@ -80,123 +80,7 @@ public class PanelView extends FrameLayout {

    private TimeAnimator mTimeAnimator;
    private ObjectAnimator mPeekAnimator;
    private FlingTracker mVelocityTracker;

    /**
     * A very simple low-pass velocity filter for motion events; not nearly as sophisticated as
     * VelocityTracker but optimized for the kinds of gestures we expect to see in status bar
     * panels.
     */
    private static class FlingTracker {
        static final boolean DEBUG = false;
        final int MAX_EVENTS = 8;
        final float DECAY = 0.75f;
        ArrayDeque<MotionEventCopy> mEventBuf = new ArrayDeque<MotionEventCopy>(MAX_EVENTS);
        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;
            }
            public float x, y;
            public long t;
        }
        public FlingTracker() {
        }
        public void addMovement(MotionEvent event) {
            if (mEventBuf.size() == MAX_EVENTS) {
                mEventBuf.remove();
            }
            mEventBuf.add(new MotionEventCopy(event.getX(), event.getY(), event.getEventTime()));
        }
        public void computeCurrentVelocity(long timebase) {
            if (FlingTracker.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) / timebase;
                    final float dx = (event.x - last.x);
                    final float dy = (event.y - last.y);
                    if (FlingTracker.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.
                        if (DEBUG_NAN) {
                            Log.v("FlingTracker", "skipping simultaneous event at t=" + event.t);
                        }
                        continue;
                    }
                    mVX += weight * dx / dt;
                    mVY += weight * dy / dt;
                    totalweight += weight;
                    weight *= DECAY;
                }
                last = event;
                i++;
            }
            if (totalweight > 0) {
                mVX /= totalweight;
                mVY /= totalweight;
            } else {
                if (DEBUG_NAN) {
                    Log.v("FlingTracker", "computeCurrentVelocity warning: totalweight=0",
                            new Throwable());
                }
                // so as not to contaminate the velocities with NaN
                mVX = mVY = 0;
            }

            if (FlingTracker.DEBUG) {
                Log.v("FlingTracker", "computed: vx=" + mVX + " vy=" + mVY);
            }
        }
        public float getXVelocity() {
            if (Float.isNaN(mVX) || Float.isInfinite(mVX)) {
                if (DEBUG_NAN) {
                    Log.v("FlingTracker", "warning: vx=" + mVX);
                }
                mVX = 0;
            }
            return mVX;
        }
        public float getYVelocity() {
            if (Float.isNaN(mVY) || Float.isInfinite(mVX)) {
                if (DEBUG_NAN) {
                    Log.v("FlingTracker", "warning: vx=" + mVY);
                }
                mVY = 0;
            }
            return mVY;
        }
        public void recycle() {
            mEventBuf.clear();
        }

        static FlingTracker sTracker;
        static FlingTracker obtain() {
            if (sTracker == null) {
                sTracker = new FlingTracker();
            }
            return sTracker;
        }
    }
    private VelocityTrackerInterface mVelocityTracker;

    PanelBar mBar;

@@ -385,7 +269,9 @@ public class PanelView extends FrameLayout {

                mInitialTouchY = y;
                mInitialTouchX = x;
                if (mVelocityTracker == null) {
                    initVelocityTracker();
                }
                trackMovement(event);
                mTimeAnimator.cancel(); // end any outstanding animations
                onTrackingStarted();
@@ -569,7 +455,7 @@ public class PanelView extends FrameLayout {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
        }
        mVelocityTracker = FlingTracker.obtain();
        mVelocityTracker = VelocityTrackerFactory.obtain(getContext());
    }

    protected boolean isScrolledToBottom() {
+72 −0
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);
    }
}
+44 −0
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;

import static android.util.Pools.SynchronizedPool;

/**
 * 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);
        }
    }
}
Loading