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

Commit 03a4a0cd authored by Tony Wickham's avatar Tony Wickham
Browse files

Update overview from home transitions

For both NoButtonNavbarToOverviewTouchController and
NavBarToHomeTouchController:
- Have consistent resistance applied such that RecentsView scales
  down and translates up slightly (but not as much as from an app)
- Have consistent animation to home if you fling to that state
  rather than stay in overview. This is handled by a new class,
  OverviewToHomeAnim, which consolidates logic from NBTHTC and
  overrides some interpolators such that RecentsView doesn't fade
  out or translate downwards during the animation (it just slides
  off the screen while the home animation plays).

Also make overview actions not clickable when alpha == 0, so that
you can tap the hotseat/qsb during the transition from home to
overview.

Bug: 144170434
Change-Id: Ic291f285ff2f63c477633c48d4fadb23cf70c28a
parent 999e08f6
Loading
Loading
Loading
Loading
+7 −4
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.launcher3.uioverrides;

import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
@@ -57,7 +58,7 @@ public final class RecentsViewStateController extends
            mRecentsView.updateEmptyMessage();
            mRecentsView.resetTaskVisuals();
        }
        setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, state);
        setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, new StateAnimationConfig(), state);
        mRecentsView.setFullscreenProgress(state.getOverviewFullscreenProgress());
    }

@@ -75,17 +76,19 @@ public final class RecentsViewStateController extends
                    AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
        }

        setAlphas(builder, toState);
        setAlphas(builder, config, toState);
        builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
                toState.getOverviewFullscreenProgress(), LINEAR);
    }

    private void setAlphas(PropertySetter propertySetter, LauncherState state) {
    private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
            LauncherState state) {
        float buttonAlpha = (state.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1 : 0;
        propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
                buttonAlpha, LINEAR);
        propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
                MultiValueAlpha.VALUE, buttonAlpha, LINEAR);
                MultiValueAlpha.VALUE, buttonAlpha, config.getInterpolator(
                        ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
    }

    @Override
+11 −4
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
@@ -163,10 +164,15 @@ public class QuickstepAtomicAnimationFactory extends
            config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
            config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL);
            config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);

            if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
                config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
            } else {
                config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
            Workspace workspace = mActivity.getWorkspace();
            }

            Workspace workspace = mActivity.getWorkspace();
            // Start from a higher workspace scale, but only if we're invisible so we don't jump.
            boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
            if (isWorkspaceVisible) {
@@ -206,8 +212,10 @@ public class QuickstepAtomicAnimationFactory extends
                config.setInterpolator(ANIM_WORKSPACE_SCALE,
                        fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
                config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
                config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
            } else {
                config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
                config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);

                // Scale up the recents, if it is not coming from the side
                RecentsView overview = mActivity.getOverviewPanel();
@@ -225,7 +233,6 @@ public class QuickstepAtomicAnimationFactory extends
                    : OVERSHOOT_1_7;
            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
            config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
        } else if (fromState == HINT_STATE && toState == NORMAL) {
            config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
            if (mHintToNormalDuration == -1) {
+20 −5
Original line number Diff line number Diff line
@@ -19,13 +19,13 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;

import android.animation.ValueAnimator;
@@ -45,6 +45,7 @@ import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.testing.TestProtocol;
@@ -52,7 +53,9 @@ import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.AssistantUtilities;
import com.android.quickstep.util.OverviewToHomeAnim;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.ActivityManagerWrapper;

@@ -63,6 +66,8 @@ public class NavBarToHomeTouchController implements TouchController,
        SingleAxisSwipeDetector.Listener {

    private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3;
    // How much of the overview scrim we can remove during the transition.
    private static final float OVERVIEW_TO_HOME_SCRIM_PROGRESS = 0.5f;

    private final Launcher mLauncher;
    private final SingleAxisSwipeDetector mSwipeDetector;
@@ -156,8 +161,13 @@ public class NavBarToHomeTouchController implements TouchController,
        final PendingAnimation builder = new PendingAnimation(accuracy);
        if (mStartState.overviewUi) {
            RecentsView recentsView = mLauncher.getOverviewPanel();
            builder.setFloat(recentsView, ADJACENT_PAGE_OFFSET,
                    -mPullbackDistance / recentsView.getPageOffsetScale(), PULLBACK_INTERPOLATOR);
            AnimatorControllerWithResistance.createRecentsResistanceFromOverviewAnim(mLauncher,
                    builder);
            float endScrimAlpha = Utilities.mapRange(OVERVIEW_TO_HOME_SCRIM_PROGRESS,
                    mStartState.getOverviewScrimAlpha(mLauncher),
                    mEndState.getOverviewScrimAlpha(mLauncher));
            builder.setFloat(mLauncher.getDragLayer().getOverviewScrim(),
                    OverviewScrim.SCRIM_PROGRESS, endScrimAlpha, PULLBACK_INTERPOLATOR);
            if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
                builder.addOnFrameCallback(
                        () -> recentsView.redrawLiveTile(false /* mightNeedToRefill */));
@@ -212,8 +222,13 @@ public class NavBarToHomeTouchController implements TouchController,
                recentsView.switchToScreenshot(null,
                        () -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
            }
            if (mStartState == OVERVIEW) {
                new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState))
                        .animateWithVelocity(velocity);
            } else {
                mLauncher.getStateManager().goToState(mEndState, true,
                        () -> onSwipeInteractionCompleted(mEndState));
            }
            if (mStartState != mEndState) {
                logStateChange(mStartState.containerType, logAction);
            }
+26 −45
Original line number Diff line number Diff line
@@ -21,11 +21,8 @@ import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.graphics.PointF;
@@ -35,14 +32,15 @@ import android.view.MotionEvent;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.OverviewToHomeAnim;
import com.android.quickstep.views.RecentsView;

/**
@@ -62,10 +60,10 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo

    private boolean mDidTouchStartInNavBar;
    private boolean mReachedOverview;
    private boolean mIsOverviewRehidden;
    private boolean mIsHomeStaggeredAnimFinished;
    // The last recorded displacement before we reached overview.
    private PointF mStartDisplacement = new PointF();
    private float mStartY;
    private AnimatorPlaybackController mOverviewResistYAnim;

    // Normal to Hint animation has flag SKIP_OVERVIEW, so we update this scrim with this animator.
    private ObjectAnimator mNormalToHintOverviewScrimAnimator;
@@ -123,6 +121,7 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
                    mToState.getOverviewScrimAlpha(mLauncher));
        }
        mReachedOverview = false;
        mOverviewResistYAnim = null;
    }

    @Override
@@ -160,6 +159,9 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
        mNormalToHintOverviewScrimAnimator = null;
        mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> {
            mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
                mOverviewResistYAnim = AnimatorControllerWithResistance
                        .createRecentsResistanceFromOverviewAnim(mLauncher, null)
                        .createPlaybackController();
                mReachedOverview = true;
                maybeSwipeInteractionToOverviewComplete();
            });
@@ -173,13 +175,6 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
        }
    }

    // Used if flinging back to home after reaching overview
    private void maybeSwipeInteractionToHomeComplete() {
        if (mIsHomeStaggeredAnimFinished && mIsOverviewRehidden) {
            onSwipeInteractionCompleted(NORMAL, Touch.FLING);
        }
    }

    @Override
    protected boolean handlingOverviewAnim() {
        return mDidTouchStartInNavBar && super.handlingOverviewAnim();
@@ -193,12 +188,18 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
        if (mMotionPauseDetector.isPaused()) {
            if (!mReachedOverview) {
                mStartDisplacement.set(xDisplacement, yDisplacement);
                mStartY = event.getY();
            } else {
                mRecentsView.setTranslationX((xDisplacement - mStartDisplacement.x)
                        * OVERVIEW_MOVEMENT_FACTOR);
                float yProgress = (mStartDisplacement.y - yDisplacement) / mStartY;
                if (yProgress > 0 && mOverviewResistYAnim != null) {
                    mOverviewResistYAnim.setPlayFraction(yProgress);
                } else {
                    mRecentsView.setTranslationY((yDisplacement - mStartDisplacement.y)
                            * OVERVIEW_MOVEMENT_FACTOR);
                }
            }
            // Stay in Overview.
            return true;
        }
@@ -212,35 +213,8 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
        StateManager<LauncherState> stateManager = mLauncher.getStateManager();
        boolean goToHomeInsteadOfOverview = isFling;
        if (goToHomeInsteadOfOverview) {
            if (velocity > 0) {
                stateManager.goToState(NORMAL, true,
                        () -> onSwipeInteractionCompleted(NORMAL, Touch.FLING));
            } else {
                mIsHomeStaggeredAnimFinished = mIsOverviewRehidden = false;

                StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim(
                        mLauncher, velocity, false /* animateOverviewScrim */);
                staggeredWorkspaceAnim.addAnimatorListener(new AnimationSuccessListener() {
                    @Override
                    public void onAnimationSuccess(Animator animator) {
                        mIsHomeStaggeredAnimFinished = true;
                        maybeSwipeInteractionToHomeComplete();
                    }
                }).start();

                // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
                stateManager.cancelAnimation();
                StateAnimationConfig config = new StateAnimationConfig();
                config.duration = OVERVIEW.getTransitionDuration(mLauncher);
                config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
                AnimatorSet anim = stateManager.createAtomicAnimation(
                        stateManager.getState(), NORMAL, config);
                anim.addListener(AnimationSuccessListener.forRunnable(() -> {
                    mIsOverviewRehidden = true;
                    maybeSwipeInteractionToHomeComplete();
                }));
                anim.start();
            }
            new OverviewToHomeAnim(mLauncher, ()-> onSwipeInteractionCompleted(NORMAL, Touch.FLING))
                    .animateWithVelocity(velocity);
        }
        if (mReachedOverview) {
            float distanceDp = dpiFromPx(Math.max(
@@ -256,6 +230,13 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
                    .withEndAction(goToHomeInsteadOfOverview
                            ? null
                            : this::maybeSwipeInteractionToOverviewComplete);
            if (!goToHomeInsteadOfOverview) {
                // Return to normal properties for the overview state.
                StateAnimationConfig config = new StateAnimationConfig();
                config.duration = duration;
                LauncherState state = mLauncher.getStateManager().getState();
                mLauncher.getStateManager().createAtomicAnimation(state, state, config).start();
            }
        }
    }

+143 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.quickstep.util;

import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.util.Log;
import android.view.animation.Interpolator;

import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.quickstep.views.RecentsView;

/**
 * Runs an animation from overview to home. Currently, this animation is just a wrapper around the
 * normal state transition, in order to keep RecentsView at the same scale and translationY that
 * it started out at as it translates offscreen. It also scrolls RecentsView to page 0 and may play
 * a {@link StaggeredWorkspaceAnim} if we're starting from an upward fling.
 */
public class OverviewToHomeAnim {

    private static final String TAG = "OverviewToHomeAnim";

    // Constants to specify how to scroll RecentsView to the default page if it's not already there.
    private static final int DEFAULT_PAGE = 0;
    private static final int PER_PAGE_SCROLL_DURATION = 150;
    private static final int MAX_PAGE_SCROLL_DURATION = 750;

    private final Launcher mLauncher;
    private final Runnable mOnReachedHome;

    // Only run mOnReachedHome when both of these are true.
    private boolean mIsHomeStaggeredAnimFinished;
    private boolean mIsOverviewHidden;

    public OverviewToHomeAnim(Launcher launcher, Runnable onReachedHome) {
        mLauncher = launcher;
        mOnReachedHome = onReachedHome;
    }

    /**
     * Starts the animation. If velocity < 0 (i.e. upwards), also plays a
     * {@link StaggeredWorkspaceAnim}.
     */
    public void animateWithVelocity(float velocity) {
        StateManager<LauncherState> stateManager = mLauncher.getStateManager();
        LauncherState startState = stateManager.getState();
        if (startState != OVERVIEW) {
            Log.e(TAG, "animateFromOverviewToHome: unexpected start state " + startState);
        }

        boolean playStaggeredWorkspaceAnim = velocity < 0;
        if (playStaggeredWorkspaceAnim) {
            StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim(
                    mLauncher, velocity, false /* animateOverviewScrim */);
            staggeredWorkspaceAnim.addAnimatorListener(new AnimationSuccessListener() {
                @Override
                public void onAnimationSuccess(Animator animator) {
                    mIsHomeStaggeredAnimFinished = true;
                    maybeOverviewToHomeAnimComplete();
                }
            }).start();
        } else {
            mIsHomeStaggeredAnimFinished = true;
        }

        RecentsView recentsView = mLauncher.getOverviewPanel();
        int numPagesToScroll = recentsView.getNextPage() - DEFAULT_PAGE;
        int scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
                numPagesToScroll * PER_PAGE_SCROLL_DURATION);
        int duration = Math.max(scrollDuration, startState.getTransitionDuration(mLauncher));

        StateAnimationConfig config = new UseFirstInterpolatorStateAnimConfig();
        config.duration = duration;
        config.animFlags = playStaggeredWorkspaceAnim
                // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
                ? PLAY_ATOMIC_OVERVIEW_PEEK
                : ANIM_ALL_COMPONENTS;
        config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, DEACCEL);
        config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
        config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
        config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, INSTANT);
        AnimatorSet anim = stateManager.createAtomicAnimation(
                startState, NORMAL, config);
        anim.addListener(new AnimationSuccessListener() {
            @Override
            public void onAnimationSuccess(Animator animator) {
                mIsOverviewHidden = true;
                maybeOverviewToHomeAnimComplete();
            }
        });
        stateManager.cancelAnimation();
        anim.start();
        recentsView.snapToPage(DEFAULT_PAGE, duration);
    }

    private void maybeOverviewToHomeAnimComplete() {
        if (mIsHomeStaggeredAnimFinished && mIsOverviewHidden) {
            mOnReachedHome.run();
        }
    }

    /**
     * Wrapper around StateAnimationConfig that doesn't allow interpolators to be set if they are
     * already set. This ensures they aren't overridden before being used.
     */
    private static class UseFirstInterpolatorStateAnimConfig extends StateAnimationConfig {
        @Override
        public void setInterpolator(int animId, Interpolator interpolator) {
            if (mInterpolators[animId] == null || interpolator == null) {
                super.setInterpolator(animId, interpolator);
            }
        }
    }
}
Loading