Loading api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -35834,6 +35834,7 @@ package android.view { method protected int getBottomPaddingOffset(); method public float getCameraDistance(); method public android.graphics.Rect getClipBounds(); method public boolean getClipBounds(android.graphics.Rect); method public final boolean getClipToOutline(); method public java.lang.CharSequence getContentDescription(); method public final android.content.Context getContext(); api/system-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -38033,6 +38033,7 @@ package android.view { method protected int getBottomPaddingOffset(); method public float getCameraDistance(); method public android.graphics.Rect getClipBounds(); method public boolean getClipBounds(android.graphics.Rect); method public final boolean getClipToOutline(); method public java.lang.CharSequence getContentDescription(); method public final android.content.Context getContext(); core/java/android/view/View.java +18 −0 Original line number Diff line number Diff line Loading @@ -15101,6 +15101,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return (mClipBounds != null) ? new Rect(mClipBounds) : null; } /** * Populates an output rectangle with the clip bounds of the view, * returning {@code true} if successful or {@code false} if the view's * clip bounds are {@code null}. * * @param outRect rectangle in which to place the clip bounds of the view * @return {@code true} if successful or {@code false} if the view's * clip bounds are {@code null} */ public boolean getClipBounds(Rect outRect) { if (mClipBounds != null) { outRect.set(mClipBounds); return true; } return false; } /** * Utility function, called by draw(canvas, parent, drawingTime) to handle the less common * case of an active Animation being run on the view. core/java/android/widget/PopupWindow.java +46 −36 Original line number Diff line number Diff line Loading @@ -94,7 +94,6 @@ public class PopupWindow { private final int[] mDrawingLocation = new int[2]; private final int[] mScreenLocation = new int[2]; private final Rect mTempRect = new Rect(); private final Rect mAnchorBounds = new Rect(); private Context mContext; private WindowManager mWindowManager; Loading Loading @@ -159,28 +158,6 @@ public class PopupWindow { private WeakReference<View> mAnchor; private final EpicenterCallback mEpicenterCallback = new EpicenterCallback() { @Override public Rect onGetEpicenter(Transition transition) { final View anchor = mAnchor != null ? mAnchor.get() : null; final View decor = mDecorView; if (anchor == null || decor == null) { return null; } final Rect anchorBounds = mAnchorBounds; final int[] anchorLocation = anchor.getLocationOnScreen(); final int[] popupLocation = mDecorView.getLocationOnScreen(); // Compute the position of the anchor relative to the popup. anchorBounds.set(0, 0, anchor.getWidth(), anchor.getHeight()); anchorBounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]); return anchorBounds; } }; private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() { @Override public void onScrollChanged() { Loading Loading @@ -355,18 +332,10 @@ public class PopupWindow { public void setEnterTransition(Transition enterTransition) { mEnterTransition = enterTransition; if (mEnterTransition != null) { mEnterTransition.setEpicenterCallback(mEpicenterCallback); } } public void setExitTransition(Transition exitTransition) { mExitTransition = exitTransition; if (mExitTransition != null) { mExitTransition.setEpicenterCallback(mEpicenterCallback); } } private Transition getTransition(int resId) { Loading Loading @@ -1281,11 +1250,14 @@ public class PopupWindow { final PopupDecorView decorView = mDecorView; decorView.setFitsSystemWindows(mLayoutInsetDecor); decorView.requestEnterTransition(mEnterTransition); setLayoutDirectionFromAnchor(); mWindowManager.addView(decorView, p); if (mEnterTransition != null) { decorView.requestEnterTransition(mEnterTransition); } } private void setLayoutDirectionFromAnchor() { Loading Loading @@ -1607,13 +1579,25 @@ public class PopupWindow { // Ensure any ongoing or pending transitions are canceled. decorView.cancelTransitions(); unregisterForScrollChanged(); mIsShowing = false; mIsTransitioningToDismiss = true; if (mExitTransition != null && decorView.isLaidOut()) { decorView.startExitTransition(mExitTransition, new TransitionListenerAdapter() { final Transition exitTransition = mExitTransition; if (exitTransition != null && decorView.isLaidOut()) { // The decor view is non-interactive during exit transitions. final LayoutParams p = (LayoutParams) decorView.getLayoutParams(); p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE; mWindowManager.updateViewLayout(decorView, p); final Rect epicenter = getRelativeAnchorBounds(); exitTransition.setEpicenterCallback(new EpicenterCallback() { @Override public Rect onGetEpicenter(Transition transition) { return epicenter; } }); decorView.startExitTransition(exitTransition, new TransitionListenerAdapter() { @Override public void onTransitionEnd(Transition transition) { dismissImmediate(decorView, contentHolder, contentView); Loading @@ -1623,11 +1607,30 @@ public class PopupWindow { dismissImmediate(decorView, contentHolder, contentView); } // Clears the anchor view. unregisterForScrollChanged(); if (mOnDismissListener != null) { mOnDismissListener.onDismiss(); } } private Rect getRelativeAnchorBounds() { final View anchor = mAnchor != null ? mAnchor.get() : null; final View decor = mDecorView; if (anchor == null || decor == null) { return null; } final int[] anchorLocation = anchor.getLocationOnScreen(); final int[] popupLocation = mDecorView.getLocationOnScreen(); // Compute the position of the anchor relative to the popup. final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight()); bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]); return bounds; } /** * Removes the popup from the window manager and tears down the supporting * view hierarchy, if necessary. Loading Loading @@ -1996,6 +1999,13 @@ public class PopupWindow { observer.removeOnGlobalLayoutListener(this); } final Rect epicenter = getRelativeAnchorBounds(); enterTransition.setEpicenterCallback(new EpicenterCallback() { @Override public Rect onGetEpicenter(Transition transition) { return epicenter; } }); startEnterTransition(enterTransition); } }); Loading core/java/com/android/internal/transition/EpicenterClipReveal.java +108 −11 Original line number Diff line number Diff line Loading @@ -17,15 +17,23 @@ package com.android.internal.transition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.RectEvaluator; import android.animation.TimeInterpolator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.transition.TransitionValues; import android.transition.Visibility; import android.util.AttributeSet; import android.util.Property; import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.view.animation.PathInterpolator; import com.android.internal.R; /** * EpicenterClipReveal captures the {@link View#getClipBounds()} before and Loading @@ -36,10 +44,39 @@ public class EpicenterClipReveal extends Visibility { private static final String PROPNAME_CLIP = "android:epicenterReveal:clip"; private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds"; public EpicenterClipReveal() {} private final TimeInterpolator mInterpolatorX; private final TimeInterpolator mInterpolatorY; private final boolean mCenterClipBounds; public EpicenterClipReveal() { mInterpolatorX = null; mInterpolatorY = null; mCenterClipBounds = false; } public EpicenterClipReveal(Context context, AttributeSet attrs) { super(context, attrs); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.EpicenterClipReveal, 0, 0); mCenterClipBounds = a.getBoolean(R.styleable.EpicenterClipReveal_centerClipBounds, false); final int interpolatorX = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorX, 0); if (interpolatorX != 0) { mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX); } else { mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN; } final int interpolatorY = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorY, 0); if (interpolatorY != 0) { mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY); } else { mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN; } a.recycle(); } @Override Loading Loading @@ -82,7 +119,7 @@ public class EpicenterClipReveal extends Visibility { // Prepare the view. view.setClipBounds(start); return createRectAnimator(view, start, end, endValues); return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY); } @Override Loading @@ -98,17 +135,23 @@ public class EpicenterClipReveal extends Visibility { // Prepare the view. view.setClipBounds(start); return createRectAnimator(view, start, end, endValues); return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY); } private Rect getEpicenterOrCenter(Rect bestRect) { final Rect epicenter = getEpicenter(); if (epicenter != null) { // Translate the clip bounds to be centered within the target bounds. if (mCenterClipBounds) { final int offsetX = bestRect.centerX() - epicenter.centerX(); final int offsetY = bestRect.centerY() - epicenter.centerY(); epicenter.offset(offsetX, offsetY); } return epicenter; } int centerX = bestRect.centerX(); int centerY = bestRect.centerY(); final int centerX = bestRect.centerX(); final int centerY = bestRect.centerY(); return new Rect(centerX, centerY, centerX, centerY); } Loading @@ -120,17 +163,71 @@ public class EpicenterClipReveal extends Visibility { return clipRect; } private Animator createRectAnimator(final View view, Rect start, Rect end, TransitionValues endValues) { final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP); private static Animator createRectAnimator(final View view, Rect start, Rect end, TransitionValues endValues, TimeInterpolator interpolatorX, TimeInterpolator interpolatorY) { final RectEvaluator evaluator = new RectEvaluator(new Rect()); ObjectAnimator anim = ObjectAnimator.ofObject(view, "clipBounds", evaluator, start, end); anim.addListener(new AnimatorListenerAdapter() { final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP); final ClipDimenProperty propX = new ClipDimenProperty(ClipDimenProperty.TARGET_X); final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, start, end); if (interpolatorX != null) { animX.setInterpolator(interpolatorX); } final ClipDimenProperty propY = new ClipDimenProperty(ClipDimenProperty.TARGET_Y); final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, start, end); if (interpolatorY != null) { animY.setInterpolator(interpolatorY); } final AnimatorSet animSet = new AnimatorSet(); animSet.playTogether(animX, animY); animSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { view.setClipBounds(terminalClip); } }); return anim; return animSet; } private static class ClipDimenProperty extends Property<View, Rect> { public static final char TARGET_X = 'x'; public static final char TARGET_Y = 'y'; private final Rect mTempRect = new Rect(); private final int mTargetDimension; public ClipDimenProperty(char targetDimension) { super(Rect.class, "clip_bounds_" + targetDimension); mTargetDimension = targetDimension; } @Override public Rect get(View object) { final Rect tempRect = mTempRect; if (!object.getClipBounds(tempRect)) { tempRect.setEmpty(); } return tempRect; } @Override public void set(View object, Rect value) { final Rect tempRect = mTempRect; if (object.getClipBounds(tempRect)) { if (mTargetDimension == TARGET_X) { tempRect.left = value.left; tempRect.right = value.right; } else { tempRect.top = value.top; tempRect.bottom = value.bottom; } object.setClipBounds(tempRect); } } } } Loading
api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -35834,6 +35834,7 @@ package android.view { method protected int getBottomPaddingOffset(); method public float getCameraDistance(); method public android.graphics.Rect getClipBounds(); method public boolean getClipBounds(android.graphics.Rect); method public final boolean getClipToOutline(); method public java.lang.CharSequence getContentDescription(); method public final android.content.Context getContext();
api/system-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -38033,6 +38033,7 @@ package android.view { method protected int getBottomPaddingOffset(); method public float getCameraDistance(); method public android.graphics.Rect getClipBounds(); method public boolean getClipBounds(android.graphics.Rect); method public final boolean getClipToOutline(); method public java.lang.CharSequence getContentDescription(); method public final android.content.Context getContext();
core/java/android/view/View.java +18 −0 Original line number Diff line number Diff line Loading @@ -15101,6 +15101,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return (mClipBounds != null) ? new Rect(mClipBounds) : null; } /** * Populates an output rectangle with the clip bounds of the view, * returning {@code true} if successful or {@code false} if the view's * clip bounds are {@code null}. * * @param outRect rectangle in which to place the clip bounds of the view * @return {@code true} if successful or {@code false} if the view's * clip bounds are {@code null} */ public boolean getClipBounds(Rect outRect) { if (mClipBounds != null) { outRect.set(mClipBounds); return true; } return false; } /** * Utility function, called by draw(canvas, parent, drawingTime) to handle the less common * case of an active Animation being run on the view.
core/java/android/widget/PopupWindow.java +46 −36 Original line number Diff line number Diff line Loading @@ -94,7 +94,6 @@ public class PopupWindow { private final int[] mDrawingLocation = new int[2]; private final int[] mScreenLocation = new int[2]; private final Rect mTempRect = new Rect(); private final Rect mAnchorBounds = new Rect(); private Context mContext; private WindowManager mWindowManager; Loading Loading @@ -159,28 +158,6 @@ public class PopupWindow { private WeakReference<View> mAnchor; private final EpicenterCallback mEpicenterCallback = new EpicenterCallback() { @Override public Rect onGetEpicenter(Transition transition) { final View anchor = mAnchor != null ? mAnchor.get() : null; final View decor = mDecorView; if (anchor == null || decor == null) { return null; } final Rect anchorBounds = mAnchorBounds; final int[] anchorLocation = anchor.getLocationOnScreen(); final int[] popupLocation = mDecorView.getLocationOnScreen(); // Compute the position of the anchor relative to the popup. anchorBounds.set(0, 0, anchor.getWidth(), anchor.getHeight()); anchorBounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]); return anchorBounds; } }; private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() { @Override public void onScrollChanged() { Loading Loading @@ -355,18 +332,10 @@ public class PopupWindow { public void setEnterTransition(Transition enterTransition) { mEnterTransition = enterTransition; if (mEnterTransition != null) { mEnterTransition.setEpicenterCallback(mEpicenterCallback); } } public void setExitTransition(Transition exitTransition) { mExitTransition = exitTransition; if (mExitTransition != null) { mExitTransition.setEpicenterCallback(mEpicenterCallback); } } private Transition getTransition(int resId) { Loading Loading @@ -1281,11 +1250,14 @@ public class PopupWindow { final PopupDecorView decorView = mDecorView; decorView.setFitsSystemWindows(mLayoutInsetDecor); decorView.requestEnterTransition(mEnterTransition); setLayoutDirectionFromAnchor(); mWindowManager.addView(decorView, p); if (mEnterTransition != null) { decorView.requestEnterTransition(mEnterTransition); } } private void setLayoutDirectionFromAnchor() { Loading Loading @@ -1607,13 +1579,25 @@ public class PopupWindow { // Ensure any ongoing or pending transitions are canceled. decorView.cancelTransitions(); unregisterForScrollChanged(); mIsShowing = false; mIsTransitioningToDismiss = true; if (mExitTransition != null && decorView.isLaidOut()) { decorView.startExitTransition(mExitTransition, new TransitionListenerAdapter() { final Transition exitTransition = mExitTransition; if (exitTransition != null && decorView.isLaidOut()) { // The decor view is non-interactive during exit transitions. final LayoutParams p = (LayoutParams) decorView.getLayoutParams(); p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE; mWindowManager.updateViewLayout(decorView, p); final Rect epicenter = getRelativeAnchorBounds(); exitTransition.setEpicenterCallback(new EpicenterCallback() { @Override public Rect onGetEpicenter(Transition transition) { return epicenter; } }); decorView.startExitTransition(exitTransition, new TransitionListenerAdapter() { @Override public void onTransitionEnd(Transition transition) { dismissImmediate(decorView, contentHolder, contentView); Loading @@ -1623,11 +1607,30 @@ public class PopupWindow { dismissImmediate(decorView, contentHolder, contentView); } // Clears the anchor view. unregisterForScrollChanged(); if (mOnDismissListener != null) { mOnDismissListener.onDismiss(); } } private Rect getRelativeAnchorBounds() { final View anchor = mAnchor != null ? mAnchor.get() : null; final View decor = mDecorView; if (anchor == null || decor == null) { return null; } final int[] anchorLocation = anchor.getLocationOnScreen(); final int[] popupLocation = mDecorView.getLocationOnScreen(); // Compute the position of the anchor relative to the popup. final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight()); bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]); return bounds; } /** * Removes the popup from the window manager and tears down the supporting * view hierarchy, if necessary. Loading Loading @@ -1996,6 +1999,13 @@ public class PopupWindow { observer.removeOnGlobalLayoutListener(this); } final Rect epicenter = getRelativeAnchorBounds(); enterTransition.setEpicenterCallback(new EpicenterCallback() { @Override public Rect onGetEpicenter(Transition transition) { return epicenter; } }); startEnterTransition(enterTransition); } }); Loading
core/java/com/android/internal/transition/EpicenterClipReveal.java +108 −11 Original line number Diff line number Diff line Loading @@ -17,15 +17,23 @@ package com.android.internal.transition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.RectEvaluator; import android.animation.TimeInterpolator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.transition.TransitionValues; import android.transition.Visibility; import android.util.AttributeSet; import android.util.Property; import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.view.animation.PathInterpolator; import com.android.internal.R; /** * EpicenterClipReveal captures the {@link View#getClipBounds()} before and Loading @@ -36,10 +44,39 @@ public class EpicenterClipReveal extends Visibility { private static final String PROPNAME_CLIP = "android:epicenterReveal:clip"; private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds"; public EpicenterClipReveal() {} private final TimeInterpolator mInterpolatorX; private final TimeInterpolator mInterpolatorY; private final boolean mCenterClipBounds; public EpicenterClipReveal() { mInterpolatorX = null; mInterpolatorY = null; mCenterClipBounds = false; } public EpicenterClipReveal(Context context, AttributeSet attrs) { super(context, attrs); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.EpicenterClipReveal, 0, 0); mCenterClipBounds = a.getBoolean(R.styleable.EpicenterClipReveal_centerClipBounds, false); final int interpolatorX = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorX, 0); if (interpolatorX != 0) { mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX); } else { mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN; } final int interpolatorY = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorY, 0); if (interpolatorY != 0) { mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY); } else { mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN; } a.recycle(); } @Override Loading Loading @@ -82,7 +119,7 @@ public class EpicenterClipReveal extends Visibility { // Prepare the view. view.setClipBounds(start); return createRectAnimator(view, start, end, endValues); return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY); } @Override Loading @@ -98,17 +135,23 @@ public class EpicenterClipReveal extends Visibility { // Prepare the view. view.setClipBounds(start); return createRectAnimator(view, start, end, endValues); return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY); } private Rect getEpicenterOrCenter(Rect bestRect) { final Rect epicenter = getEpicenter(); if (epicenter != null) { // Translate the clip bounds to be centered within the target bounds. if (mCenterClipBounds) { final int offsetX = bestRect.centerX() - epicenter.centerX(); final int offsetY = bestRect.centerY() - epicenter.centerY(); epicenter.offset(offsetX, offsetY); } return epicenter; } int centerX = bestRect.centerX(); int centerY = bestRect.centerY(); final int centerX = bestRect.centerX(); final int centerY = bestRect.centerY(); return new Rect(centerX, centerY, centerX, centerY); } Loading @@ -120,17 +163,71 @@ public class EpicenterClipReveal extends Visibility { return clipRect; } private Animator createRectAnimator(final View view, Rect start, Rect end, TransitionValues endValues) { final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP); private static Animator createRectAnimator(final View view, Rect start, Rect end, TransitionValues endValues, TimeInterpolator interpolatorX, TimeInterpolator interpolatorY) { final RectEvaluator evaluator = new RectEvaluator(new Rect()); ObjectAnimator anim = ObjectAnimator.ofObject(view, "clipBounds", evaluator, start, end); anim.addListener(new AnimatorListenerAdapter() { final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP); final ClipDimenProperty propX = new ClipDimenProperty(ClipDimenProperty.TARGET_X); final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, start, end); if (interpolatorX != null) { animX.setInterpolator(interpolatorX); } final ClipDimenProperty propY = new ClipDimenProperty(ClipDimenProperty.TARGET_Y); final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, start, end); if (interpolatorY != null) { animY.setInterpolator(interpolatorY); } final AnimatorSet animSet = new AnimatorSet(); animSet.playTogether(animX, animY); animSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { view.setClipBounds(terminalClip); } }); return anim; return animSet; } private static class ClipDimenProperty extends Property<View, Rect> { public static final char TARGET_X = 'x'; public static final char TARGET_Y = 'y'; private final Rect mTempRect = new Rect(); private final int mTargetDimension; public ClipDimenProperty(char targetDimension) { super(Rect.class, "clip_bounds_" + targetDimension); mTargetDimension = targetDimension; } @Override public Rect get(View object) { final Rect tempRect = mTempRect; if (!object.getClipBounds(tempRect)) { tempRect.setEmpty(); } return tempRect; } @Override public void set(View object, Rect value) { final Rect tempRect = mTempRect; if (object.getClipBounds(tempRect)) { if (mTargetDimension == TARGET_X) { tempRect.left = value.left; tempRect.right = value.right; } else { tempRect.top = value.top; tempRect.bottom = value.bottom; } object.setClipBounds(tempRect); } } } }