diff --git a/core/api/current.txt b/core/api/current.txt index d5be3495d371b..9b41f44ad6c08 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -729,6 +729,10 @@ package android { field public static final int freezesText = 16843116; // 0x101016c field public static final int fromAlpha = 16843210; // 0x10101ca field public static final int fromDegrees = 16843187; // 0x10101b3 + field public static final int fromExtendBottom; + field public static final int fromExtendLeft; + field public static final int fromExtendRight; + field public static final int fromExtendTop; field public static final int fromId = 16843850; // 0x101044a field public static final int fromScene = 16843741; // 0x10103dd field public static final int fromXDelta = 16843206; // 0x10101c6 @@ -1574,6 +1578,10 @@ package android { field public static final int titleTextStyle = 16843512; // 0x10102f8 field public static final int toAlpha = 16843211; // 0x10101cb field public static final int toDegrees = 16843188; // 0x10101b4 + field public static final int toExtendBottom; + field public static final int toExtendLeft; + field public static final int toExtendRight; + field public static final int toExtendTop; field public static final int toId = 16843849; // 0x1010449 field public static final int toScene = 16843742; // 0x10103de field public static final int toXDelta = 16843207; // 0x10101c7 diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index ab749ee284a86..c42c816f217ec 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -864,6 +864,15 @@ public abstract class Animation implements Cloneable { return mHasRoundedCorners; } + /** + * @return if a window animation has outsets applied to it. + * + * @hide + */ + public boolean hasExtension() { + return false; + } + /** * If showBackground is {@code true} and this animation is applied on a window, then the windows * in the animation will animate with the background associated with this window behind them. diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java index 03c6ca69a592c..a5ea6f6b058af 100644 --- a/core/java/android/view/animation/AnimationSet.java +++ b/core/java/android/view/animation/AnimationSet.java @@ -517,4 +517,15 @@ public class AnimationSet extends Animation { public boolean willChangeBounds() { return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK; } + + /** @hide */ + @Override + public boolean hasExtension() { + for (Animation animation : mAnimations) { + if (animation.hasExtension()) { + return true; + } + } + return false; + } } diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index 7ce0f4571f762..7d1dc76608718 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -190,6 +190,8 @@ public class AnimationUtils { anim = new TranslateAnimation(c, attrs); } else if (name.equals("cliprect")) { anim = new ClipRectAnimation(c, attrs); + } else if (name.equals("extend")) { + anim = new ExtendAnimation(c, attrs); } else { throw new RuntimeException("Unknown animation name: " + parser.getName()); } diff --git a/core/java/android/view/animation/ExtendAnimation.java b/core/java/android/view/animation/ExtendAnimation.java new file mode 100644 index 0000000000000..fd627e50ab0e5 --- /dev/null +++ b/core/java/android/view/animation/ExtendAnimation.java @@ -0,0 +1,176 @@ +/* + * 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.view.animation; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Insets; +import android.util.AttributeSet; + +/** + * An animation that controls the outset of an object. + * + * @hide + */ +public class ExtendAnimation extends Animation { + protected Insets mFromInsets = Insets.NONE; + protected Insets mToInsets = Insets.NONE; + + private int mFromLeftType = ABSOLUTE; + private int mFromTopType = ABSOLUTE; + private int mFromRightType = ABSOLUTE; + private int mFromBottomType = ABSOLUTE; + + private int mToLeftType = ABSOLUTE; + private int mToTopType = ABSOLUTE; + private int mToRightType = ABSOLUTE; + private int mToBottomType = ABSOLUTE; + + private float mFromLeftValue; + private float mFromTopValue; + private float mFromRightValue; + private float mFromBottomValue; + + private float mToLeftValue; + private float mToTopValue; + private float mToRightValue; + private float mToBottomValue; + + /** + * Constructor used when an ExtendAnimation is loaded from a resource. + * + * @param context Application context to use + * @param attrs Attribute set from which to read values + */ + public ExtendAnimation(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.ExtendAnimation); + + Description d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_fromExtendLeft)); + mFromLeftType = d.type; + mFromLeftValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_fromExtendTop)); + mFromTopType = d.type; + mFromTopValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_fromExtendRight)); + mFromRightType = d.type; + mFromRightValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_fromExtendBottom)); + mFromBottomType = d.type; + mFromBottomValue = d.value; + + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_toExtendLeft)); + mToLeftType = d.type; + mToLeftValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_toExtendTop)); + mToTopType = d.type; + mToTopValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_toExtendRight)); + mToRightType = d.type; + mToRightValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_toExtendBottom)); + mToBottomType = d.type; + mToBottomValue = d.value; + + a.recycle(); + } + + /** + * Constructor to use when building an ExtendAnimation from code + * + * @param fromInsets the insets to animate from + * @param toInsets the insets to animate to + */ + public ExtendAnimation(Insets fromInsets, Insets toInsets) { + if (fromInsets == null || toInsets == null) { + throw new RuntimeException("Expected non-null animation outsets"); + } + mFromLeftValue = -fromInsets.left; + mFromTopValue = -fromInsets.top; + mFromRightValue = -fromInsets.right; + mFromBottomValue = -fromInsets.bottom; + + mToLeftValue = -toInsets.left; + mToTopValue = -toInsets.top; + mToRightValue = -toInsets.right; + mToBottomValue = -toInsets.bottom; + } + + /** + * Constructor to use when building an ExtendAnimation from code + */ + public ExtendAnimation(int fromL, int fromT, int fromR, int fromB, + int toL, int toT, int toR, int toB) { + this(Insets.of(-fromL, -fromT, -fromR, -fromB), Insets.of(-toL, -toT, -toR, -toB)); + } + + @Override + protected void applyTransformation(float it, Transformation tr) { + int l = mFromInsets.left + (int) ((mToInsets.left - mFromInsets.left) * it); + int t = mFromInsets.top + (int) ((mToInsets.top - mFromInsets.top) * it); + int r = mFromInsets.right + (int) ((mToInsets.right - mFromInsets.right) * it); + int b = mFromInsets.bottom + (int) ((mToInsets.bottom - mFromInsets.bottom) * it); + tr.setInsets(l, t, r, b); + } + + @Override + public boolean willChangeTransformationMatrix() { + return false; + } + + /** @hide */ + @Override + public boolean hasExtension() { + return mFromInsets.left < 0 || mFromInsets.top < 0 || mFromInsets.right < 0 + || mFromInsets.bottom < 0; + } + + @Override + public void initialize(int width, int height, int parentWidth, int parentHeight) { + super.initialize(width, height, parentWidth, parentHeight); + // We remove any negative extension (i.e. positive insets) and set those to 0 + mFromInsets = Insets.min(Insets.of( + -(int) resolveSize(mFromLeftType, mFromLeftValue, width, parentWidth), + -(int) resolveSize(mFromTopType, mFromTopValue, height, parentHeight), + -(int) resolveSize(mFromRightType, mFromRightValue, width, parentWidth), + -(int) resolveSize(mFromBottomType, mFromBottomValue, height, parentHeight) + ), Insets.NONE); + mToInsets = Insets.min(Insets.of( + -(int) resolveSize(mToLeftType, mToLeftValue, width, parentWidth), + -(int) resolveSize(mToTopType, mToTopValue, height, parentHeight), + -(int) resolveSize(mToRightType, mToRightValue, width, parentWidth), + -(int) resolveSize(mToBottomType, mToBottomValue, height, parentHeight) + ), Insets.NONE); + } +} diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java index b35a66e6eb26b..bd623088017a3 100644 --- a/core/java/android/view/animation/Transformation.java +++ b/core/java/android/view/animation/Transformation.java @@ -18,6 +18,7 @@ package android.view.animation; import android.annotation.FloatRange; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Rect; @@ -53,6 +54,8 @@ public class Transformation { private boolean mHasClipRect; private Rect mClipRect = new Rect(); + private Insets mInsets = Insets.NONE; + /** * Creates a new transformation with alpha = 1 and the identity matrix. */ @@ -132,8 +135,9 @@ public class Transformation { setClipRect(bounds); } } + setInsets(Insets.add(getInsets(), t.getInsets())); } - + /** * Like {@link #compose(Transformation)} but does this.postConcat(t) of * the transformation matrix. @@ -160,7 +164,7 @@ public class Transformation { public Matrix getMatrix() { return mMatrix; } - + /** * Sets the degree of transparency * @param alpha 1.0 means fully opaqe and 0.0 means fully transparent @@ -169,6 +173,13 @@ public class Transformation { mAlpha = alpha; } + /** + * @return The degree of transparency + */ + public float getAlpha() { + return mAlpha; + } + /** * Sets the current Transform's clip rect * @hide @@ -203,12 +214,29 @@ public class Transformation { } /** - * @return The degree of transparency + * Sets the current Transform's insets + * @hide */ - public float getAlpha() { - return mAlpha; + public void setInsets(Insets insets) { + mInsets = insets; } - + + /** + * Sets the current Transform's insets + * @hide + */ + public void setInsets(int left, int top, int right, int bottom) { + mInsets = Insets.of(left, top, right, bottom); + } + + /** + * Returns the current Transform's outset rect + * @hide + */ + public Insets getInsets() { + return mInsets; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(64); @@ -216,7 +244,7 @@ public class Transformation { toShortString(sb); return sb.toString(); } - + /** * Return a string representation of the transformation in a compact form. */ @@ -225,7 +253,7 @@ public class Transformation { toShortString(sb); return sb.toString(); } - + /** * @hide */ @@ -234,7 +262,7 @@ public class Transformation { sb.append(" matrix="); sb.append(mMatrix.toShortString()); sb.append('}'); } - + /** * Print short string, to optimize dumping. * @hide diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 7916ef43da509..04e29890568a9 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2885,7 +2885,7 @@ public void sayHello(View v) method of your context (typically, your Activity). {@deprecated View actually traverses the Context - hierarchy looking for the relevant method, which is fragile (an intermediate + hierarchy looking for the relevant method, which is fragile (an intermediate ContextWrapper adding a same-named method would change behavior) and restricts bytecode optimizers such as R8. Instead, use View.setOnClickListener.}--> @@ -6975,6 +6975,34 @@ + + + + + + + + + + + + + + + + + + + + @@ -7827,7 +7855,7 @@ diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 2e96c65ef17ff..047c04b3596c7 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3260,6 +3260,14 @@ + + + + + + + +