Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f0b46b95 authored by George Mount's avatar George Mount
Browse files

Use safe access to OnPreDrawListener.

Bug 32472451

It is important to remove an OnPreDrawListener from the correct
ViewTreeObserver. When a View is added to the view hierarchy, the
initial ViewTreeObserver is not active. The listener must then be
removed from the current OnPreDrawListener. When a View has been
removed from the hierarchy, it is important to remove the listener
from the orignal ViewTreeObserver.

Test: cts-tradefed run singleCommand cts -d --skip-preconditions
--skip-connectivity-check -m CtsUsageStatsTestCases
Test: cts-tradefed run singleCommand cts -d --skip-preconditions
--skip-connectivity-check -m CtsFragmentTestCases

Change-Id: I735f71d2d9c84e86ef846aab0088a8651300fbe8
parent 5ddd7172
Loading
Loading
Loading
Loading
+34 −23
Original line number Original line Diff line number Diff line
@@ -37,6 +37,8 @@ import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.Window;
import android.widget.ImageView;
import android.widget.ImageView;


import com.android.internal.view.OneShotPreDrawListener;

import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collection;


@@ -570,16 +572,9 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
    protected void scheduleSetSharedElementEnd(final ArrayList<View> snapshots) {
    protected void scheduleSetSharedElementEnd(final ArrayList<View> snapshots) {
        final View decorView = getDecor();
        final View decorView = getDecor();
        if (decorView != null) {
        if (decorView != null) {
            decorView.getViewTreeObserver().addOnPreDrawListener(
            OneShotPreDrawListener.add(decorView, () -> {
                    new ViewTreeObserver.OnPreDrawListener() {
                        @Override
                        public boolean onPreDraw() {
                            decorView.getViewTreeObserver().removeOnPreDrawListener(this);
                notifySharedElementEnd(snapshots);
                notifySharedElementEnd(snapshots);
                            return true;
            });
                        }
                    }
            );
        }
        }
    }
    }


@@ -816,6 +811,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
                if (moveWithParent && !isInTransitionGroup(parent, decor)) {
                if (moveWithParent && !isInTransitionGroup(parent, decor)) {
                    GhostViewListeners listener = new GhostViewListeners(view, parent, decor);
                    GhostViewListeners listener = new GhostViewListeners(view, parent, decor);
                    parent.getViewTreeObserver().addOnPreDrawListener(listener);
                    parent.getViewTreeObserver().addOnPreDrawListener(listener);
                    parent.addOnAttachStateChangeListener(listener);
                    mGhostViewListeners.add(listener);
                    mGhostViewListeners.add(listener);
                }
                }
            }
            }
@@ -842,8 +838,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
        int numListeners = mGhostViewListeners.size();
        int numListeners = mGhostViewListeners.size();
        for (int i = 0; i < numListeners; i++) {
        for (int i = 0; i < numListeners; i++) {
            GhostViewListeners listener = mGhostViewListeners.get(i);
            GhostViewListeners listener = mGhostViewListeners.get(i);
            ViewGroup parent = (ViewGroup) listener.getView().getParent();
            listener.removeListener();
            parent.getViewTreeObserver().removeOnPreDrawListener(listener);
        }
        }
        mGhostViewListeners.clear();
        mGhostViewListeners.clear();


@@ -874,14 +869,8 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
    protected void scheduleGhostVisibilityChange(final int visibility) {
    protected void scheduleGhostVisibilityChange(final int visibility) {
        final View decorView = getDecor();
        final View decorView = getDecor();
        if (decorView != null) {
        if (decorView != null) {
            decorView.getViewTreeObserver()
            OneShotPreDrawListener.add(decorView, () -> {
                    .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                        @Override
                        public boolean onPreDraw() {
                            decorView.getViewTreeObserver().removeOnPreDrawListener(this);
                setGhostVisibility(visibility);
                setGhostVisibility(visibility);
                            return true;
                        }
            });
            });
        }
        }
    }
    }
@@ -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 View mView;
        private ViewGroup mDecor;
        private ViewGroup mDecor;
        private View mParent;
        private View mParent;
        private Matrix mMatrix = new Matrix();
        private Matrix mMatrix = new Matrix();
        private ViewTreeObserver mViewTreeObserver;


        public GhostViewListeners(View view, View parent, ViewGroup decor) {
        public GhostViewListeners(View view, View parent, ViewGroup decor) {
            mView = view;
            mView = view;
            mParent = parent;
            mParent = parent;
            mDecor = decor;
            mDecor = decor;
            mViewTreeObserver = parent.getViewTreeObserver();
        }
        }


        public View getView() {
        public View getView() {
@@ -1008,13 +1000,32 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
        public boolean onPreDraw() {
        public boolean onPreDraw() {
            GhostView ghostView = GhostView.getGhost(mView);
            GhostView ghostView = GhostView.getGhost(mView);
            if (ghostView == null) {
            if (ghostView == null) {
                mParent.getViewTreeObserver().removeOnPreDrawListener(this);
                removeListener();
            } else {
            } else {
                GhostView.calculateMatrix(mView, mDecor, mMatrix);
                GhostView.calculateMatrix(mView, mDecor, mMatrix);
                ghostView.setMatrix(mMatrix);
                ghostView.setMatrix(mMatrix);
            }
            }
            return true;
            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 {
    static class SharedElementOriginalState {
+8 −13
Original line number Original line Diff line number Diff line
@@ -22,9 +22,10 @@ import android.transition.Transition;
import android.util.SparseArray;
import android.util.SparseArray;
import android.view.View;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.Window;


import com.android.internal.view.OneShotPreDrawListener;

import java.lang.ref.WeakReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.ArrayList;


@@ -321,17 +322,11 @@ class ActivityTransitionState {
                }
                }
                if (delayExitBack && decor != null) {
                if (delayExitBack && decor != null) {
                    final ViewGroup finalDecor = decor;
                    final ViewGroup finalDecor = decor;
                    decor.getViewTreeObserver().addOnPreDrawListener(
                    OneShotPreDrawListener.add(decor, () -> {
                            new ViewTreeObserver.OnPreDrawListener() {
                                @Override
                                public boolean onPreDraw() {
                                    finalDecor.getViewTreeObserver().removeOnPreDrawListener(this);
                        if (mReturnExitCoordinator != null) {
                        if (mReturnExitCoordinator != null) {
                            mReturnExitCoordinator.startExit(activity.mResultCode,
                            mReturnExitCoordinator.startExit(activity.mResultCode,
                                    activity.mResultData);
                                    activity.mResultData);
                        }
                        }
                                    return true;
                                }
                    });
                    });
                } else {
                } else {
                    mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
                    mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
+27 −42
Original line number Original line Diff line number Diff line
@@ -30,10 +30,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup;
import android.view.ViewGroupOverlay;
import android.view.ViewGroupOverlay;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.Window;
import android.view.Window;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEvent;


import com.android.internal.view.OneShotPreDrawListener;

import java.util.ArrayList;
import java.util.ArrayList;


/**
/**
@@ -58,7 +59,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
    private boolean mAreViewsReady;
    private boolean mAreViewsReady;
    private boolean mIsViewsTransitionStarted;
    private boolean mIsViewsTransitionStarted;
    private Transition mEnterViewsTransition;
    private Transition mEnterViewsTransition;
    private OnPreDrawListener mViewsReadyListener;
    private OneShotPreDrawListener mViewsReadyListener;
    private final boolean mIsCrossTask;
    private final boolean mIsCrossTask;


    public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
    public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
@@ -74,13 +75,18 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
        mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
        mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
        final View decorView = getDecor();
        final View decorView = getDecor();
        if (decorView != null) {
        if (decorView != null) {
            decorView.getViewTreeObserver().addOnPreDrawListener(
            final ViewTreeObserver viewTreeObserver = decorView.getViewTreeObserver();
            viewTreeObserver.addOnPreDrawListener(
                    new ViewTreeObserver.OnPreDrawListener() {
                    new ViewTreeObserver.OnPreDrawListener() {
                        @Override
                        @Override
                        public boolean onPreDraw() {
                        public boolean onPreDraw() {
                            if (mIsReadyForTransition) {
                            if (mIsReadyForTransition) {
                                if (viewTreeObserver.isAlive()) {
                                    viewTreeObserver.removeOnPreDrawListener(this);
                                } else {
                                    decorView.getViewTreeObserver().removeOnPreDrawListener(this);
                                    decorView.getViewTreeObserver().removeOnPreDrawListener(this);
                                }
                                }
                            }
                            return mIsReadyForTransition;
                            return mIsReadyForTransition;
                        }
                        }
                    });
                    });
@@ -147,16 +153,10 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
                (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
                (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
            viewsReady(sharedElements);
            viewsReady(sharedElements);
        } else {
        } else {
            mViewsReadyListener = new ViewTreeObserver.OnPreDrawListener() {
            mViewsReadyListener = OneShotPreDrawListener.add(decor, () -> {
                @Override
                public boolean onPreDraw() {
                mViewsReadyListener = null;
                mViewsReadyListener = null;
                    decor.getViewTreeObserver().removeOnPreDrawListener(this);
                viewsReady(sharedElements);
                viewsReady(sharedElements);
                    return true;
            });
                }
            };
            decor.getViewTreeObserver().addOnPreDrawListener(mViewsReadyListener);
            decor.invalidate();
            decor.invalidate();
        }
        }
    }
    }
@@ -206,18 +206,12 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
            moveSharedElementsToOverlay();
            moveSharedElementsToOverlay();
            mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
            mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
        } else if (decorView != null) {
        } else if (decorView != null) {
            decorView.getViewTreeObserver()
            OneShotPreDrawListener.add(decorView, () -> {
                    .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                        @Override
                        public boolean onPreDraw() {
                            decorView.getViewTreeObserver().removeOnPreDrawListener(this);
                if (mResultReceiver != null) {
                if (mResultReceiver != null) {
                    Bundle state = captureSharedElementState();
                    Bundle state = captureSharedElementState();
                    moveSharedElementsToOverlay();
                    moveSharedElementsToOverlay();
                    mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
                    mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
                }
                }
                            return true;
                        }
            });
            });
        }
        }
        if (allowOverlappingTransitions()) {
        if (allowOverlappingTransitions()) {
@@ -271,7 +265,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
            mIsReadyForTransition = true;
            mIsReadyForTransition = true;
            final ViewGroup decor = getDecor();
            final ViewGroup decor = getDecor();
            if (decor != null && mViewsReadyListener != null) {
            if (decor != null && mViewsReadyListener != null) {
                decor.getViewTreeObserver().removeOnPreDrawListener(mViewsReadyListener);
                mViewsReadyListener.removeListener();
                mViewsReadyListener = null;
                mViewsReadyListener = null;
            }
            }
            showViews(mTransitioningViews, true);
            showViews(mTransitioningViews, true);
@@ -457,19 +451,10 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
            public void onSharedElementsReady() {
            public void onSharedElementsReady() {
                final View decorView = getDecor();
                final View decorView = getDecor();
                if (decorView != null) {
                if (decorView != null) {
                    decorView.getViewTreeObserver()
                    OneShotPreDrawListener.add(decorView, () -> {
                            .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                        startTransition(() -> {
                                @Override
                                public boolean onPreDraw() {
                                    decorView.getViewTreeObserver().removeOnPreDrawListener(this);
                                    startTransition(new Runnable() {
                                        @Override
                                        public void run() {
                                startSharedElementTransition(sharedElementState);
                                startSharedElementTransition(sharedElementState);
                                        }
                        });
                        });
                                    return false;
                                }
                    });
                    });
                    decorView.invalidate();
                    decorView.invalidate();
                }
                }
+5 −10
Original line number Original line Diff line number Diff line
@@ -34,9 +34,10 @@ import android.transition.Transition;
import android.transition.TransitionManager;
import android.transition.TransitionManager;
import android.view.View;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.Window;


import com.android.internal.view.OneShotPreDrawListener;

import java.util.ArrayList;
import java.util.ArrayList;


/**
/**
@@ -168,14 +169,8 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
        });
        });
        final ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
        final ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
                mSharedElementNames);
                mSharedElementNames);
        decorView.getViewTreeObserver()
        OneShotPreDrawListener.add(decorView, () -> {
                .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        decorView.getViewTreeObserver().removeOnPreDrawListener(this);
            setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
            setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
                        return true;
                    }
        });
        });
        setGhostVisibility(View.INVISIBLE);
        setGhostVisibility(View.INVISIBLE);
        scheduleGhostVisibilityChange(View.INVISIBLE);
        scheduleGhostVisibilityChange(View.INVISIBLE);
+50 −76
Original line number Original line Diff line number Diff line
@@ -24,7 +24,8 @@ import android.util.ArrayMap;
import android.util.SparseArray;
import android.util.SparseArray;
import android.view.View;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;

import com.android.internal.view.OneShotPreDrawListener;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collection;
@@ -320,15 +321,8 @@ class FragmentTransition {
                && exitingFragment.mHidden && exitingFragment.mHiddenChanged) {
                && exitingFragment.mHidden && exitingFragment.mHiddenChanged) {
            exitingFragment.setHideReplaced(true);
            exitingFragment.setHideReplaced(true);
            final View fragmentView = exitingFragment.getView();
            final View fragmentView = exitingFragment.getView();
            final ViewGroup container = exitingFragment.mContainer;
            OneShotPreDrawListener.add(exitingFragment.mContainer, () -> {
            container.getViewTreeObserver().addOnPreDrawListener(
                    new ViewTreeObserver.OnPreDrawListener() {
                        @Override
                        public boolean onPreDraw() {
                            container.getViewTreeObserver().removeOnPreDrawListener(this);
                setViewVisibility(exitingViews, View.INVISIBLE);
                setViewVisibility(exitingViews, View.INVISIBLE);
                            return true;
                        }
            });
            });
            exitTransition.addListener(new Transition.TransitionListenerAdapter() {
            exitTransition.addListener(new Transition.TransitionListenerAdapter() {
                @Override
                @Override
@@ -364,12 +358,7 @@ class FragmentTransition {
            final Transition enterTransition, final ArrayList<View> enteringViews,
            final Transition enterTransition, final ArrayList<View> enteringViews,
            final Transition exitTransition, final ArrayList<View> exitingViews) {
            final Transition exitTransition, final ArrayList<View> exitingViews) {


        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
        OneShotPreDrawListener.add(sceneRoot, () -> {
                new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);

            if (enterTransition != null) {
            if (enterTransition != null) {
                enterTransition.removeTarget(nonExistentView);
                enterTransition.removeTarget(nonExistentView);
                ArrayList<View> views = configureEnteringExitingViews(
                ArrayList<View> views = configureEnteringExitingViews(
@@ -384,9 +373,6 @@ class FragmentTransition {
                exitingViews.clear();
                exitingViews.clear();
                exitingViews.add(nonExistentView);
                exitingViews.add(nonExistentView);
            }
            }

                        return true;
                    }
        });
        });
    }
    }


@@ -541,18 +527,12 @@ class FragmentTransition {
            epicenterView = null;
            epicenterView = null;
        }
        }


        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
        OneShotPreDrawListener.add(sceneRoot, () -> {
                new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
            callSharedElementStartEnd(inFragment, outFragment, inIsPop,
            callSharedElementStartEnd(inFragment, outFragment, inIsPop,
                    inSharedElements, false);
                    inSharedElements, false);
            if (epicenterView != null) {
            if (epicenterView != null) {
                epicenterView.getBoundsOnScreen(epicenter);
                epicenterView.getBoundsOnScreen(epicenter);
            }
            }
                        return true;
                    }
        });
        });
        return sharedElementTransition;
        return sharedElementTransition;
    }
    }
@@ -643,11 +623,7 @@ class FragmentTransition {


        TransitionSet finalSharedElementTransition = sharedElementTransition;
        TransitionSet finalSharedElementTransition = sharedElementTransition;


        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
        OneShotPreDrawListener.add(sceneRoot, () -> {
                new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
            ArrayMap<String, View> inSharedElements = captureInSharedElements(
            ArrayMap<String, View> inSharedElements = captureInSharedElements(
                    nameOverrides, finalSharedElementTransition, fragments);
                    nameOverrides, finalSharedElementTransition, fragments);


@@ -670,8 +646,6 @@ class FragmentTransition {
                    inEpicenterView.getBoundsOnScreen(inEpicenter);
                    inEpicenterView.getBoundsOnScreen(inEpicenter);
                }
                }
            }
            }
                        return true;
                    }
        });
        });
        return sharedElementTransition;
        return sharedElementTransition;
    }
    }
Loading