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

Commit c1c14065 authored by Jim Miller's avatar Jim Miller Committed by Android (Google) Code Review
Browse files

Merge "Fix 6398209: Lots of improvements to gesture search from navbar" into jb-dev

parents 4441a369 20830421
Loading
Loading
Loading
Loading
+125 −71
Original line number Diff line number Diff line
@@ -79,18 +79,17 @@ public class MultiWaveView extends View {
    private static final int SHOW_ANIMATION_DURATION = 200;
    private static final int SHOW_ANIMATION_DELAY = 0;
    private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
    private static final long RING_EXPAND_DURATION = 200;
    private static final float TARGET_INITIAL_POSITION_SCALE = 0.8f;

    private TimeInterpolator mChevronAnimationInterpolator = Ease.Quad.easeOut;

    private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
    private ArrayList<TargetDrawable> mChevronDrawables = new ArrayList<TargetDrawable>();
    private ArrayList<Tweener> mChevronAnimations = new ArrayList<Tweener>();
    private ArrayList<Tweener> mTargetAnimations = new ArrayList<Tweener>();
    private AnimationBundle mChevronAnimations = new AnimationBundle();
    private AnimationBundle mTargetAnimations = new AnimationBundle();
    private AnimationBundle mHandleAnimations = new AnimationBundle();
    private ArrayList<String> mTargetDescriptions;
    private ArrayList<String> mDirectionDescriptions;
    private Tweener mHandleAnimation;
    private OnTriggerListener mOnTriggerListener;
    private TargetDrawable mHandleDrawable;
    private TargetDrawable mOuterRing;
@@ -114,6 +113,33 @@ public class MultiWaveView extends View {
    private boolean mDragging;
    private int mNewTargetResources;

    private class AnimationBundle extends ArrayList<Tweener> {
        private static final long serialVersionUID = 0xA84D78726F127468L;
        private boolean mSuspended;

        public void start() {
            if (mSuspended) return; // ignore attempts to start animations
            final int count = size();
            for (int i = 0; i < count; i++) {
                Tweener anim = get(i);
                anim.animator.start();
            }
        }

        public void stop() {
            final int count = size();
            for (int i = 0; i < count; i++) {
                Tweener anim = get(i);
                anim.animator.end();
            }
            clear();
        }

        public void setSuspended(boolean suspend) {
            mSuspended = suspend;
        }
    };

    private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
        public void onAnimationEnd(Animator animator) {
            switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
@@ -239,6 +265,7 @@ public class MultiWaveView extends View {

        a.recycle();
        setVibrateEnabled(mVibrationDuration > 0);
        assignDefaultsIfNeeded();
    }

    private void dump() {
@@ -254,6 +281,21 @@ public class MultiWaveView extends View {
        Log.v(TAG, "VerticalOffset = " + mVerticalOffset);
    }

    public void suspendAnimations() {
        mChevronAnimations.setSuspended(true);
        mTargetAnimations.setSuspended(true);
        mHandleAnimations.setSuspended(true);
    }

    public void resumeAnimations() {
        mChevronAnimations.setSuspended(false);
        mTargetAnimations.setSuspended(false);
        mHandleAnimations.setSuspended(false);
        mChevronAnimations.start();
        mTargetAnimations.start();
        mHandleAnimations.start();
    }

    @Override
    protected int getSuggestedMinimumWidth() {
        // View should be large enough to contain the background + handle and
@@ -340,6 +382,8 @@ public class MultiWaveView extends View {

        final int directionCount = mFeedbackCount > 0 ? mChevronDrawables.size()/mFeedbackCount : 0;

        mChevronAnimations.stop();

        // Add an animation for all chevron drawables.  There are mFeedbackCount drawables
        // in each direction and directionCount directions.
        for (int direction = 0; direction < directionCount; direction++) {
@@ -367,24 +411,21 @@ public class MultiWaveView extends View {
                        "onUpdate", mUpdateListener));
            }
        }
        mChevronAnimations.start();
    }

    private void stopChevronAnimation() {
        for (Tweener anim : mChevronAnimations) {
            anim.animator.end();
        }
        mChevronAnimations.clear();
        mChevronAnimations.stop();
    }

    private void stopHandleAnimation() {
        if (mHandleAnimation != null) {
            mHandleAnimation.animator.end();
            mHandleAnimation = null;
        }
        mHandleAnimations.stop();
    }

    private void deactivateTargets() {
        for (TargetDrawable target : mTargetDrawables) {
        final int count = mTargetDrawables.size();
        for (int i = 0; i < count; i++) {
            TargetDrawable target = mTargetDrawables.get(i);
            target.setState(TargetDrawable.STATE_INACTIVE);
        }
        mActiveTarget = -1;
@@ -445,14 +486,16 @@ public class MultiWaveView extends View {
        // Animate handle back to the center based on current state.
        int delay = targetHit ? RETURN_TO_HOME_DELAY : 0;
        int duration = targetHit ? 0 : RETURN_TO_HOME_DURATION;
        mHandleAnimation = Tweener.to(mHandleDrawable, duration,
        mHandleAnimations.stop();
        mHandleAnimations.add(Tweener.to(mHandleDrawable, duration,
                "ease", Ease.Quart.easeOut,
                "delay", delay,
                "alpha", 1.0f,
                "x", 0,
                "y", 0,
                "onUpdate", mUpdateListener,
                "onComplete", (mDragging && !targetHit) ? mResetListenerWithPing : mResetListener);
                "onComplete", (mDragging && !targetHit) ? mResetListenerWithPing : mResetListener));
        mHandleAnimations.start();

        setGrabbedState(OnTriggerListener.NO_HANDLE);
    }
@@ -467,9 +510,7 @@ public class MultiWaveView extends View {
    }

    private void hideTargets(boolean animate) {
        if (mTargetAnimations.size() > 0) {
            stopTargetAnimation();
        }
        mTargetAnimations.stop();
        // Note: these animations should complete at the same time so that we can swap out
        // the target assets asynchronously from the setTargetResources() call.
        mAnimatingTargets = animate;
@@ -497,12 +538,12 @@ public class MultiWaveView extends View {
                "delay", delay,
                "onUpdate", mUpdateListener,
                "onComplete", mTargetUpdateListener));

        mTargetAnimations.start();
    }

    private void showTargets(boolean animate) {
        if (mTargetAnimations.size() > 0) {
            stopTargetAnimation();
        }
        mTargetAnimations.stop();
        mAnimatingTargets = animate;
        final int delay = animate ? SHOW_ANIMATION_DELAY : 0;
        final int length = mTargetDrawables.size();
@@ -521,7 +562,7 @@ public class MultiWaveView extends View {
        }
        mOuterRing.setScaleX(0.5f);
        mOuterRing.setScaleY(0.5f);
        mTargetAnimations.add(Tweener.to(mOuterRing, animate ? RING_EXPAND_DURATION : 0,
        mTargetAnimations.add(Tweener.to(mOuterRing, animate ? SHOW_ANIMATION_DURATION : 0,
                "ease", Ease.Cubic.easeOut,
                "alpha", 1.0f,
                "scaleX", 1.0f,
@@ -529,13 +570,12 @@ public class MultiWaveView extends View {
                "delay", delay,
                "onUpdate", mUpdateListener,
                "onComplete", mTargetUpdateListener));

        mTargetAnimations.start();
    }

    private void stopTargetAnimation() {
        for (Tweener anim : mTargetAnimations) {
            anim.animator.end();
        }
        mTargetAnimations.clear();
        mTargetAnimations.stop();
    }

    private void vibrate() {
@@ -658,7 +698,6 @@ public class MultiWaveView extends View {
     *
     */
    public void ping() {
        stopChevronAnimation();
        startChevronAnimation();
    }

@@ -721,7 +760,7 @@ public class MultiWaveView extends View {
    }

    private void handleDown(MotionEvent event) {
       if (!trySwitchToFirstTouchState(event)) {
       if (!trySwitchToFirstTouchState(event.getX(), event.getY())) {
            mDragging = false;
            stopTargetAnimation();
            ping();
@@ -747,14 +786,11 @@ public class MultiWaveView extends View {
    }

    private void handleMove(MotionEvent event) {
        if (!mDragging) {
            trySwitchToFirstTouchState(event);
            return;
        }

        int activeTarget = -1;
        final int historySize = event.getHistorySize();
        final boolean singleTarget = mTargetDrawables.size() == 1;
        ArrayList<TargetDrawable> targets = mTargetDrawables;
        int ntargets = targets.size();
        final boolean singleTarget = ntargets == 1;
        float x = 0.0f;
        float y = 0.0f;
        for (int k = 0; k < historySize + 1; k++) {
@@ -768,6 +804,9 @@ public class MultiWaveView extends View {
            float limitX = tx * scale;
            float limitY = ty * scale;

            if (!mDragging) {
                trySwitchToFirstTouchState(eventX, eventY);
            } else {
                if (singleTarget) {
                    // Snap to outer ring if there's only one target
                    float snapRadius = mOuterRadius - mSnapMargin;
@@ -775,12 +814,12 @@ public class MultiWaveView extends View {
                        activeTarget = 0;
                    }
                } else {
                // If there's more than one target, snap to the closest one less than hitRadius away.
                    // For more than one target, snap to the closest one less than hitRadius away.
                    float best = Float.MAX_VALUE;
                    final float hitRadius2 = mHitRadius * mHitRadius;
                for (int i = 0; i < mTargetDrawables.size(); i++) {
                    for (int i = 0; i < ntargets; i++) {
                        // Snap to the first target in range
                    TargetDrawable target = mTargetDrawables.get(i);
                        TargetDrawable target = targets.get(i);
                        float dx = limitX - target.getX();
                        float dy = limitY - target.getY();
                        float dist2 = dx*dx + dy*dy;
@@ -790,13 +829,18 @@ public class MultiWaveView extends View {
                        }
                    }
                }
            }
            x = limitX;
            y = limitY;
        }

        if (!mDragging) {
            return;
        }

        if (activeTarget != -1) {
            switchToState(STATE_SNAP, x,y);
            TargetDrawable target = mTargetDrawables.get(activeTarget);
            TargetDrawable target = targets.get(activeTarget);
            float newX = singleTarget ? x : target.getX();
            float newY = singleTarget ? y : target.getY();
            moveHandleTo(newX, newY, false);
@@ -812,7 +856,7 @@ public class MultiWaveView extends View {
        if (mActiveTarget != activeTarget) {
            // Defocus the old target
            if (mActiveTarget != -1) {
                TargetDrawable target = mTargetDrawables.get(mActiveTarget);
                TargetDrawable target = targets.get(mActiveTarget);
                if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
                    target.setState(TargetDrawable.STATE_INACTIVE);
                    mHandleDrawable.setAlpha(1.0f);
@@ -820,7 +864,7 @@ public class MultiWaveView extends View {
            }
            // Focus the new target
            if (activeTarget != -1) {
                TargetDrawable target = mTargetDrawables.get(activeTarget);
                TargetDrawable target = targets.get(activeTarget);
                if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
                    target.setState(TargetDrawable.STATE_FOCUSED);
                    mHandleDrawable.setAlpha(0.0f);
@@ -877,9 +921,7 @@ public class MultiWaveView extends View {
        }
    }

    private boolean trySwitchToFirstTouchState(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
    private boolean trySwitchToFirstTouchState(float x, float y) {
        final float tx = x - mWaveCenterX;
        final float ty = y - mWaveCenterY;
        if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledTapRadiusSquared()) {
@@ -892,9 +934,9 @@ public class MultiWaveView extends View {
        return false;
    }

    private void assignDefaultsIfNeeded(float centerX, float centerY) {
    private void assignDefaultsIfNeeded() {
        if (mOuterRadius == 0.0f) {
            mOuterRadius = 0.5f*(float) Math.hypot(centerX, centerY);
            mOuterRadius = Math.max(mOuterRing.getWidth(), mOuterRing.getHeight())/2.0f;
        }
        if (mHitRadius == 0.0f) {
            // Use the radius of inscribed circle of the first target.
@@ -941,6 +983,7 @@ public class MultiWaveView extends View {
        super.onLayout(changed, left, top, right, bottom);
        final int width = right - left;
        final int height = bottom - top;

        // Target placement width/height. This puts the targets on the greater of the ring
        // width or the specified outer radius.
        final float placementWidth = Math.max(mOuterRing.getWidth(), 2 * mOuterRadius);
@@ -950,8 +993,6 @@ public class MultiWaveView extends View {
        float newWaveCenterY = mVerticalOffset + mVerticalInset
                + Math.max(height, + mMaxTargetHeight + placementHeight) / 2;

        assignDefaultsIfNeeded(newWaveCenterX, newWaveCenterY);

        if (mInitialLayout) {
            hideChevrons();
            hideTargets(false);
@@ -976,9 +1017,12 @@ public class MultiWaveView extends View {

    private void updateTargetPositions(float centerX, float centerY) {
        // Reposition the target drawables if the view changed.
        for (int i = 0; i < mTargetDrawables.size(); i++) {
            final TargetDrawable targetIcon = mTargetDrawables.get(i);
            double angle = -2.0f * Math.PI * i / mTargetDrawables.size();
        ArrayList<TargetDrawable> targets = mTargetDrawables;
        final int size = targets.size();
        final float alpha = (float) (-2.0f * Math.PI / size);
        for (int i = 0; i < size; i++) {
            final TargetDrawable targetIcon = targets.get(i);
            final float angle = alpha * i;
            targetIcon.setPositionX(centerX);
            targetIcon.setPositionY(centerY);
            targetIcon.setX(mOuterRadius * (float) Math.cos(angle));
@@ -987,7 +1031,10 @@ public class MultiWaveView extends View {
    }

    private void updateChevronPositions(float centerX, float centerY) {
        for (TargetDrawable target : mChevronDrawables) {
        ArrayList<TargetDrawable> chevrons = mChevronDrawables;
        final int size = chevrons.size();
        for (int i = 0; i < size; i++) {
            TargetDrawable target = chevrons.get(i);
            if (target != null) {
                target.setPositionX(centerX);
                target.setPositionY(centerY);
@@ -996,7 +1043,10 @@ public class MultiWaveView extends View {
    }

    private void hideChevrons() {
        for (TargetDrawable chevron : mChevronDrawables) {
        ArrayList<TargetDrawable> chevrons = mChevronDrawables;
        final int size = chevrons.size();
        for (int i = 0; i < size; i++) {
            TargetDrawable chevron = chevrons.get(i);
            if (chevron != null) {
                chevron.setAlpha(0.0f);
            }
@@ -1006,14 +1056,18 @@ public class MultiWaveView extends View {
    @Override
    protected void onDraw(Canvas canvas) {
        mOuterRing.draw(canvas);
        for (TargetDrawable target : mTargetDrawables) {
        final int ntargets = mTargetDrawables.size();
        for (int i = 0; i < ntargets; i++) {
            TargetDrawable target = mTargetDrawables.get(i);
            if (target != null) {
                target.draw(canvas);
            }
        }
        for (TargetDrawable target : mChevronDrawables) {
            if (target != null) {
                target.draw(canvas);
        final int nchevrons = mChevronDrawables.size();
        for (int i = 0; i < nchevrons; i++) {
            TargetDrawable chevron = mChevronDrawables.get(i);
            if (chevron != null) {
                chevron.draw(canvas);
            }
        }
        mHandleDrawable.draw(canvas);
+0 −1
Original line number Diff line number Diff line
@@ -122,7 +122,6 @@ class Tweener {
            anim.addListener(listener);
        }
        anim.addListener(mCleanupListener);
        anim.start();

        return tween;
    }
+1 −1
Original line number Diff line number Diff line
@@ -19,5 +19,5 @@
    <size android:height="@dimen/navbar_search_outerring_diameter"
        android:width="@dimen/navbar_search_outerring_diameter" />
    <solid android:color="#00000000" />
    <stroke android:color="#1affffff" android:width="2dp" />
    <stroke android:color="#40ffffff" android:width="2dp" />
</shape>
 No newline at end of file
+2 −2
Original line number Diff line number Diff line
@@ -115,10 +115,10 @@
    <dimen name="navbar_search_hit_radius">60dip</dimen>

    <!-- Diameter of outer shape drawable shown in navbar search-->
    <dimen name="navbar_search_outerring_diameter">270dp</dimen>
    <dimen name="navbar_search_outerring_diameter">340dp</dimen>

    <!-- Threshold for swipe-up gesture to activate search dialog -->
    <dimen name="navbar_search_up_threshhold">20dip</dimen>
    <dimen name="navbar_search_up_threshhold">40dip</dimen>

    <!-- Height of search panel including navigation bar height -->
    <dimen name="navbar_search_panel_height">230dip</dimen>
+23 −10
Original line number Diff line number Diff line
@@ -23,13 +23,14 @@ import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.speech.RecognizerIntent;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.widget.FrameLayout;

import com.android.internal.widget.multiwaveview.MultiWaveView;
@@ -146,6 +147,14 @@ public class SearchPanelView extends FrameLayout implements
        }
    }

    private OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
        public boolean onPreDraw() {
            getViewTreeObserver().removeOnPreDrawListener(this);
            mMultiWaveView.resumeAnimations();
            return false;
        }
    };

    public void show(final boolean show, boolean animate) {
        if (animate) {
            if (mShowing != show) {
@@ -156,17 +165,21 @@ public class SearchPanelView extends FrameLayout implements
            mShowing = show;
            onAnimationEnd(null);
        }
        postDelayed(new Runnable() {
            public void run() {
                setVisibility(show ? View.VISIBLE : View.INVISIBLE);
        if (show) {
            if (getVisibility() != View.VISIBLE) {
                setVisibility(View.VISIBLE);
                // Don't start the animation until we've created the layer, which is done
                // right before we are drawn
                mMultiWaveView.suspendAnimations();
                getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
            }
            setFocusable(true);
            setFocusableInTouchMode(true);
            requestFocus();
        } else {
            setVisibility(View.INVISIBLE);
        }
    }
        }, show ? 0 : 100);
    }

    public void hide(boolean animate) {
        if (mBar != null) {