1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
/*
* Copyright (C) 2022 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 android.window;
import android.util.FloatProperty;
import com.android.internal.dynamicanimation.animation.SpringAnimation;
import com.android.internal.dynamicanimation.animation.SpringForce;
/**
* An animator that drives the predictive back progress with a spring.
*
* The back gesture's latest touch point and committal state determines the final position of
* the spring. The continuous movement of the spring is used to produce {@link BackEvent}s with
* smoothly transitioning progress values.
*
* @hide
*/
public class BackProgressAnimator {
/**
* A factor to scale the input progress by, so that it works better with the spring.
* We divide the output progress by this value before sending it to apps, so that apps
* always receive progress values in [0, 1].
*/
private static final float SCALE_FACTOR = 100f;
private final SpringAnimation mSpring;
private ProgressCallback mCallback;
private float mProgress = 0;
private BackEvent mLastBackEvent;
private boolean mStarted = false;
private void setProgress(float progress) {
mProgress = progress;
}
private float getProgress() {
return mProgress;
}
private static final FloatProperty<BackProgressAnimator> PROGRESS_PROP =
new FloatProperty<BackProgressAnimator>("progress") {
@Override
public void setValue(BackProgressAnimator animator, float value) {
animator.setProgress(value);
animator.updateProgressValue(value);
}
@Override
public Float get(BackProgressAnimator object) {
return object.getProgress();
}
};
/** A callback to be invoked when there's a progress value update from the animator. */
public interface ProgressCallback {
/** Called when there's a progress value update. */
void onProgressUpdate(BackEvent event);
}
public BackProgressAnimator() {
mSpring = new SpringAnimation(this, PROGRESS_PROP);
mSpring.setSpring(new SpringForce()
.setStiffness(SpringForce.STIFFNESS_MEDIUM)
.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY));
}
/**
* Sets a new target position for the back progress.
*
* @param event the {@link BackEvent} containing the latest target progress.
*/
public void onBackProgressed(BackEvent event) {
if (!mStarted) {
return;
}
mLastBackEvent = event;
mSpring.animateToFinalPosition(event.getProgress() * SCALE_FACTOR);
}
/**
* Starts the back progress animation.
*
* @param event the {@link BackEvent} that started the gesture.
* @param callback the back callback to invoke for the gesture. It will receive back progress
* dispatches as the progress animation updates.
*/
public void onBackStarted(BackEvent event, ProgressCallback callback) {
reset();
mLastBackEvent = event;
mCallback = callback;
mStarted = true;
}
/**
* Resets the back progress animation. This should be called when back is invoked or cancelled.
*/
public void reset() {
mSpring.animateToFinalPosition(0);
if (mSpring.canSkipToEnd()) {
mSpring.skipToEnd();
} else {
// Should never happen.
mSpring.cancel();
}
mStarted = false;
mLastBackEvent = null;
mCallback = null;
mProgress = 0;
}
private void updateProgressValue(float progress) {
if (mLastBackEvent == null || mCallback == null || !mStarted) {
return;
}
mCallback.onProgressUpdate(
new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(),
progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge(),
mLastBackEvent.getDepartingAnimationTarget()));
}
}
|