1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.graphics.Rect;
22 
23 import com.android.internal.util.Preconditions;
24 
25 import java.lang.ref.WeakReference;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.Iterator;
29 import java.util.List;
30 
31 /**
32  * Used by {@link ViewRootImpl} to track system gesture exclusion rects reported by views.
33  */
34 class GestureExclusionTracker {
35     private boolean mGestureExclusionViewsChanged = false;
36     private boolean mRootGestureExclusionRectsChanged = false;
37     private List<Rect> mRootGestureExclusionRects = Collections.emptyList();
38     private List<GestureExclusionViewInfo> mGestureExclusionViewInfos = new ArrayList<>();
39     private List<Rect> mGestureExclusionRects = Collections.emptyList();
40 
updateRectsForView(@onNull View view)41     public void updateRectsForView(@NonNull View view) {
42         boolean found = false;
43         final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator();
44         while (i.hasNext()) {
45             final GestureExclusionViewInfo info = i.next();
46             final View v = info.getView();
47             if (v == null || !v.isAttachedToWindow() || !v.isAggregatedVisible()) {
48                 mGestureExclusionViewsChanged = true;
49                 i.remove();
50                 continue;
51             }
52             if (v == view) {
53                 found = true;
54                 info.mDirty = true;
55                 break;
56             }
57         }
58         if (!found && view.isAttachedToWindow()) {
59             mGestureExclusionViewInfos.add(new GestureExclusionViewInfo(view));
60             mGestureExclusionViewsChanged = true;
61         }
62     }
63 
64     @Nullable
computeChangedRects()65     public List<Rect> computeChangedRects() {
66         boolean changed = mRootGestureExclusionRectsChanged;
67         final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator();
68         final List<Rect> rects = new ArrayList<>(mRootGestureExclusionRects);
69         while (i.hasNext()) {
70             final GestureExclusionViewInfo info = i.next();
71             switch (info.update()) {
72                 case GestureExclusionViewInfo.CHANGED:
73                     changed = true;
74                     // Deliberate fall-through
75                 case GestureExclusionViewInfo.UNCHANGED:
76                     rects.addAll(info.mExclusionRects);
77                     break;
78                 case GestureExclusionViewInfo.GONE:
79                     mGestureExclusionViewsChanged = true;
80                     i.remove();
81                     break;
82             }
83         }
84         if (changed || mGestureExclusionViewsChanged) {
85             mGestureExclusionViewsChanged = false;
86             mRootGestureExclusionRectsChanged = false;
87             if (!mGestureExclusionRects.equals(rects)) {
88                 mGestureExclusionRects = rects;
89                 return rects;
90             }
91         }
92         return null;
93     }
94 
setRootSystemGestureExclusionRects(@onNull List<Rect> rects)95     public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) {
96         Preconditions.checkNotNull(rects, "rects must not be null");
97         mRootGestureExclusionRects = rects;
98         mRootGestureExclusionRectsChanged = true;
99     }
100 
101     @NonNull
getRootSystemGestureExclusionRects()102     public List<Rect> getRootSystemGestureExclusionRects() {
103         return mRootGestureExclusionRects;
104     }
105 
106     private static class GestureExclusionViewInfo {
107         public static final int CHANGED = 0;
108         public static final int UNCHANGED = 1;
109         public static final int GONE = 2;
110 
111         private final WeakReference<View> mView;
112         boolean mDirty = true;
113         List<Rect> mExclusionRects = Collections.emptyList();
114 
GestureExclusionViewInfo(View view)115         GestureExclusionViewInfo(View view) {
116             mView = new WeakReference<>(view);
117         }
118 
getView()119         public View getView() {
120             return mView.get();
121         }
122 
update()123         public int update() {
124             final View excludedView = getView();
125             if (excludedView == null || !excludedView.isAttachedToWindow()
126                     || !excludedView.isAggregatedVisible()) return GONE;
127             final List<Rect> localRects = excludedView.getSystemGestureExclusionRects();
128             final List<Rect> newRects = new ArrayList<>(localRects.size());
129             for (Rect src : localRects) {
130                 Rect mappedRect = new Rect(src);
131                 ViewParent p = excludedView.getParent();
132                 if (p != null && p.getChildVisibleRect(excludedView, mappedRect, null)) {
133                     newRects.add(mappedRect);
134                 }
135             }
136 
137             if (mExclusionRects.equals(localRects)) return UNCHANGED;
138             mExclusionRects = newRects;
139             return CHANGED;
140         }
141     }
142 }
143