|  | /* | 
|  | * Copyright (C) 2021 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; | 
|  |  | 
|  | import android.annotation.NonNull; | 
|  | import android.annotation.Nullable; | 
|  | import android.graphics.Rect; | 
|  |  | 
|  | import com.android.internal.util.Preconditions; | 
|  |  | 
|  | import java.lang.ref.WeakReference; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Collections; | 
|  | import java.util.Iterator; | 
|  | import java.util.List; | 
|  | import java.util.function.Function; | 
|  |  | 
|  | /** | 
|  | * Abstract class to track a collection of rects reported by the views under the same | 
|  | * {@link ViewRootImpl}. | 
|  | */ | 
|  | class ViewRootRectTracker { | 
|  | private final Function<View, List<Rect>> mRectCollector; | 
|  | private boolean mViewsChanged = false; | 
|  | private boolean mRootRectsChanged = false; | 
|  | private List<Rect> mRootRects = Collections.emptyList(); | 
|  | private List<ViewInfo> mViewInfos = new ArrayList<>(); | 
|  | private List<Rect> mRects = Collections.emptyList(); | 
|  |  | 
|  | /** | 
|  | * @param rectCollector given a view returns a list of the rects of interest for this | 
|  | *                      ViewRootRectTracker | 
|  | */ | 
|  | ViewRootRectTracker(Function<View, List<Rect>> rectCollector) { | 
|  | mRectCollector = rectCollector; | 
|  | } | 
|  |  | 
|  | public void updateRectsForView(@NonNull View view) { | 
|  | boolean found = false; | 
|  | final Iterator<ViewInfo> i = mViewInfos.iterator(); | 
|  | while (i.hasNext()) { | 
|  | final ViewInfo info = i.next(); | 
|  | final View v = info.getView(); | 
|  | if (v == null || !v.isAttachedToWindow() || !v.isAggregatedVisible()) { | 
|  | mViewsChanged = true; | 
|  | i.remove(); | 
|  | continue; | 
|  | } | 
|  | if (v == view) { | 
|  | found = true; | 
|  | info.mDirty = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (!found && view.isAttachedToWindow()) { | 
|  | mViewInfos.add(new ViewInfo(view)); | 
|  | mViewsChanged = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return all Rects from all visible Views in the global (root) coordinate system, | 
|  | * or {@code null} if Rects are unchanged since the last call to this method. | 
|  | */ | 
|  | @Nullable | 
|  | public List<Rect> computeChangedRects() { | 
|  | if (computeChanges()) { | 
|  | return mRects; | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Computes changes to all Rects from all Views. | 
|  | * After calling this method, the updated list of Rects can be retrieved | 
|  | * with {@link #getLastComputedRects()}. | 
|  | * | 
|  | * @return {@code true} if there were changes, {@code false} otherwise. | 
|  | */ | 
|  | public boolean computeChanges() { | 
|  | boolean changed = mRootRectsChanged; | 
|  | final Iterator<ViewInfo> i = mViewInfos.iterator(); | 
|  | final List<Rect> rects = new ArrayList<>(mRootRects); | 
|  | while (i.hasNext()) { | 
|  | final ViewInfo info = i.next(); | 
|  | switch (info.update()) { | 
|  | case ViewInfo.CHANGED: | 
|  | changed = true; | 
|  | // Deliberate fall-through | 
|  | case ViewInfo.UNCHANGED: | 
|  | rects.addAll(info.mRects); | 
|  | break; | 
|  | case ViewInfo.GONE: | 
|  | mViewsChanged = true; | 
|  | i.remove(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (changed || mViewsChanged) { | 
|  | mViewsChanged = false; | 
|  | mRootRectsChanged = false; | 
|  | if (!mRects.equals(rects)) { | 
|  | mRects = rects; | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns a List of all Rects from all visible Views in the global (root) coordinate system. | 
|  | * This list is only updated when calling {@link #computeChanges()} or | 
|  | * {@link #computeChangedRects()}. | 
|  | * | 
|  | * @return all Rects from all visible Views in the global (root) coordinate system | 
|  | */ | 
|  | @NonNull | 
|  | public List<Rect> getLastComputedRects() { | 
|  | return mRects; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Sets rects defined in the global (root) coordinate system, i.e. not for a specific view. | 
|  | */ | 
|  | public void setRootRects(@NonNull List<Rect> rects) { | 
|  | Preconditions.checkNotNull(rects, "rects must not be null"); | 
|  | mRootRects = rects; | 
|  | mRootRectsChanged = true; | 
|  | } | 
|  |  | 
|  | @NonNull | 
|  | public List<Rect> getRootRects() { | 
|  | return mRootRects; | 
|  | } | 
|  |  | 
|  | @NonNull | 
|  | private List<Rect> getTrackedRectsForView(@NonNull View v) { | 
|  | final List<Rect> rects = mRectCollector.apply(v); | 
|  | return rects == null ? Collections.emptyList() : rects; | 
|  | } | 
|  |  | 
|  | private class ViewInfo { | 
|  | public static final int CHANGED = 0; | 
|  | public static final int UNCHANGED = 1; | 
|  | public static final int GONE = 2; | 
|  |  | 
|  | private final WeakReference<View> mView; | 
|  | boolean mDirty = true; | 
|  | List<Rect> mRects = Collections.emptyList(); | 
|  |  | 
|  | ViewInfo(View view) { | 
|  | mView = new WeakReference<>(view); | 
|  | } | 
|  |  | 
|  | public View getView() { | 
|  | return mView.get(); | 
|  | } | 
|  |  | 
|  | public int update() { | 
|  | final View view = getView(); | 
|  | if (view == null || !view.isAttachedToWindow() | 
|  | || !view.isAggregatedVisible()) return GONE; | 
|  | final List<Rect> localRects = getTrackedRectsForView(view); | 
|  | final List<Rect> newRects = new ArrayList<>(localRects.size()); | 
|  | for (Rect src : localRects) { | 
|  | Rect mappedRect = new Rect(src); | 
|  | ViewParent p = view.getParent(); | 
|  | if (p != null && p.getChildVisibleRect(view, mappedRect, null)) { | 
|  | newRects.add(mappedRect); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (mRects.equals(localRects)) return UNCHANGED; | 
|  | mRects = newRects; | 
|  | return CHANGED; | 
|  | } | 
|  | } | 
|  | } |