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

Commit de0093df authored by Jon Miranda's avatar Jon Miranda
Browse files

Add ListenerView so that we can fast finish the FloatingIconView fade anim.

This change allows us to quickly end the animation if the user begins
interacting with launcher before the animation is over. This can currently
happen when the user swipes up to go home, and then quickly swipes up again
to enter overview.

Bug: 129421279
Change-Id: I88c7d55ef8ac09f999c082317de3bb3693c11466
parent 0f8c7c0e
Loading
Loading
Loading
Loading
+7 −4
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.util.AttributeSet;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import androidx.annotation.IntDef;
@@ -58,6 +59,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
            TYPE_ON_BOARD_POPUP,
            TYPE_DISCOVERY_BOUNCE,
            TYPE_SNACKBAR,
            TYPE_LISTENER,

            TYPE_TASK_MENU,
            TYPE_OPTIONS_POPUP
@@ -72,15 +74,16 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
    public static final int TYPE_ON_BOARD_POPUP = 1 << 5;
    public static final int TYPE_DISCOVERY_BOUNCE = 1 << 6;
    public static final int TYPE_SNACKBAR = 1 << 7;
    public static final int TYPE_LISTENER = 1 << 8;

    // Popups related to quickstep UI
    public static final int TYPE_TASK_MENU = 1 << 8;
    public static final int TYPE_OPTIONS_POPUP = 1 << 9;
    public static final int TYPE_TASK_MENU = 1 << 9;
    public static final int TYPE_OPTIONS_POPUP = 1 << 10;

    public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
            | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
            | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
            | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR;
            | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER;

    // Type of popups which should be kept open during launcher rebind
    public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
@@ -90,7 +93,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
    public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
            | TYPE_SNACKBAR;

    public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE;
    public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER;

    // These view all have particular operation associated with swipe down interaction.
    public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET |
+88 −43
Original line number Diff line number Diff line
@@ -70,7 +70,7 @@ import static com.android.launcher3.Utilities.mapToRange;
public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView {

    public static final float SHAPE_PROGRESS_DURATION = 0.15f;

    private static final int FADE_DURATION_MS = 200;
    private static final Rect sTmpRect = new Rect();

    private Runnable mEndRunnable;
@@ -93,10 +93,15 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
    private float mBgDrawableStartScale = 1f;
    private float mBgDrawableEndScale = 1f;

    private AnimatorSet mFadeAnimatorSet;
    private ListenerView mListenerView;

    private FloatingIconView(Context context) {
        super(context);

        mBlurSizeOutline = context.getResources().getDimensionPixelSize(
                R.dimen.blur_size_medium_outline);
        mListenerView = new ListenerView(context, null);
    }

    /**
@@ -138,6 +143,12 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
            if (mRevealAnimator == null) {
                mRevealAnimator = (ValueAnimator) FolderShape.getShape().createRevealAnimator(this,
                        mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, !isOpening);
                mRevealAnimator.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        mRevealAnimator = null;
                    }
                });
                mRevealAnimator.start();
                // We pause here so we can set the current fraction ourselves.
                mRevealAnimator.pause();
@@ -314,7 +325,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,

    @WorkerThread
    private int getOffsetForIconBounds(Drawable drawable) {
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O ||
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ||
                !(drawable instanceof AdaptiveIconDrawable)) {
            return 0;
        }
@@ -364,6 +375,18 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
        }
    }

    public void onListenerViewClosed() {
        // Fast finish here.
        if (mEndRunnable != null) {
            mEndRunnable.run();
            mEndRunnable = null;
        }
        if (mFadeAnimatorSet != null) {
            mFadeAnimatorSet.end();
            mFadeAnimatorSet = null;
        }
    }

    @Override
    public void onAnimationStart(Animator animator) {}

@@ -410,12 +433,31 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
        // We need to add it to the overlay, but keep it invisible until animation starts..
        final DragLayer dragLayer = launcher.getDragLayer();
        view.setVisibility(INVISIBLE);
        ((ViewGroup) dragLayer.getParent()).getOverlay().add(view);
        ((ViewGroup) dragLayer.getParent()).addView(view);
        dragLayer.addView(view.mListenerView);
        view.mListenerView.setListener(view::onListenerViewClosed);

        if (hideOriginal) {
        view.mEndRunnable = () -> {
            view.mEndRunnable = null;

            if (hideOriginal) {
                if (isOpening) {
                    originalView.setVisibility(VISIBLE);
                    view.finish(dragLayer);
                } else {
                    view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
                    view.mFadeAnimatorSet.start();
                }
            } else {
                view.finish(dragLayer);
            }
        };
        return view;
    }

    private AnimatorSet createFadeAnimation(View originalView, DragLayer dragLayer) {
        AnimatorSet fade = new AnimatorSet();
                fade.setDuration(200);
        fade.setDuration(FADE_DURATION_MS);
        fade.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
@@ -424,11 +466,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,

            @Override
            public void onAnimationEnd(Animator animation) {
                        ((ViewGroup) dragLayer.getParent()).getOverlay().remove(view);

                        if (view.mRevealAnimator != null) {
                            view.mRevealAnimator.end();
                        }
                finish(dragLayer);
            }
        });

@@ -450,12 +488,14 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
        } else {
            fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
        }
                fade.start();
                // TODO: Do not run fade animation until we fix b/129421279.
                fade.end();
            };

        return fade;
    }
        return view;

    private void finish(DragLayer dragLayer) {
        ((ViewGroup) dragLayer.getParent()).removeView(this);
        dragLayer.removeView(mListenerView);
        recycle();
    }

    private void recycle() {
@@ -475,10 +515,15 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
        mBackground = null;
        mClipPath = null;
        mFinalDrawableBounds.setEmpty();
        mBgDrawableBounds.setEmpty();;
        mBgDrawableBounds.setEmpty();
        if (mRevealAnimator != null) {
            mRevealAnimator.cancel();
        }
        mRevealAnimator = null;
        if (mFadeAnimatorSet != null) {
            mFadeAnimatorSet.cancel();
        }
        mFadeAnimatorSet = null;
        mListenerView.setListener(null);
    }
}
+86 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.launcher3.views;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import com.android.launcher3.AbstractFloatingView;

/**
 * An invisible AbstractFloatingView that can run a callback when it is being closed.
 */
public class ListenerView extends AbstractFloatingView {

    public Runnable mCloseListener;

    public ListenerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setVisibility(View.GONE);
    }

    public void setListener(Runnable listener) {
        mCloseListener = listener;
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mIsOpen = true;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mIsOpen = false;
    }

    @Override
    protected void handleClose(boolean animate) {
        if (mIsOpen) {
            if (mCloseListener != null) {
                mCloseListener.run();
            } else {
                if (getParent() instanceof ViewGroup) {
                    ((ViewGroup) getParent()).removeView(this);
                }
            }
        }
        mIsOpen = false;
    }

    @Override
    public void logActionCommand(int command) {
        // Users do not interact with FloatingIconView, so there is nothing to log here.
    }

    @Override
    protected boolean isOfType(int type) {
        return (type & TYPE_LISTENER) != 0;
    }

    @Override
    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            handleClose(false);
        }
        // We want other views to be able to intercept the touch so we return false here.
        return false;
    }
}