summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityTransitionCoordinator.java57
-rw-r--r--core/java/android/app/ActivityTransitionState.java21
-rw-r--r--core/java/android/app/EnterTransitionCoordinator.java69
-rw-r--r--core/java/android/app/ExitTransitionCoordinator.java15
-rw-r--r--core/java/android/app/FragmentTransition.java126
-rw-r--r--core/java/android/transition/TransitionManager.java8
-rw-r--r--core/java/com/android/internal/view/OneShotPreDrawListener.java89
7 files changed, 220 insertions, 165 deletions
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index ec21882f431e..af41db01418e 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -37,6 +37,8 @@ import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.ImageView;
+import com.android.internal.view.OneShotPreDrawListener;
+
import java.util.ArrayList;
import java.util.Collection;
@@ -570,16 +572,9 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
protected void scheduleSetSharedElementEnd(final ArrayList<View> snapshots) {
final View decorView = getDecor();
if (decorView != null) {
- decorView.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- decorView.getViewTreeObserver().removeOnPreDrawListener(this);
- notifySharedElementEnd(snapshots);
- return true;
- }
- }
- );
+ OneShotPreDrawListener.add(decorView, () -> {
+ notifySharedElementEnd(snapshots);
+ });
}
}
@@ -816,6 +811,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
if (moveWithParent && !isInTransitionGroup(parent, decor)) {
GhostViewListeners listener = new GhostViewListeners(view, parent, decor);
parent.getViewTreeObserver().addOnPreDrawListener(listener);
+ parent.addOnAttachStateChangeListener(listener);
mGhostViewListeners.add(listener);
}
}
@@ -842,8 +838,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
int numListeners = mGhostViewListeners.size();
for (int i = 0; i < numListeners; i++) {
GhostViewListeners listener = mGhostViewListeners.get(i);
- ViewGroup parent = (ViewGroup) listener.getView().getParent();
- parent.getViewTreeObserver().removeOnPreDrawListener(listener);
+ listener.removeListener();
}
mGhostViewListeners.clear();
@@ -874,15 +869,9 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
protected void scheduleGhostVisibilityChange(final int visibility) {
final View decorView = getDecor();
if (decorView != null) {
- decorView.getViewTreeObserver()
- .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- decorView.getViewTreeObserver().removeOnPreDrawListener(this);
- setGhostVisibility(visibility);
- return true;
- }
- });
+ OneShotPreDrawListener.add(decorView, () -> {
+ setGhostVisibility(visibility);
+ });
}
}
@@ -988,16 +977,19 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
}
}
- private static class GhostViewListeners implements ViewTreeObserver.OnPreDrawListener {
+ private static class GhostViewListeners implements ViewTreeObserver.OnPreDrawListener,
+ View.OnAttachStateChangeListener {
private View mView;
private ViewGroup mDecor;
private View mParent;
private Matrix mMatrix = new Matrix();
+ private ViewTreeObserver mViewTreeObserver;
public GhostViewListeners(View view, View parent, ViewGroup decor) {
mView = view;
mParent = parent;
mDecor = decor;
+ mViewTreeObserver = parent.getViewTreeObserver();
}
public View getView() {
@@ -1008,13 +1000,32 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
public boolean onPreDraw() {
GhostView ghostView = GhostView.getGhost(mView);
if (ghostView == null) {
- mParent.getViewTreeObserver().removeOnPreDrawListener(this);
+ removeListener();
} else {
GhostView.calculateMatrix(mView, mDecor, mMatrix);
ghostView.setMatrix(mMatrix);
}
return true;
}
+
+ public void removeListener() {
+ if (mViewTreeObserver.isAlive()) {
+ mViewTreeObserver.removeOnPreDrawListener(this);
+ } else {
+ mParent.getViewTreeObserver().removeOnPreDrawListener(this);
+ }
+ mParent.removeOnAttachStateChangeListener(this);
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mViewTreeObserver = v.getViewTreeObserver();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ removeListener();
+ }
}
static class SharedElementOriginalState {
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 60046b5932ee..f2616ffa2465 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -22,9 +22,10 @@ import android.transition.Transition;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import android.view.Window;
+import com.android.internal.view.OneShotPreDrawListener;
+
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -321,18 +322,12 @@ class ActivityTransitionState {
}
if (delayExitBack && decor != null) {
final ViewGroup finalDecor = decor;
- decor.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- finalDecor.getViewTreeObserver().removeOnPreDrawListener(this);
- if (mReturnExitCoordinator != null) {
- mReturnExitCoordinator.startExit(activity.mResultCode,
- activity.mResultData);
- }
- return true;
- }
- });
+ OneShotPreDrawListener.add(decor, () -> {
+ if (mReturnExitCoordinator != null) {
+ mReturnExitCoordinator.startExit(activity.mResultCode,
+ activity.mResultData);
+ }
+ });
} else {
mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 27a02003669d..3464c4d8cbeb 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -30,10 +30,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroupOverlay;
import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.Window;
import android.view.accessibility.AccessibilityEvent;
+import com.android.internal.view.OneShotPreDrawListener;
+
import java.util.ArrayList;
/**
@@ -58,7 +59,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
private boolean mAreViewsReady;
private boolean mIsViewsTransitionStarted;
private Transition mEnterViewsTransition;
- private OnPreDrawListener mViewsReadyListener;
+ private OneShotPreDrawListener mViewsReadyListener;
private final boolean mIsCrossTask;
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
@@ -74,12 +75,17 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
final View decorView = getDecor();
if (decorView != null) {
- decorView.getViewTreeObserver().addOnPreDrawListener(
+ final ViewTreeObserver viewTreeObserver = decorView.getViewTreeObserver();
+ viewTreeObserver.addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (mIsReadyForTransition) {
- decorView.getViewTreeObserver().removeOnPreDrawListener(this);
+ if (viewTreeObserver.isAlive()) {
+ viewTreeObserver.removeOnPreDrawListener(this);
+ } else {
+ decorView.getViewTreeObserver().removeOnPreDrawListener(this);
+ }
}
return mIsReadyForTransition;
}
@@ -147,16 +153,10 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
(sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
viewsReady(sharedElements);
} else {
- mViewsReadyListener = new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- mViewsReadyListener = null;
- decor.getViewTreeObserver().removeOnPreDrawListener(this);
- viewsReady(sharedElements);
- return true;
- }
- };
- decor.getViewTreeObserver().addOnPreDrawListener(mViewsReadyListener);
+ mViewsReadyListener = OneShotPreDrawListener.add(decor, () -> {
+ mViewsReadyListener = null;
+ viewsReady(sharedElements);
+ });
decor.invalidate();
}
}
@@ -206,19 +206,13 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
moveSharedElementsToOverlay();
mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
} else if (decorView != null) {
- decorView.getViewTreeObserver()
- .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- decorView.getViewTreeObserver().removeOnPreDrawListener(this);
- if (mResultReceiver != null) {
- Bundle state = captureSharedElementState();
- moveSharedElementsToOverlay();
- mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
- }
- return true;
- }
- });
+ OneShotPreDrawListener.add(decorView, () -> {
+ if (mResultReceiver != null) {
+ Bundle state = captureSharedElementState();
+ moveSharedElementsToOverlay();
+ mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
+ }
+ });
}
if (allowOverlappingTransitions()) {
startEnterTransitionOnly();
@@ -271,7 +265,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
mIsReadyForTransition = true;
final ViewGroup decor = getDecor();
if (decor != null && mViewsReadyListener != null) {
- decor.getViewTreeObserver().removeOnPreDrawListener(mViewsReadyListener);
+ mViewsReadyListener.removeListener();
mViewsReadyListener = null;
}
showViews(mTransitioningViews, true);
@@ -457,20 +451,11 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
public void onSharedElementsReady() {
final View decorView = getDecor();
if (decorView != null) {
- decorView.getViewTreeObserver()
- .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- decorView.getViewTreeObserver().removeOnPreDrawListener(this);
- startTransition(new Runnable() {
- @Override
- public void run() {
- startSharedElementTransition(sharedElementState);
- }
- });
- return false;
- }
- });
+ OneShotPreDrawListener.add(decorView, () -> {
+ startTransition(() -> {
+ startSharedElementTransition(sharedElementState);
+ });
+ });
decorView.invalidate();
}
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index b5b6e4aa1892..6a79e6cfdf9c 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -34,9 +34,10 @@ import android.transition.Transition;
import android.transition.TransitionManager;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import android.view.Window;
+import com.android.internal.view.OneShotPreDrawListener;
+
import java.util.ArrayList;
/**
@@ -168,15 +169,9 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
});
final ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
mSharedElementNames);
- decorView.getViewTreeObserver()
- .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- decorView.getViewTreeObserver().removeOnPreDrawListener(this);
- setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
- return true;
- }
- });
+ OneShotPreDrawListener.add(decorView, () -> {
+ setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
+ });
setGhostVisibility(View.INVISIBLE);
scheduleGhostVisibilityChange(View.INVISIBLE);
if (mListener != null) {
diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java
index d27dff5e2227..088fd08d97a8 100644
--- a/core/java/android/app/FragmentTransition.java
+++ b/core/java/android/app/FragmentTransition.java
@@ -24,7 +24,8 @@ import android.util.ArrayMap;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
+
+import com.android.internal.view.OneShotPreDrawListener;
import java.util.ArrayList;
import java.util.Collection;
@@ -320,16 +321,9 @@ class FragmentTransition {
&& exitingFragment.mHidden && exitingFragment.mHiddenChanged) {
exitingFragment.setHideReplaced(true);
final View fragmentView = exitingFragment.getView();
- final ViewGroup container = exitingFragment.mContainer;
- container.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- container.getViewTreeObserver().removeOnPreDrawListener(this);
- setViewVisibility(exitingViews, View.INVISIBLE);
- return true;
- }
- });
+ OneShotPreDrawListener.add(exitingFragment.mContainer, () -> {
+ setViewVisibility(exitingViews, View.INVISIBLE);
+ });
exitTransition.addListener(new Transition.TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
@@ -364,30 +358,22 @@ class FragmentTransition {
final Transition enterTransition, final ArrayList<View> enteringViews,
final Transition exitTransition, final ArrayList<View> exitingViews) {
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-
- if (enterTransition != null) {
- enterTransition.removeTarget(nonExistentView);
- ArrayList<View> views = configureEnteringExitingViews(
- enterTransition, inFragment, sharedElementsIn, nonExistentView);
- enteringViews.addAll(views);
- }
-
- if (exitingViews != null) {
- ArrayList<View> tempExiting = new ArrayList<>();
- tempExiting.add(nonExistentView);
- replaceTargets(exitTransition, exitingViews, tempExiting);
- exitingViews.clear();
- exitingViews.add(nonExistentView);
- }
+ OneShotPreDrawListener.add(sceneRoot, () -> {
+ if (enterTransition != null) {
+ enterTransition.removeTarget(nonExistentView);
+ ArrayList<View> views = configureEnteringExitingViews(
+ enterTransition, inFragment, sharedElementsIn, nonExistentView);
+ enteringViews.addAll(views);
+ }
- return true;
- }
- });
+ if (exitingViews != null) {
+ ArrayList<View> tempExiting = new ArrayList<>();
+ tempExiting.add(nonExistentView);
+ replaceTargets(exitTransition, exitingViews, tempExiting);
+ exitingViews.clear();
+ exitingViews.add(nonExistentView);
+ }
+ });
}
/**
@@ -541,19 +527,13 @@ class FragmentTransition {
epicenterView = null;
}
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
- callSharedElementStartEnd(inFragment, outFragment, inIsPop,
- inSharedElements, false);
- if (epicenterView != null) {
- epicenterView.getBoundsOnScreen(epicenter);
- }
- return true;
- }
- });
+ OneShotPreDrawListener.add(sceneRoot, () -> {
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+ inSharedElements, false);
+ if (epicenterView != null) {
+ epicenterView.getBoundsOnScreen(epicenter);
+ }
+ });
return sharedElementTransition;
}
@@ -643,36 +623,30 @@ class FragmentTransition {
TransitionSet finalSharedElementTransition = sharedElementTransition;
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
- ArrayMap<String, View> inSharedElements = captureInSharedElements(
- nameOverrides, finalSharedElementTransition, fragments);
-
- if (inSharedElements != null) {
- sharedElementsIn.addAll(inSharedElements.values());
- sharedElementsIn.add(nonExistentView);
- }
+ OneShotPreDrawListener.add(sceneRoot, () -> {
+ ArrayMap<String, View> inSharedElements = captureInSharedElements(
+ nameOverrides, finalSharedElementTransition, fragments);
- callSharedElementStartEnd(inFragment, outFragment, inIsPop,
- inSharedElements, false);
- if (finalSharedElementTransition != null) {
- finalSharedElementTransition.getTargets().clear();
- finalSharedElementTransition.getTargets().addAll(sharedElementsIn);
- replaceTargets(finalSharedElementTransition, sharedElementsOut,
- sharedElementsIn);
-
- final View inEpicenterView = getInEpicenterView(inSharedElements,
- fragments, enterTransition, inIsPop);
- if (inEpicenterView != null) {
- inEpicenterView.getBoundsOnScreen(inEpicenter);
- }
- }
- return true;
- }
- });
+ if (inSharedElements != null) {
+ sharedElementsIn.addAll(inSharedElements.values());
+ sharedElementsIn.add(nonExistentView);
+ }
+
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+ inSharedElements, false);
+ if (finalSharedElementTransition != null) {
+ finalSharedElementTransition.getTargets().clear();
+ finalSharedElementTransition.getTargets().addAll(sharedElementsIn);
+ replaceTargets(finalSharedElementTransition, sharedElementsOut,
+ sharedElementsIn);
+
+ final View inEpicenterView = getInEpicenterView(inSharedElements,
+ fragments, enterTransition, inIsPop);
+ if (inEpicenterView != null) {
+ inEpicenterView.getBoundsOnScreen(inEpicenter);
+ }
+ }
+ });
return sharedElementTransition;
}
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 479f49368191..6e4d78ded4f2 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -242,14 +242,20 @@ public class TransitionManager {
Transition mTransition;
ViewGroup mSceneRoot;
+ final ViewTreeObserver mViewTreeObserver;
MultiListener(Transition transition, ViewGroup sceneRoot) {
mTransition = transition;
mSceneRoot = sceneRoot;
+ mViewTreeObserver = mSceneRoot.getViewTreeObserver();
}
private void removeListeners() {
- mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ if (mViewTreeObserver.isAlive()) {
+ mViewTreeObserver.removeOnPreDrawListener(this);
+ } else {
+ mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ }
mSceneRoot.removeOnAttachStateChangeListener(this);
}
diff --git a/core/java/com/android/internal/view/OneShotPreDrawListener.java b/core/java/com/android/internal/view/OneShotPreDrawListener.java
new file mode 100644
index 000000000000..98ffd82af887
--- /dev/null
+++ b/core/java/com/android/internal/view/OneShotPreDrawListener.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.view;
+
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+/**
+ * An OnPreDrawListener that will remove itself after one OnPreDraw call. Typical
+ * usage is:
+ * <pre><code>
+ * OneShotPreDrawListener.add(view, () -> { view.doSomething(); })
+ * </code></pre>
+ * <p>
+ * The onPreDraw always returns true.
+ * <p>
+ * The listener will also remove itself from the ViewTreeObserver when the view
+ * is detached from the view hierarchy. In that case, the Runnable will never be
+ * executed.
+ */
+public class OneShotPreDrawListener implements ViewTreeObserver.OnPreDrawListener,
+ View.OnAttachStateChangeListener {
+ private final View mView;
+ private ViewTreeObserver mViewTreeObserver;
+ private final Runnable mRunnable;
+
+ private OneShotPreDrawListener(View view, Runnable runnable) {
+ mView = view;
+ mViewTreeObserver = view.getViewTreeObserver();
+ mRunnable = runnable;
+ }
+
+ /**
+ * Creates a OneShotPreDrawListener and adds it to view's ViewTreeObserver.
+ * @param view The view whose ViewTreeObserver the OnPreDrawListener should listen.
+ * @param runnable The Runnable to execute in the OnPreDraw (once)
+ * @return The added OneShotPreDrawListener. It can be removed prior to
+ * the onPreDraw by calling {@link #removeListener()}.
+ */
+ public static OneShotPreDrawListener add(View view, Runnable runnable) {
+ OneShotPreDrawListener listener = new OneShotPreDrawListener(view, runnable);
+ view.getViewTreeObserver().addOnPreDrawListener(listener);
+ view.addOnAttachStateChangeListener(listener);
+ return listener;
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ removeListener();
+ mRunnable.run();
+ return true;
+ }
+
+ /**
+ * Removes the listener from the ViewTreeObserver. This is useful to call if the
+ * callback should be removed prior to {@link #onPreDraw()}.
+ */
+ public void removeListener() {
+ if (mViewTreeObserver.isAlive()) {
+ mViewTreeObserver.removeOnPreDrawListener(this);
+ } else {
+ mView.getViewTreeObserver().removeOnPreDrawListener(this);
+ }
+ mView.removeOnAttachStateChangeListener(this);
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mViewTreeObserver = v.getViewTreeObserver();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ removeListener();
+ }
+}