| /* |
| * Copyright (C) 2016 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.allapps; |
| |
| import android.animation.ObjectAnimator; |
| import android.view.animation.AnimationUtils; |
| import android.view.animation.Interpolator; |
| |
| import com.android.launcher3.Launcher; |
| import com.android.launcher3.R; |
| import com.android.launcher3.pageindicators.CaretDrawable; |
| |
| public class AllAppsCaretController { |
| // Determines when the caret should flip. Should be accessed via getThreshold() |
| private static final float CARET_THRESHOLD = 0.015f; |
| private static final float CARET_THRESHOLD_LAND = 0.5f; |
| // The velocity at which the caret will peak (i.e. exhibit a 90 degree bend) |
| private static final float PEAK_VELOCITY = VerticalPullDetector.RELEASE_VELOCITY_PX_MS * .7f; |
| |
| private Launcher mLauncher; |
| |
| private ObjectAnimator mCaretAnimator; |
| private CaretDrawable mCaretDrawable; |
| private float mLastCaretProgress; |
| private boolean mThresholdCrossed; |
| |
| public AllAppsCaretController(CaretDrawable caret, Launcher launcher) { |
| mLauncher = launcher; |
| mCaretDrawable = caret; |
| |
| final long caretAnimationDuration = launcher.getResources().getInteger( |
| R.integer.config_caretAnimationDuration); |
| final Interpolator caretInterpolator = AnimationUtils.loadInterpolator(launcher, |
| android.R.interpolator.fast_out_slow_in); |
| |
| // We will set values later |
| mCaretAnimator = ObjectAnimator.ofFloat(mCaretDrawable, "caretProgress", 0); |
| mCaretAnimator.setDuration(caretAnimationDuration); |
| mCaretAnimator.setInterpolator(caretInterpolator); |
| } |
| |
| /** |
| * Updates the state of the caret based on the progress of the {@link AllAppsContainerView}, as |
| * defined by the {@link AllAppsTransitionController}. Uses the container's velocity to adjust |
| * angle of caret. |
| * |
| * @param containerProgress The progress of the container in the range [0..1] |
| * @param velocity The velocity of the container |
| * @param dragging {@code true} if the container is being dragged |
| */ |
| public void updateCaret(float containerProgress, float velocity, boolean dragging) { |
| // If we're in portrait and the shift is not 0 or 1, adjust the caret based on velocity |
| if (getThreshold() < containerProgress && containerProgress < 1 - getThreshold() && |
| !mLauncher.useVerticalBarLayout()) { |
| mThresholdCrossed = true; |
| |
| // How fast are we moving as a percentage of the peak velocity? |
| final float pctOfFlingVelocity = Math.max(-1, Math.min(velocity / PEAK_VELOCITY, 1)); |
| |
| mCaretDrawable.setCaretProgress(pctOfFlingVelocity); |
| |
| // Set the last caret progress to this progress to prevent animator cancellation |
| mLastCaretProgress = pctOfFlingVelocity; |
| // Animate to neutral. This is necessary so the caret doesn't "freeze" when the |
| // container stops moving (e.g., during a drag or when the threshold is reached). |
| animateCaretToProgress(CaretDrawable.PROGRESS_CARET_NEUTRAL); |
| } else if (!dragging) { |
| // Otherwise, if we're not dragging, match the caret to the appropriate state |
| if (containerProgress <= getThreshold()) { // All Apps is up |
| animateCaretToProgress(CaretDrawable.PROGRESS_CARET_POINTING_DOWN); |
| } else if (containerProgress >= 1 - getThreshold()) { // All Apps is down |
| animateCaretToProgress(CaretDrawable.PROGRESS_CARET_POINTING_UP); |
| } |
| } |
| } |
| |
| private void animateCaretToProgress(float progress) { |
| // If the new progress is the same as the last progress we animated to, terminate early |
| if (Float.compare(mLastCaretProgress, progress) == 0) { |
| return; |
| } |
| |
| if (mCaretAnimator.isRunning()) { |
| mCaretAnimator.cancel(); // Stop the animator in its tracks |
| } |
| |
| // Update the progress and start the animation |
| mLastCaretProgress = progress; |
| mCaretAnimator.setFloatValues(progress); |
| mCaretAnimator.start(); |
| } |
| |
| private float getThreshold() { |
| // In landscape, just return the landscape threshold. |
| if (mLauncher.useVerticalBarLayout()) { |
| return CARET_THRESHOLD_LAND; |
| } |
| |
| // Before the threshold is crossed, it is reported as zero. This makes the caret immediately |
| // responsive when a drag begins. After the threshold is crossed, we return the constant |
| // value. This means the caret will start its state-based adjustment sooner. That is, we |
| // won't have to wait until the panel is completely settled to begin animation. |
| return mThresholdCrossed ? CARET_THRESHOLD : 0f; |
| } |
| |
| public void onDragStart() { |
| mThresholdCrossed = false; |
| } |
| } |