diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 148527fc47df7..fab88a28e2196 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5619,6 +5619,16 @@ public class Activity extends ContextThemeWrapper public void onEnterAnimationComplete() { } + /** + * @hide + */ + public void dispatchEnterAnimationComplete() { + onEnterAnimationComplete(); + if (getWindow() != null && getWindow().getDecorView() != null) { + getWindow().getDecorView().getViewTreeObserver().dispatchOnEnterAnimationComplete(); + } + } + /** * Adjust the current immersive mode setting. * diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 98a096c661be9..b64e724faa56c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2536,7 +2536,7 @@ public final class ActivityThread { private void handleEnterAnimationComplete(IBinder token) { ActivityClientRecord r = mActivities.get(token); if (r != null) { - r.activity.onEnterAnimationComplete(); + r.activity.dispatchEnterAnimationComplete(); } } diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index b85fec8a0c083..521fd31173297 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -37,6 +37,8 @@ public final class ViewTreeObserver { private CopyOnWriteArrayList mOnWindowAttachListeners; private CopyOnWriteArrayList mOnGlobalFocusListeners; private CopyOnWriteArrayList mOnTouchModeChangeListeners; + private CopyOnWriteArrayList + mOnEnterAnimationCompleteListeners; // Non-recursive listeners use CopyOnWriteArray // Any listener invoked from ViewRootImpl.performTraversals() should not be recursive @@ -315,6 +317,13 @@ public final class ViewTreeObserver { public void onComputeInternalInsets(InternalInsetsInfo inoutInfo); } + /** + * @hide + */ + public interface OnEnterAnimationCompleteListener { + public void onEnterAnimationComplete(); + } + /** * Creates a new ViewTreeObserver. This constructor should not be called */ @@ -780,6 +789,29 @@ public final class ViewTreeObserver { mOnComputeInternalInsetsListeners.remove(victim); } + /** + * @hide + */ + public void addOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) { + checkIsAlive(); + if (mOnEnterAnimationCompleteListeners == null) { + mOnEnterAnimationCompleteListeners = + new CopyOnWriteArrayList(); + } + mOnEnterAnimationCompleteListeners.add(listener); + } + + /** + * @hide + */ + public void removeOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) { + checkIsAlive(); + if (mOnEnterAnimationCompleteListeners == null) { + return; + } + mOnEnterAnimationCompleteListeners.remove(listener); + } + private void checkIsAlive() { if (!mAlive) { throw new IllegalStateException("This ViewTreeObserver is not alive, call " @@ -1021,6 +1053,23 @@ public final class ViewTreeObserver { } } + /** + * @hide + */ + public final void dispatchOnEnterAnimationComplete() { + // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to + // perform the dispatching. The iterator is a safe guard against listeners that + // could mutate the list by calling the various add/remove methods. This prevents + // the array from being modified while we iterate it. + final CopyOnWriteArrayList listeners = + mOnEnterAnimationCompleteListeners; + if (listeners != null && !listeners.isEmpty()) { + for (OnEnterAnimationCompleteListener listener : listeners) { + listener.onEnterAnimationComplete(); + } + } + } + /** * Copy on write array. This array is not thread safe, and only one loop can * iterate over this array at any given time. This class avoids allocations diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java index 99b1baef132c5..d617c0591c390 100644 --- a/core/java/com/android/internal/widget/SwipeDismissLayout.java +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -26,6 +26,7 @@ import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; @@ -76,6 +77,19 @@ public class SwipeDismissLayout extends FrameLayout { private OnDismissedListener mDismissedListener; private OnSwipeProgressChangedListener mProgressListener; + private ViewTreeObserver.OnEnterAnimationCompleteListener mOnEnterAnimationCompleteListener = + new ViewTreeObserver.OnEnterAnimationCompleteListener() { + @Override + public void onEnterAnimationComplete() { + // SwipeDismissLayout assumes that the host Activity is translucent + // and temporarily disables translucency when it is fully visible. + // As soon as the user starts swiping, we will re-enable + // translucency. + if (getContext() instanceof Activity) { + ((Activity) getContext()).convertFromTranslucent(); + } + } + }; private float mLastX; @@ -103,13 +117,6 @@ public class SwipeDismissLayout extends FrameLayout { android.R.integer.config_shortAnimTime); mCancelInterpolator = new DecelerateInterpolator(1.5f); mDismissInterpolator = new AccelerateInterpolator(1.5f); - // SwipeDismissLayout assumes that the host Activity is translucent - // and temporarily disables translucency when it is fully visible. - // As soon as the user starts swiping, we will re-enable - // translucency. - if (context instanceof Activity) { - ((Activity) context).convertFromTranslucent(); - } } public void setOnDismissedListener(OnDismissedListener listener) { @@ -120,6 +127,24 @@ public class SwipeDismissLayout extends FrameLayout { mProgressListener = listener; } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (getContext() instanceof Activity) { + getViewTreeObserver().addOnEnterAnimationCompleteListener( + mOnEnterAnimationCompleteListener); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (getContext() instanceof Activity) { + getViewTreeObserver().removeOnEnterAnimationCompleteListener( + mOnEnterAnimationCompleteListener); + } + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // offset because the view is translated during swipe