Transforming notifications now based on the user dragging
The animation is not a canned animation anymore but base on the finger movement of the user. Bug: 19437552 Change-Id: I0f81ac2ff05a92673e3f3b9b72a5c2de238890d0
This commit is contained in:
@@ -44,6 +44,10 @@
|
||||
<item type="id" name="notification_screenshot"/>
|
||||
<item type="id" name="notification_hidden"/>
|
||||
<item type="id" name="notification_volumeui"/>
|
||||
<item type="id" name="transformation_start_x_tag"/>
|
||||
<item type="id" name="transformation_start_y_tag"/>
|
||||
<item type="id" name="transformation_start_scale_x_tag"/>
|
||||
<item type="id" name="transformation_start_scale_y_tag"/>
|
||||
|
||||
<!-- Whether the icon is from a notification for which targetSdk < L -->
|
||||
<item type="id" name="icon_is_pre_L"/>
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||
|
||||
/**
|
||||
* A helper to fade views in and out.
|
||||
@@ -44,7 +45,34 @@ public class CrossFadeHelper {
|
||||
if (view.hasOverlappingRendering()) {
|
||||
view.animate().withLayer();
|
||||
}
|
||||
}
|
||||
|
||||
public static void fadeOut(View view, float fadeOutAmount) {
|
||||
view.animate().cancel();
|
||||
if (fadeOutAmount == 1.0f) {
|
||||
view.setVisibility(View.INVISIBLE);
|
||||
} else if (view.getVisibility() == View.INVISIBLE) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
fadeOutAmount = mapToFadeDuration(fadeOutAmount);
|
||||
float alpha = Interpolators.ALPHA_OUT.getInterpolation(1.0f - fadeOutAmount);
|
||||
view.setAlpha(alpha);
|
||||
updateLayerType(view, alpha);
|
||||
}
|
||||
|
||||
private static float mapToFadeDuration(float fadeOutAmount) {
|
||||
// Assuming a linear interpolator, we can easily map it to our new duration
|
||||
float endPoint = (float) ANIMATION_DURATION_LENGTH
|
||||
/ (float) StackStateAnimator.ANIMATION_DURATION_STANDARD;
|
||||
return Math.min(fadeOutAmount / endPoint, 1.0f);
|
||||
}
|
||||
|
||||
private static void updateLayerType(View view, float alpha) {
|
||||
if (view.hasOverlappingRendering() && alpha > 0.0f && alpha < 1.0f) {
|
||||
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
} else if (view.getLayerType() == View.LAYER_TYPE_HARDWARE) {
|
||||
view.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static void fadeIn(final View view) {
|
||||
@@ -62,4 +90,15 @@ public class CrossFadeHelper {
|
||||
view.animate().withLayer();
|
||||
}
|
||||
}
|
||||
|
||||
public static void fadeIn(View view, float fadeInAmount) {
|
||||
view.animate().cancel();
|
||||
if (view.getVisibility() == View.INVISIBLE) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
fadeInAmount = mapToFadeDuration(fadeInAmount);
|
||||
float alpha = Interpolators.ALPHA_IN.getInterpolation(fadeInAmount);
|
||||
view.setAlpha(alpha);
|
||||
updateLayerType(view, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -868,6 +868,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
|
||||
public void setUserLocked(boolean userLocked) {
|
||||
mUserLocked = userLocked;
|
||||
mPrivateLayout.setUserExpanding(userLocked);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,6 +48,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
private static final int VISIBLE_TYPE_EXPANDED = 1;
|
||||
private static final int VISIBLE_TYPE_HEADSUP = 2;
|
||||
private static final int VISIBLE_TYPE_SINGLELINE = 3;
|
||||
private static final int UNDEFINED = -1;
|
||||
|
||||
private final Rect mClipBounds = new Rect();
|
||||
private final int mMinContractedHeight;
|
||||
@@ -102,6 +103,8 @@ public class NotificationContentView extends FrameLayout {
|
||||
private boolean mExpandable;
|
||||
private boolean mClipToActualHeight = true;
|
||||
private ExpandableNotificationRow mContainingNotification;
|
||||
private int mTransformationStartVisibleType;
|
||||
private boolean mUserExpanding;
|
||||
|
||||
public NotificationContentView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -349,6 +352,41 @@ public class NotificationContentView extends FrameLayout {
|
||||
invalidateOutline();
|
||||
}
|
||||
|
||||
private void updateContentTransformation() {
|
||||
int visibleType = calculateVisibleType();
|
||||
if (visibleType != mVisibleType) {
|
||||
// A new transformation starts
|
||||
mTransformationStartVisibleType = mVisibleType;
|
||||
final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
|
||||
final TransformableView hiddenView = getTransformableViewForVisibleType(
|
||||
mTransformationStartVisibleType);
|
||||
shownView.transformFrom(hiddenView, 0.0f);
|
||||
getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
|
||||
hiddenView.transformTo(shownView, 0.0f);
|
||||
mVisibleType = visibleType;
|
||||
}
|
||||
if (mTransformationStartVisibleType != UNDEFINED
|
||||
&& mVisibleType != mTransformationStartVisibleType) {
|
||||
final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
|
||||
final TransformableView hiddenView = getTransformableViewForVisibleType(
|
||||
mTransformationStartVisibleType);
|
||||
float transformationAmount = calculateTransformationAmount();
|
||||
shownView.transformFrom(hiddenView, transformationAmount);
|
||||
hiddenView.transformTo(shownView, transformationAmount);
|
||||
} else {
|
||||
updateViewVisibilities(visibleType);
|
||||
}
|
||||
}
|
||||
|
||||
private float calculateTransformationAmount() {
|
||||
int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
|
||||
int endHeight = getViewForVisibleType(mVisibleType).getHeight();
|
||||
int progress = Math.abs(mContentHeight - startHeight);
|
||||
int totalDistance = Math.abs(endHeight - startHeight);
|
||||
float amount = (float) progress / (float) totalDistance;
|
||||
return Math.min(1.0f, amount);
|
||||
}
|
||||
|
||||
public int getContentHeight() {
|
||||
return mContentHeight;
|
||||
}
|
||||
@@ -397,6 +435,10 @@ public class NotificationContentView extends FrameLayout {
|
||||
if (mContractedChild == null) {
|
||||
return;
|
||||
}
|
||||
if (mUserExpanding) {
|
||||
updateContentTransformation();
|
||||
return;
|
||||
}
|
||||
int visibleType = calculateVisibleType();
|
||||
if (visibleType != mVisibleType || force) {
|
||||
if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
|
||||
@@ -492,9 +534,21 @@ public class NotificationContentView extends FrameLayout {
|
||||
* @return one of the static enum types in this view, calculated form the current state
|
||||
*/
|
||||
private int calculateVisibleType() {
|
||||
boolean noExpandedChild = mExpandedChild == null;
|
||||
|
||||
if (mUserExpanding) {
|
||||
int expandedVisualType = getVisualTypeForHeight(
|
||||
mContainingNotification.getMaxExpandHeight());
|
||||
int collapsedVisualType = getVisualTypeForHeight(
|
||||
mContainingNotification.getMinExpandHeight());
|
||||
return mTransformationStartVisibleType == collapsedVisualType
|
||||
? expandedVisualType
|
||||
: collapsedVisualType;
|
||||
}
|
||||
int viewHeight = Math.min(mContentHeight, mContainingNotification.getIntrinsicHeight());
|
||||
return getVisualTypeForHeight(viewHeight);
|
||||
}
|
||||
|
||||
private int getVisualTypeForHeight(float viewHeight) {
|
||||
boolean noExpandedChild = mExpandedChild == null;
|
||||
if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
|
||||
return VISIBLE_TYPE_EXPANDED;
|
||||
}
|
||||
@@ -723,4 +777,15 @@ public class NotificationContentView extends FrameLayout {
|
||||
updateSingleLineView();
|
||||
}
|
||||
}
|
||||
|
||||
public void setUserExpanding(boolean userExpanding) {
|
||||
mUserExpanding = userExpanding;
|
||||
if (userExpanding) {
|
||||
mTransformationStartVisibleType = mVisibleType;
|
||||
} else {
|
||||
mTransformationStartVisibleType = UNDEFINED;
|
||||
mVisibleType = calculateVisibleType();
|
||||
updateViewVisibilities(mVisibleType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public interface TransformableView {
|
||||
|
||||
/**
|
||||
* Get the current state of a view in a transform animation
|
||||
*
|
||||
* @param fadingView which view we are interested in
|
||||
* @return the current transform state of this viewtype
|
||||
*/
|
||||
@@ -37,18 +38,37 @@ public interface TransformableView {
|
||||
|
||||
/**
|
||||
* Transform to the given view
|
||||
*
|
||||
* @param notification the view to transform to
|
||||
*/
|
||||
void transformTo(TransformableView notification, Runnable endRunnable);
|
||||
|
||||
/**
|
||||
* Transform to the given view by a specified amount.
|
||||
*
|
||||
* @param notification the view to transform to
|
||||
* @param transformationAmount how much transformation should be done
|
||||
*/
|
||||
void transformTo(TransformableView notification, float transformationAmount);
|
||||
|
||||
/**
|
||||
* Transform to this view from the given view
|
||||
*
|
||||
* @param notification the view to transform from
|
||||
*/
|
||||
void transformFrom(TransformableView notification);
|
||||
|
||||
/**
|
||||
* Transform to this view from the given view by a specified amount.
|
||||
*
|
||||
* @param notification the view to transform from
|
||||
* @param transformationAmount how much transformation should be done
|
||||
*/
|
||||
void transformFrom(TransformableView notification, float transformationAmount);
|
||||
|
||||
/**
|
||||
* Set this view to be fully visible or gone
|
||||
*
|
||||
* @param visible
|
||||
*/
|
||||
void setVisible(boolean visible);
|
||||
|
||||
@@ -16,13 +16,17 @@
|
||||
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.notification.TransformState;
|
||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
@@ -33,9 +37,9 @@ public class ViewTransformationHelper implements TransformableView {
|
||||
|
||||
private static final int TAG_CONTAINS_TRANSFORMED_VIEW = R.id.contains_transformed_view;
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
private ArrayMap<Integer, View> mTransformedViews = new ArrayMap<>();
|
||||
private ArrayMap<Integer, CustomTransformation> mCustomTransformations = new ArrayMap<>();
|
||||
private ValueAnimator mViewTransformationAnimation;
|
||||
|
||||
public void addTransformedView(int key, View transformedView) {
|
||||
mTransformedViews.put(key, transformedView);
|
||||
@@ -59,61 +63,123 @@ public class ViewTransformationHelper implements TransformableView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformTo(TransformableView notification, Runnable endRunnable) {
|
||||
Runnable runnable = endRunnable;
|
||||
public void transformTo(final TransformableView notification, final Runnable endRunnable) {
|
||||
if (mViewTransformationAnimation != null) {
|
||||
mViewTransformationAnimation.cancel();
|
||||
}
|
||||
mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
|
||||
mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
transformTo(notification, animation.getAnimatedFraction());
|
||||
}
|
||||
});
|
||||
mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
|
||||
mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
|
||||
if (endRunnable != null) {
|
||||
mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
|
||||
public boolean mCancelled;
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
endRunnable.run();
|
||||
if (!mCancelled) {
|
||||
setVisible(false);
|
||||
} else {
|
||||
abortTransformations();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mCancelled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
mViewTransformationAnimation.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformTo(TransformableView notification, float transformationAmount) {
|
||||
for (Integer viewType : mTransformedViews.keySet()) {
|
||||
TransformState ownState = getCurrentState(viewType);
|
||||
if (ownState != null) {
|
||||
CustomTransformation customTransformation = mCustomTransformations.get(viewType);
|
||||
if (customTransformation != null && customTransformation.transformTo(
|
||||
ownState, notification, runnable)) {
|
||||
ownState, notification, transformationAmount)) {
|
||||
ownState.recycle();
|
||||
runnable = null;
|
||||
continue;
|
||||
}
|
||||
TransformState otherState = notification.getCurrentState(viewType);
|
||||
if (otherState != null) {
|
||||
boolean run = ownState.transformViewTo(otherState, runnable);
|
||||
ownState.transformViewTo(otherState, transformationAmount);
|
||||
otherState.recycle();
|
||||
if (run) {
|
||||
runnable = null;
|
||||
}
|
||||
} else {
|
||||
// there's no other view available
|
||||
CrossFadeHelper.fadeOut(mTransformedViews.get(viewType), runnable);
|
||||
runnable = null;
|
||||
CrossFadeHelper.fadeOut(mTransformedViews.get(viewType), transformationAmount);
|
||||
}
|
||||
ownState.recycle();
|
||||
}
|
||||
}
|
||||
if (runnable != null) {
|
||||
// We need to post, since the visible type is only set after the transformation is
|
||||
// started
|
||||
mHandler.post(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification) {
|
||||
public void transformFrom(final TransformableView notification) {
|
||||
if (mViewTransformationAnimation != null) {
|
||||
mViewTransformationAnimation.cancel();
|
||||
}
|
||||
mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
|
||||
mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
transformFrom(notification, animation.getAnimatedFraction());
|
||||
}
|
||||
});
|
||||
mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
|
||||
public boolean mCancelled;
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!mCancelled) {
|
||||
setVisible(true);
|
||||
} else {
|
||||
abortTransformations();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mCancelled = true;
|
||||
}
|
||||
});
|
||||
mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
|
||||
mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
|
||||
mViewTransformationAnimation.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification, float transformationAmount) {
|
||||
for (Integer viewType : mTransformedViews.keySet()) {
|
||||
TransformState ownState = getCurrentState(viewType);
|
||||
if (ownState != null) {
|
||||
CustomTransformation customTransformation = mCustomTransformations.get(viewType);
|
||||
if (customTransformation != null && customTransformation.transformFrom(
|
||||
ownState, notification)) {
|
||||
ownState, notification, transformationAmount)) {
|
||||
ownState.recycle();
|
||||
continue;
|
||||
}
|
||||
TransformState otherState = notification.getCurrentState(viewType);
|
||||
if (otherState != null) {
|
||||
ownState.transformViewFrom(otherState);
|
||||
ownState.transformViewFrom(otherState, transformationAmount);
|
||||
otherState.recycle();
|
||||
} else {
|
||||
// There's no other view, lets fade us in
|
||||
// Certain views need to prepare the fade in and make sure its children are
|
||||
// completely visible. An example is the notification header.
|
||||
ownState.prepareFadeIn();
|
||||
CrossFadeHelper.fadeIn(mTransformedViews.get(viewType));
|
||||
if (transformationAmount == 0.0f) {
|
||||
ownState.prepareFadeIn();
|
||||
}
|
||||
CrossFadeHelper.fadeIn(mTransformedViews.get(viewType), transformationAmount);
|
||||
}
|
||||
ownState.recycle();
|
||||
}
|
||||
@@ -131,6 +197,16 @@ public class ViewTransformationHelper implements TransformableView {
|
||||
}
|
||||
}
|
||||
|
||||
private void abortTransformations() {
|
||||
for (Integer viewType : mTransformedViews.keySet()) {
|
||||
TransformState ownState = getCurrentState(viewType);
|
||||
if (ownState != null) {
|
||||
ownState.abortTransformation();
|
||||
ownState.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the remaining transformation views such that all views are being transformed correctly
|
||||
* @param viewRoot the root below which all elements need to be transformed
|
||||
@@ -173,22 +249,44 @@ public class ViewTransformationHelper implements TransformableView {
|
||||
}
|
||||
}
|
||||
|
||||
public interface CustomTransformation {
|
||||
public static abstract class CustomTransformation {
|
||||
/**
|
||||
* Transform a state to the given view
|
||||
* @param ownState the state to transform
|
||||
* @param notification the view to transform to
|
||||
* @param transformationAmount how much transformation should be done
|
||||
* @return whether a custom transformation is performed
|
||||
*/
|
||||
boolean transformTo(TransformState ownState, TransformableView notification,
|
||||
Runnable endRunnable);
|
||||
public abstract boolean transformTo(TransformState ownState,
|
||||
TransformableView notification,
|
||||
float transformationAmount);
|
||||
|
||||
/**
|
||||
* Transform to this state from the given view
|
||||
* @param ownState the state to transform to
|
||||
* @param notification the view to transform from
|
||||
* @param transformationAmount how much transformation should be done
|
||||
* @return whether a custom transformation is performed
|
||||
*/
|
||||
boolean transformFrom(TransformState ownState, TransformableView notification);
|
||||
public abstract boolean transformFrom(TransformState ownState,
|
||||
TransformableView notification,
|
||||
float transformationAmount);
|
||||
|
||||
/**
|
||||
* Perform a custom initialisation before transforming.
|
||||
*
|
||||
* @param ownState our own state
|
||||
* @param otherState the other state
|
||||
* @return whether a custom initialization is done
|
||||
*/
|
||||
public boolean initTransformation(TransformState ownState,
|
||||
TransformState otherState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean customTransformTarget(TransformState ownState,
|
||||
TransformState otherState) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class HeaderTransformState extends TransformState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transformViewTo(TransformState otherState, Runnable endRunnable) {
|
||||
public boolean transformViewTo(TransformState otherState, float transformationAmount) {
|
||||
// if the transforming notification has a header, we have ensured that it looks the same
|
||||
// but the expand button, so lets fade just that one and transform the work profile icon.
|
||||
if (!(mTransformedView instanceof NotificationHeaderView)) {
|
||||
@@ -62,14 +62,14 @@ public class HeaderTransformState extends TransformState {
|
||||
if (headerChild != mExpandButton) {
|
||||
headerChild.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
CrossFadeHelper.fadeOut(mExpandButton, endRunnable);
|
||||
CrossFadeHelper.fadeOut(mExpandButton, transformationAmount);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformViewFrom(TransformState otherState) {
|
||||
public void transformViewFrom(TransformState otherState, float transformationAmount) {
|
||||
// if the transforming notification has a header, we have ensured that it looks the same
|
||||
// but the expand button, so lets fade just that one and transform the work profile icon.
|
||||
if (!(mTransformedView instanceof NotificationHeaderView)) {
|
||||
@@ -85,12 +85,13 @@ public class HeaderTransformState extends TransformState {
|
||||
continue;
|
||||
}
|
||||
if (headerChild == mExpandButton) {
|
||||
CrossFadeHelper.fadeIn(mExpandButton);
|
||||
CrossFadeHelper.fadeIn(mExpandButton, transformationAmount);
|
||||
} else {
|
||||
headerChild.setVisibility(View.VISIBLE);
|
||||
if (headerChild == mWorkProfileIcon) {
|
||||
mWorkProfileState.animateViewFrom(
|
||||
((HeaderTransformState) otherState).mWorkProfileState);
|
||||
mWorkProfileState.transformViewFullyFrom(
|
||||
((HeaderTransformState) otherState).mWorkProfileState,
|
||||
transformationAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,13 +71,13 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout
|
||||
new ViewTransformationHelper.CustomTransformation() {
|
||||
@Override
|
||||
public boolean transformTo(TransformState ownState, TransformableView notification,
|
||||
Runnable endRunnable) {
|
||||
float transformationAmount) {
|
||||
// We want to transform to the same y location as the title
|
||||
TransformState otherState = notification.getCurrentState(
|
||||
TRANSFORMING_VIEW_TITLE);
|
||||
CrossFadeHelper.fadeOut(mTextView, endRunnable);
|
||||
CrossFadeHelper.fadeOut(mTextView, transformationAmount);
|
||||
if (otherState != null) {
|
||||
ownState.animateViewVerticalTo(otherState, endRunnable);
|
||||
ownState.transformViewVerticalTo(otherState, transformationAmount);
|
||||
otherState.recycle();
|
||||
}
|
||||
return true;
|
||||
@@ -85,13 +85,13 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout
|
||||
|
||||
@Override
|
||||
public boolean transformFrom(TransformState ownState,
|
||||
TransformableView notification) {
|
||||
TransformableView notification, float transformationAmount) {
|
||||
// We want to transform from the same y location as the title
|
||||
TransformState otherState = notification.getCurrentState(
|
||||
TRANSFORMING_VIEW_TITLE);
|
||||
CrossFadeHelper.fadeIn(mTextView);
|
||||
CrossFadeHelper.fadeIn(mTextView, transformationAmount);
|
||||
if (otherState != null) {
|
||||
ownState.animateViewVerticalFrom(otherState);
|
||||
ownState.transformViewVerticalFrom(otherState, transformationAmount);
|
||||
otherState.recycle();
|
||||
}
|
||||
return true;
|
||||
@@ -132,11 +132,21 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout
|
||||
mTransformationHelper.transformTo(notification, endRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformTo(TransformableView notification, float transformationAmount) {
|
||||
mTransformationHelper.transformTo(notification, transformationAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification) {
|
||||
mTransformationHelper.transformFrom(notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification, float transformationAmount) {
|
||||
mTransformationHelper.transformFrom(notification, transformationAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible) {
|
||||
setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
|
||||
|
||||
@@ -62,7 +62,7 @@ public class ImageTransformState extends TransformState {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean animateScale() {
|
||||
protected boolean transformScale() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,8 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
||||
|
||||
protected void updateTransformedTypes() {
|
||||
mTransformationHelper.reset();
|
||||
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_HEADER, mNotificationHeader);
|
||||
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_HEADER,
|
||||
mNotificationHeader);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -298,11 +299,21 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
||||
mTransformationHelper.transformTo(notification, endRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformTo(TransformableView notification, float transformationAmount) {
|
||||
mTransformationHelper.transformTo(notification, transformationAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification) {
|
||||
mTransformationHelper.transformFrom(notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification, float transformationAmount) {
|
||||
mTransformationHelper.transformFrom(notification, transformationAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible) {
|
||||
super.setVisible(visible);
|
||||
|
||||
@@ -49,76 +49,65 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
|
||||
new ViewTransformationHelper.CustomTransformation() {
|
||||
@Override
|
||||
public boolean transformTo(TransformState ownState,
|
||||
TransformableView notification, final Runnable endRunnable) {
|
||||
TransformableView notification, final float transformationAmount) {
|
||||
if (!(notification instanceof HybridNotificationView)) {
|
||||
return false;
|
||||
}
|
||||
TransformState otherState = notification.getCurrentState(
|
||||
TRANSFORMING_VIEW_TITLE);
|
||||
final View text = ownState.getTransformedView();
|
||||
CrossFadeHelper.fadeOut(text, endRunnable);
|
||||
CrossFadeHelper.fadeOut(text, transformationAmount);
|
||||
if (otherState != null) {
|
||||
int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
|
||||
int[] ownPosition = ownState.getLaidOutLocationOnScreen();
|
||||
text.animate()
|
||||
.translationY((otherStablePosition[1]
|
||||
+ otherState.getTransformedView().getHeight()
|
||||
- ownPosition[1]) * 0.33f)
|
||||
.setDuration(
|
||||
StackStateAnimator.ANIMATION_DURATION_STANDARD)
|
||||
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (endRunnable != null) {
|
||||
endRunnable.run();
|
||||
}
|
||||
TransformState.setClippingDeactivated(text,
|
||||
false);
|
||||
}
|
||||
});
|
||||
TransformState.setClippingDeactivated(text, true);
|
||||
ownState.transformViewVerticalTo(otherState, this,
|
||||
transformationAmount);
|
||||
otherState.recycle();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean customTransformTarget(TransformState ownState,
|
||||
TransformState otherState) {
|
||||
float endY = getTransformationY(ownState, otherState);
|
||||
ownState.setTransformationEndY(endY);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transformFrom(TransformState ownState,
|
||||
TransformableView notification) {
|
||||
TransformableView notification, float transformationAmount) {
|
||||
if (!(notification instanceof HybridNotificationView)) {
|
||||
return false;
|
||||
}
|
||||
TransformState otherState = notification.getCurrentState(
|
||||
TRANSFORMING_VIEW_TITLE);
|
||||
final View text = ownState.getTransformedView();
|
||||
boolean isVisible = text.getVisibility() == View.VISIBLE;
|
||||
CrossFadeHelper.fadeIn(text);
|
||||
CrossFadeHelper.fadeIn(text, transformationAmount);
|
||||
if (otherState != null) {
|
||||
int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
|
||||
int[] ownStablePosition = ownState.getLaidOutLocationOnScreen();
|
||||
if (!isVisible) {
|
||||
text.setTranslationY((otherStablePosition[1]
|
||||
+ otherState.getTransformedView().getHeight()
|
||||
- ownStablePosition[1]) * 0.33f);
|
||||
}
|
||||
text.animate()
|
||||
.translationY(0)
|
||||
.setDuration(
|
||||
StackStateAnimator.ANIMATION_DURATION_STANDARD)
|
||||
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TransformState.setClippingDeactivated(text,
|
||||
false);
|
||||
}
|
||||
});
|
||||
TransformState.setClippingDeactivated(text, true);
|
||||
ownState.transformViewVerticalFrom(otherState, this,
|
||||
transformationAmount);
|
||||
otherState.recycle();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initTransformation(TransformState ownState,
|
||||
TransformState otherState) {
|
||||
float startY = getTransformationY(ownState, otherState);
|
||||
ownState.setTransformationStartY(startY);
|
||||
return true;
|
||||
}
|
||||
|
||||
private float getTransformationY(TransformState ownState,
|
||||
TransformState otherState) {
|
||||
int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
|
||||
int[] ownStablePosition = ownState.getLaidOutLocationOnScreen();
|
||||
return (otherStablePosition[1]
|
||||
+ otherState.getTransformedView().getHeight()
|
||||
- ownStablePosition[1]) * 0.33f;
|
||||
}
|
||||
|
||||
}, TRANSFORMING_VIEW_TEXT);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,4 +34,8 @@ public class NotificationUtils {
|
||||
v.setTag(R.id.icon_is_grayscale, grayscale);
|
||||
return grayscale;
|
||||
}
|
||||
|
||||
public static float interpolate(float start, float end, float amount) {
|
||||
return start * (1.0f - amount) + end * amount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,12 +97,22 @@ public abstract class NotificationViewWrapper implements TransformableView {
|
||||
CrossFadeHelper.fadeOut(mView, endRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformTo(TransformableView notification, float transformationAmount) {
|
||||
CrossFadeHelper.fadeOut(mView, transformationAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification) {
|
||||
// By default we are fading in completely
|
||||
CrossFadeHelper.fadeIn(mView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification, float transformationAmount) {
|
||||
CrossFadeHelper.fadeIn(mView, transformationAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible) {
|
||||
mView.animate().cancel();
|
||||
|
||||
@@ -30,23 +30,30 @@ import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.CrossFadeHelper;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||
import com.android.systemui.statusbar.ViewTransformationHelper;
|
||||
|
||||
/**
|
||||
* A transform state of a view.
|
||||
*/
|
||||
public class TransformState {
|
||||
|
||||
private static final int ANIMATE_X = 0x1;
|
||||
private static final int ANIMATE_Y = 0x10;
|
||||
private static final int ANIMATE_ALL = ANIMATE_X | ANIMATE_Y;
|
||||
private static final float UNDEFINED = -1f;
|
||||
private static final int TRANSOFORM_X = 0x1;
|
||||
private static final int TRANSOFORM_Y = 0x10;
|
||||
private static final int TRANSOFORM_ALL = TRANSOFORM_X | TRANSOFORM_Y;
|
||||
private static final int CLIP_CLIPPING_SET = R.id.clip_children_set_tag;
|
||||
private static final int CLIP_CHILDREN_TAG = R.id.clip_children_tag;
|
||||
private static final int CLIP_TO_PADDING = R.id.clip_to_padding_tag;
|
||||
private static final int TRANSFORMATION_START_X = R.id.transformation_start_x_tag;
|
||||
private static final int TRANSFORMATION_START_Y = R.id.transformation_start_y_tag;
|
||||
private static final int TRANSFORMATION_START_SCLALE_X = R.id.transformation_start_scale_x_tag;
|
||||
private static final int TRANSFORMATION_START_SCLALE_Y = R.id.transformation_start_scale_y_tag;
|
||||
private static Pools.SimplePool<TransformState> sInstancePool = new Pools.SimplePool<>(40);
|
||||
|
||||
protected View mTransformedView;
|
||||
private int[] mOwnPosition = new int[2];
|
||||
private float mTransformationEndY = UNDEFINED;
|
||||
private float mTransformationEndX = UNDEFINED;
|
||||
|
||||
public void initFrom(View view) {
|
||||
mTransformedView = view;
|
||||
@@ -55,129 +62,233 @@ public class TransformState {
|
||||
/**
|
||||
* Transforms the {@link #mTransformedView} from the given transformviewstate
|
||||
* @param otherState the state to transform from
|
||||
* @param transformationAmount how much to transform
|
||||
*/
|
||||
public void transformViewFrom(TransformState otherState) {
|
||||
public void transformViewFrom(TransformState otherState, float transformationAmount) {
|
||||
mTransformedView.animate().cancel();
|
||||
if (sameAs(otherState)) {
|
||||
// We have the same content, lets show ourselves
|
||||
mTransformedView.setAlpha(1.0f);
|
||||
mTransformedView.setVisibility(View.VISIBLE);
|
||||
if (mTransformedView.getVisibility() == View.INVISIBLE) {
|
||||
// We have the same content, lets show ourselves
|
||||
mTransformedView.setAlpha(1.0f);
|
||||
mTransformedView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
CrossFadeHelper.fadeIn(mTransformedView);
|
||||
CrossFadeHelper.fadeIn(mTransformedView, transformationAmount);
|
||||
}
|
||||
animateViewFrom(otherState);
|
||||
transformViewFullyFrom(otherState, transformationAmount);
|
||||
}
|
||||
|
||||
public void animateViewFrom(TransformState otherState) {
|
||||
animateViewFrom(otherState, ANIMATE_ALL);
|
||||
public void transformViewFullyFrom(TransformState otherState, float transformationAmount) {
|
||||
transformViewFrom(otherState, TRANSOFORM_ALL, null, transformationAmount);
|
||||
}
|
||||
|
||||
public void animateViewVerticalFrom(TransformState otherState) {
|
||||
animateViewFrom(otherState, ANIMATE_Y);
|
||||
public void transformViewVerticalFrom(TransformState otherState,
|
||||
ViewTransformationHelper.CustomTransformation customTransformation,
|
||||
float transformationAmount) {
|
||||
transformViewFrom(otherState, TRANSOFORM_Y, customTransformation, transformationAmount);
|
||||
}
|
||||
|
||||
private void animateViewFrom(TransformState otherState, int animationFlags) {
|
||||
public void transformViewVerticalFrom(TransformState otherState, float transformationAmount) {
|
||||
transformViewFrom(otherState, TRANSOFORM_Y, null, transformationAmount);
|
||||
}
|
||||
|
||||
private void transformViewFrom(TransformState otherState, int transformationFlags,
|
||||
ViewTransformationHelper.CustomTransformation customTransformation,
|
||||
float transformationAmount) {
|
||||
final View transformedView = mTransformedView;
|
||||
boolean transformX = (transformationFlags & TRANSOFORM_X) != 0;
|
||||
boolean transformY = (transformationFlags & TRANSOFORM_Y) != 0;
|
||||
boolean transformScale = transformScale();
|
||||
// lets animate the positions correctly
|
||||
int[] otherPosition = otherState.getLocationOnScreen();
|
||||
int[] ownStablePosition = getLaidOutLocationOnScreen();
|
||||
if ((animationFlags & ANIMATE_X) != 0) {
|
||||
transformedView.setTranslationX(otherPosition[0] - ownStablePosition[0]);
|
||||
transformedView.animate().translationX(0);
|
||||
}
|
||||
if ((animationFlags & ANIMATE_Y) != 0) {
|
||||
transformedView.setTranslationY(otherPosition[1] - ownStablePosition[1]);
|
||||
transformedView.animate().translationY(0);
|
||||
}
|
||||
if (animateScale()) {
|
||||
// we also want to animate the scale if we're the same
|
||||
View otherView = otherState.getTransformedView();
|
||||
if (otherView.getWidth() != transformedView.getWidth()) {
|
||||
float scaleX = (otherView.getWidth() * otherView.getScaleX()
|
||||
/ (float) transformedView.getWidth());
|
||||
transformedView.setScaleX(scaleX);
|
||||
transformedView.setPivotX(0);
|
||||
transformedView.animate().scaleX(1.0f);
|
||||
if (transformationAmount == 0.0f) {
|
||||
int[] otherPosition = otherState.getLocationOnScreen();
|
||||
int[] ownStablePosition = getLaidOutLocationOnScreen();
|
||||
if (customTransformation == null
|
||||
|| !customTransformation.initTransformation(this, otherState)) {
|
||||
if (transformX) {
|
||||
setTransformationStartX(otherPosition[0] - ownStablePosition[0]);
|
||||
}
|
||||
if (transformY) {
|
||||
setTransformationStartY(otherPosition[1] - ownStablePosition[1]);
|
||||
}
|
||||
// we also want to animate the scale if we're the same
|
||||
View otherView = otherState.getTransformedView();
|
||||
if (transformScale && otherView.getWidth() != transformedView.getWidth()) {
|
||||
setTransformationStartScaleX(otherView.getWidth() * otherView.getScaleX()
|
||||
/ (float) transformedView.getWidth());
|
||||
transformedView.setPivotX(0);
|
||||
} else {
|
||||
setTransformationStartScaleX(UNDEFINED);
|
||||
}
|
||||
if (transformScale && otherView.getHeight() != transformedView.getHeight()) {
|
||||
setTransformationStartScaleY(otherView.getHeight() * otherView.getScaleY()
|
||||
/ (float) transformedView.getHeight());
|
||||
transformedView.setPivotY(0);
|
||||
} else {
|
||||
setTransformationStartScaleY(UNDEFINED);
|
||||
}
|
||||
}
|
||||
if (otherView.getHeight() != transformedView.getHeight()) {
|
||||
float scaleY = (otherView.getHeight() * otherView.getScaleY()
|
||||
/ (float) transformedView.getHeight());
|
||||
transformedView.setScaleY(scaleY);
|
||||
transformedView.setPivotY(0);
|
||||
transformedView.animate().scaleY(1.0f);
|
||||
if (!transformX) {
|
||||
setTransformationStartX(UNDEFINED);
|
||||
}
|
||||
if (!transformY) {
|
||||
setTransformationStartY(UNDEFINED);
|
||||
}
|
||||
if (!transformScale) {
|
||||
setTransformationStartScaleX(UNDEFINED);
|
||||
setTransformationStartScaleY(UNDEFINED);
|
||||
}
|
||||
setClippingDeactivated(transformedView, true);
|
||||
}
|
||||
float interpolatedValue = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
|
||||
transformationAmount);
|
||||
if (transformX) {
|
||||
transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(),
|
||||
0.0f,
|
||||
interpolatedValue));
|
||||
}
|
||||
if (transformY) {
|
||||
transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(),
|
||||
0.0f,
|
||||
interpolatedValue));
|
||||
}
|
||||
if (transformScale) {
|
||||
float transformationStartScaleX = getTransformationStartScaleX();
|
||||
if (transformationStartScaleX != UNDEFINED) {
|
||||
transformedView.setScaleX(
|
||||
NotificationUtils.interpolate(transformationStartScaleX,
|
||||
1.0f,
|
||||
interpolatedValue));
|
||||
}
|
||||
float transformationStartScaleY = getTransformationStartScaleY();
|
||||
if (transformationStartScaleY != UNDEFINED) {
|
||||
transformedView.setScaleY(
|
||||
NotificationUtils.interpolate(transformationStartScaleY,
|
||||
1.0f,
|
||||
interpolatedValue));
|
||||
}
|
||||
}
|
||||
transformedView.animate()
|
||||
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
|
||||
.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setClippingDeactivated(transformedView, false);
|
||||
}
|
||||
});
|
||||
setClippingDeactivated(transformedView, true);
|
||||
}
|
||||
|
||||
protected boolean animateScale() {
|
||||
protected boolean transformScale() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the {@link #mTransformedView} to the given transformviewstate
|
||||
* @param otherState the state to transform from
|
||||
* @param endRunnable a runnable to run at the end of the animation
|
||||
* @param transformationAmount how much to transform
|
||||
* @return whether an animation was started
|
||||
*/
|
||||
public boolean transformViewTo(TransformState otherState, final Runnable endRunnable) {
|
||||
public boolean transformViewTo(TransformState otherState, float transformationAmount) {
|
||||
mTransformedView.animate().cancel();
|
||||
if (sameAs(otherState)) {
|
||||
// We have the same text, lets show ourselfs
|
||||
mTransformedView.setAlpha(0.0f);
|
||||
mTransformedView.setVisibility(View.INVISIBLE);
|
||||
if (mTransformedView.getVisibility() == View.VISIBLE) {
|
||||
mTransformedView.setAlpha(0.0f);
|
||||
mTransformedView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
CrossFadeHelper.fadeOut(mTransformedView, endRunnable);
|
||||
CrossFadeHelper.fadeOut(mTransformedView, transformationAmount);
|
||||
}
|
||||
animateViewTo(otherState, endRunnable);
|
||||
transformViewFullyTo(otherState, transformationAmount);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void animateViewTo(TransformState otherState, Runnable endRunnable) {
|
||||
animateViewTo(otherState, endRunnable, ANIMATE_ALL);
|
||||
public void transformViewFullyTo(TransformState otherState, float transformationAmount) {
|
||||
transformViewTo(otherState, TRANSOFORM_ALL, null, transformationAmount);
|
||||
}
|
||||
|
||||
public void animateViewVerticalTo(TransformState otherState, Runnable endRunnable) {
|
||||
animateViewTo(otherState, endRunnable, ANIMATE_Y);
|
||||
public void transformViewVerticalTo(TransformState otherState,
|
||||
ViewTransformationHelper.CustomTransformation customTransformation,
|
||||
float transformationAmount) {
|
||||
transformViewTo(otherState, TRANSOFORM_Y, customTransformation, transformationAmount);
|
||||
}
|
||||
|
||||
private void animateViewTo(TransformState otherState, final Runnable endRunnable,
|
||||
int animationFlags) {
|
||||
public void transformViewVerticalTo(TransformState otherState, float transformationAmount) {
|
||||
transformViewTo(otherState, TRANSOFORM_Y, null, transformationAmount);
|
||||
}
|
||||
|
||||
private void transformViewTo(TransformState otherState, int transformationFlags,
|
||||
ViewTransformationHelper.CustomTransformation customTransformation,
|
||||
float transformationAmount) {
|
||||
// lets animate the positions correctly
|
||||
|
||||
final View transformedView = mTransformedView;
|
||||
boolean transformX = (transformationFlags & TRANSOFORM_X) != 0;
|
||||
boolean transformY = (transformationFlags & TRANSOFORM_Y) != 0;
|
||||
boolean transformScale = transformScale();
|
||||
// lets animate the positions correctly
|
||||
if (transformationAmount == 0.0f) {
|
||||
if (transformX) {
|
||||
float transformationStartX = getTransformationStartX();
|
||||
float start = transformationStartX != UNDEFINED ? transformationStartX
|
||||
: transformedView.getTranslationX();
|
||||
setTransformationStartX(start);
|
||||
}
|
||||
if (transformY) {
|
||||
float transformationStartY = getTransformationStartY();
|
||||
float start = transformationStartY != UNDEFINED ? transformationStartY
|
||||
: transformedView.getTranslationY();
|
||||
setTransformationStartY(start);
|
||||
}
|
||||
View otherView = otherState.getTransformedView();
|
||||
if (transformScale && otherView.getWidth() != transformedView.getWidth()) {
|
||||
setTransformationStartScaleX(transformedView.getScaleX());
|
||||
transformedView.setPivotX(0);
|
||||
} else {
|
||||
setTransformationStartScaleX(UNDEFINED);
|
||||
}
|
||||
if (transformScale && otherView.getHeight() != transformedView.getHeight()) {
|
||||
setTransformationStartScaleY(transformedView.getScaleY());
|
||||
transformedView.setPivotY(0);
|
||||
} else {
|
||||
setTransformationStartScaleY(UNDEFINED);
|
||||
}
|
||||
setClippingDeactivated(transformedView, true);
|
||||
}
|
||||
float interpolatedValue = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
|
||||
transformationAmount);
|
||||
int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
|
||||
int[] ownPosition = getLaidOutLocationOnScreen();
|
||||
final View transformedView = mTransformedView;
|
||||
if ((animationFlags & ANIMATE_X) != 0) {
|
||||
transformedView.animate()
|
||||
.translationX(otherStablePosition[0] - ownPosition[0]);
|
||||
if (transformX) {
|
||||
float endX = otherStablePosition[0] - ownPosition[0];
|
||||
if (customTransformation != null
|
||||
&& customTransformation.customTransformTarget(this, otherState)) {
|
||||
endX = mTransformationEndX;
|
||||
}
|
||||
transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(),
|
||||
endX,
|
||||
interpolatedValue));
|
||||
}
|
||||
if ((animationFlags & ANIMATE_Y) != 0) {
|
||||
transformedView.animate()
|
||||
.translationY(otherStablePosition[1] - ownPosition[1]);
|
||||
if (transformY) {
|
||||
float endY = otherStablePosition[1] - ownPosition[1];
|
||||
if (customTransformation != null
|
||||
&& customTransformation.customTransformTarget(this, otherState)) {
|
||||
endY = mTransformationEndY;
|
||||
}
|
||||
transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(),
|
||||
endY,
|
||||
interpolatedValue));
|
||||
}
|
||||
if (transformScale) {
|
||||
View otherView = otherState.getTransformedView();
|
||||
float transformationStartScaleX = getTransformationStartScaleX();
|
||||
if (transformationStartScaleX != UNDEFINED) {
|
||||
transformedView.setScaleX(
|
||||
NotificationUtils.interpolate(transformationStartScaleX,
|
||||
(otherView.getWidth() / (float) transformedView.getWidth()),
|
||||
interpolatedValue));
|
||||
}
|
||||
float transformationStartScaleY = getTransformationStartScaleY();
|
||||
if (transformationStartScaleY != UNDEFINED) {
|
||||
transformedView.setScaleY(
|
||||
NotificationUtils.interpolate(transformationStartScaleY,
|
||||
(otherView.getHeight() / (float) transformedView.getHeight()),
|
||||
interpolatedValue));
|
||||
}
|
||||
}
|
||||
transformedView.animate()
|
||||
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
|
||||
.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (endRunnable != null) {
|
||||
endRunnable.run();
|
||||
}
|
||||
setClippingDeactivated(transformedView, false);
|
||||
}
|
||||
});
|
||||
setClippingDeactivated(transformedView, true);
|
||||
}
|
||||
|
||||
public static void setClippingDeactivated(final View transformedView, boolean deactivated) {
|
||||
@@ -281,8 +392,54 @@ public class TransformState {
|
||||
}
|
||||
}
|
||||
|
||||
public void setTransformationEndY(float transformationEndY) {
|
||||
mTransformationEndY = transformationEndY;
|
||||
}
|
||||
|
||||
public void setTransformationEndX(float transformationEndX) {
|
||||
mTransformationEndX = transformationEndX;
|
||||
}
|
||||
|
||||
public float getTransformationStartX() {
|
||||
Object tag = mTransformedView.getTag(TRANSFORMATION_START_X);
|
||||
return tag == null ? UNDEFINED : (float) tag;
|
||||
}
|
||||
|
||||
public float getTransformationStartY() {
|
||||
Object tag = mTransformedView.getTag(TRANSFORMATION_START_Y);
|
||||
return tag == null ? UNDEFINED : (float) tag;
|
||||
}
|
||||
|
||||
public float getTransformationStartScaleX() {
|
||||
Object tag = mTransformedView.getTag(TRANSFORMATION_START_SCLALE_X);
|
||||
return tag == null ? UNDEFINED : (float) tag;
|
||||
}
|
||||
|
||||
public float getTransformationStartScaleY() {
|
||||
Object tag = mTransformedView.getTag(TRANSFORMATION_START_SCLALE_Y);
|
||||
return tag == null ? UNDEFINED : (float) tag;
|
||||
}
|
||||
|
||||
public void setTransformationStartX(float transformationStartX) {
|
||||
mTransformedView.setTag(TRANSFORMATION_START_X, transformationStartX);
|
||||
}
|
||||
|
||||
public void setTransformationStartY(float transformationStartY) {
|
||||
mTransformedView.setTag(TRANSFORMATION_START_Y, transformationStartY);
|
||||
}
|
||||
|
||||
private void setTransformationStartScaleX(float startScaleX) {
|
||||
mTransformedView.setTag(TRANSFORMATION_START_SCLALE_X, startScaleX);
|
||||
}
|
||||
|
||||
private void setTransformationStartScaleY(float startScaleY) {
|
||||
mTransformedView.setTag(TRANSFORMATION_START_SCLALE_Y, startScaleY);
|
||||
}
|
||||
|
||||
protected void reset() {
|
||||
mTransformedView = null;
|
||||
mTransformationEndX = UNDEFINED;
|
||||
mTransformationEndY = UNDEFINED;
|
||||
}
|
||||
|
||||
public void setVisible(boolean visible) {
|
||||
@@ -306,6 +463,15 @@ public class TransformState {
|
||||
mTransformedView.setTranslationY(0);
|
||||
mTransformedView.setScaleX(1.0f);
|
||||
mTransformedView.setScaleY(1.0f);
|
||||
setClippingDeactivated(mTransformedView, false);
|
||||
abortTransformation();
|
||||
}
|
||||
|
||||
public void abortTransformation() {
|
||||
mTransformedView.setTag(TRANSFORMATION_START_X, UNDEFINED);
|
||||
mTransformedView.setTag(TRANSFORMATION_START_Y, UNDEFINED);
|
||||
mTransformedView.setTag(TRANSFORMATION_START_SCLALE_X, UNDEFINED);
|
||||
mTransformedView.setTag(TRANSFORMATION_START_SCLALE_Y, UNDEFINED);
|
||||
}
|
||||
|
||||
public static TransformState obtain() {
|
||||
|
||||
Reference in New Issue
Block a user