blob: dd97177139d175b737962e76f85786696648ee18 [file] [log] [blame]
/*
* Copyright (C) 2018 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.car.notification;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.IntDef;
import android.content.Context;
import android.util.Log;
import com.android.car.notification.template.CarNotificationBaseViewHolder;
import java.lang.annotation.Retention;
/** A general animation tool kit to dismiss {@link CarNotificationBaseViewHolder} */
class DismissAnimationHelper {
private static final String TAG = "CarDismissHelper";
/**
* The weight of how much swipe distance plays on the alpha value of the view.
* A weight of 1F will make the view completely transparent if the swipe distance is larger
* than the view width.
*/
private static final float SWIPE_DISTANCE_WEIGHT_ON_ALPHA = 0.9F;
private final DismissCallback mCallBacks;
/**
* The direction of motion.
* <ol>
* <li> LEFT means swiping to the left.
* <li> RIGHT means swiping to the right.
* </ol>
*/
@Retention(SOURCE)
@IntDef({Direction.LEFT, Direction.RIGHT})
public @interface Direction {
int LEFT = 1;
int RIGHT = 2;
}
/**
* The percentage of the view holder's width a non-dismissible view holder is allow to translate
* during a swipe gesture. As gesture's delta x distance grows the view holder should translate
* asymptotically to this amount.
*/
private final float mMaxPercentageOfWidthWithResistance;
/**
* The callback indicating the supplied view has been dismissed.
*/
interface DismissCallback {
/**
* Called after animation ends and the view is considered dismissed.
*/
void onDismiss(CarNotificationBaseViewHolder viewHolder);
}
DismissAnimationHelper(Context context, DismissCallback callbacks) {
mCallBacks = callbacks;
mMaxPercentageOfWidthWithResistance =
context.getResources().getFloat(R.dimen.max_percentage_of_width_with_resistance);
}
/** Animate the dismissal of the given item. The velocityX is assumed to be 0. */
void animateDismiss(CarNotificationBaseViewHolder viewHolder,
@Direction int swipeDirection) {
animateDismiss(viewHolder, swipeDirection, 0f);
}
/** Animate the dismissal of the given item. */
void animateDismiss(
CarNotificationBaseViewHolder viewHolder,
@Direction int swipeDirection,
float velocityX) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "animateDismiss direction=" + swipeDirection + " velocityX=" + velocityX);
}
viewHolder.setIsAnimating(true);
int viewWidth = viewHolder.itemView.getWidth();
viewHolder.itemView.animate()
.translationX(swipeDirection == Direction.RIGHT ? viewWidth : -viewWidth)
.alpha(0)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mCallBacks.onDismiss(viewHolder);
}
});
}
/** Animate the restore back of the given item back to it's initial state. */
void animateRestore(CarNotificationBaseViewHolder viewHolder, float velocityX) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "animateRestore velocityX=" + velocityX);
}
viewHolder.setIsAnimating(true);
viewHolder.itemView.animate()
.translationX(0)
.alpha(1)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
viewHolder.setIsAnimating(false);
}
});
}
float calculateAlphaValue(CarNotificationBaseViewHolder viewHolder, float translateX) {
if (!viewHolder.isDismissible() || translateX == 0) {
return 1F;
}
int width = viewHolder.itemView.getWidth();
return SWIPE_DISTANCE_WEIGHT_ON_ALPHA * (1 - Math.min(Math.abs(translateX / width), 1))
+ (1 - SWIPE_DISTANCE_WEIGHT_ON_ALPHA);
}
float calculateTranslateDistance(CarNotificationBaseViewHolder viewHolder, float moveDeltaX) {
// If we can dismiss then translate the same distance the touch event moved and if delta
// x is 0 just return 0.
if (viewHolder.isDismissible() || moveDeltaX == 0) {
return moveDeltaX;
}
// Calculate possible drag resistance.
int swipeDirection = moveDeltaX > 0 ? Direction.RIGHT : Direction.LEFT;
int width = viewHolder.itemView.getWidth();
float maxSwipeDistanceWithResistance = mMaxPercentageOfWidthWithResistance * width;
if (Math.abs(moveDeltaX) >= width) {
// If deltaX is too large, constrain to
// maxScrollDistanceWithResistance.
return (swipeDirection == Direction.RIGHT)
? maxSwipeDistanceWithResistance
: -maxSwipeDistanceWithResistance;
} else {
// Otherwise, just attenuate deltaX.
return maxSwipeDistanceWithResistance
* (float) Math.sin((moveDeltaX / width) * (Math.PI / 2));
}
}
}