1 /*
2  * Copyright (C) 2020 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 com.android.systemui.shared.system;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.graphics.Rect;
22 import android.graphics.Region;
23 import android.view.ViewTreeObserver;
24 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
25 
26 import java.util.HashMap;
27 
28 public class ViewTreeObserverWrapper {
29 
30     private static final HashMap<OnComputeInsetsListener, ViewTreeObserver>
31             sListenerObserverMap = new HashMap<>();
32     private static final HashMap<OnComputeInsetsListener, OnComputeInternalInsetsListener>
33             sListenerInternalListenerMap = new HashMap<>();
34 
35     /**
36      * Register a callback to be invoked when the invoked when it is time to compute the window's
37      * insets.
38      *
39      * @param observer The observer to be added
40      * @param listener The callback to add
41      * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
42      */
addOnComputeInsetsListener( @onNull ViewTreeObserver observer, @NonNull OnComputeInsetsListener listener)43     public static void addOnComputeInsetsListener(
44             @NonNull ViewTreeObserver observer, @NonNull OnComputeInsetsListener listener) {
45         final OnComputeInternalInsetsListener internalListener = internalInOutInfo -> {
46             final InsetsInfo inOutInfo = new InsetsInfo();
47             inOutInfo.contentInsets.set(internalInOutInfo.contentInsets);
48             inOutInfo.visibleInsets.set(internalInOutInfo.visibleInsets);
49             inOutInfo.touchableRegion.set(internalInOutInfo.touchableRegion);
50             listener.onComputeInsets(inOutInfo);
51             internalInOutInfo.contentInsets.set(inOutInfo.contentInsets);
52             internalInOutInfo.visibleInsets.set(inOutInfo.visibleInsets);
53             internalInOutInfo.touchableRegion.set(inOutInfo.touchableRegion);
54             internalInOutInfo.setTouchableInsets(inOutInfo.mTouchableInsets);
55         };
56         sListenerObserverMap.put(listener, observer);
57         sListenerInternalListenerMap.put(listener, internalListener);
58         observer.addOnComputeInternalInsetsListener(internalListener);
59     }
60 
61     /**
62      * Remove a previously installed insets computation callback.
63      *
64      * @param victim The callback to remove
65      * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
66      * @see #addOnComputeInsetsListener(ViewTreeObserver, OnComputeInsetsListener)
67      */
removeOnComputeInsetsListener(@onNull OnComputeInsetsListener victim)68     public static void removeOnComputeInsetsListener(@NonNull OnComputeInsetsListener victim) {
69         final ViewTreeObserver observer = sListenerObserverMap.get(victim);
70         final OnComputeInternalInsetsListener listener = sListenerInternalListenerMap.get(victim);
71         if (observer != null && listener != null) {
72             observer.removeOnComputeInternalInsetsListener(listener);
73         }
74         sListenerObserverMap.remove(victim);
75         sListenerInternalListenerMap.remove(victim);
76     }
77 
78     /**
79      * Interface definition for a callback to be invoked when layout has
80      * completed and the client can compute its interior insets.
81      */
82     public interface OnComputeInsetsListener {
83         /**
84          * Callback method to be invoked when layout has completed and the
85          * client can compute its interior insets.
86          *
87          * @param inoutInfo Should be filled in by the implementation with
88          * the information about the insets of the window.  This is called
89          * with whatever values the previous OnComputeInsetsListener
90          * returned, if there are multiple such listeners in the window.
91          */
onComputeInsets(InsetsInfo inoutInfo)92         void onComputeInsets(InsetsInfo inoutInfo);
93     }
94 
95     /**
96      * Parameters used with OnComputeInsetsListener.
97      */
98     public final static class InsetsInfo {
99 
100         /**
101          * Offsets from the frame of the window at which the content of
102          * windows behind it should be placed.
103          */
104         public final Rect contentInsets = new Rect();
105 
106         /**
107          * Offsets from the frame of the window at which windows behind it
108          * are visible.
109          */
110         public final Rect visibleInsets = new Rect();
111 
112         /**
113          * Touchable region defined relative to the origin of the frame of the window.
114          * Only used when {@link #setTouchableInsets(int)} is called with
115          * the option {@link #TOUCHABLE_INSETS_REGION}.
116          */
117         public final Region touchableRegion = new Region();
118 
119         /**
120          * Option for {@link #setTouchableInsets(int)}: the entire window frame
121          * can be touched.
122          */
123         public static final int TOUCHABLE_INSETS_FRAME =
124                 ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
125 
126         /**
127          * Option for {@link #setTouchableInsets(int)}: the area inside of
128          * the content insets can be touched.
129          */
130         public static final int TOUCHABLE_INSETS_CONTENT =
131                 ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
132 
133         /**
134          * Option for {@link #setTouchableInsets(int)}: the area inside of
135          * the visible insets can be touched.
136          */
137         public static final int TOUCHABLE_INSETS_VISIBLE =
138                 ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
139 
140         /**
141          * Option for {@link #setTouchableInsets(int)}: the area inside of
142          * the provided touchable region in {@link #touchableRegion} can be touched.
143          */
144         public static final int TOUCHABLE_INSETS_REGION =
145                 ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
146 
147         /**
148          * Set which parts of the window can be touched: either
149          * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
150          * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
151          */
setTouchableInsets(int val)152         public void setTouchableInsets(int val) {
153             mTouchableInsets = val;
154         }
155 
156         int mTouchableInsets;
157 
158         @Override
hashCode()159         public int hashCode() {
160             int result = contentInsets.hashCode();
161             result = 31 * result + visibleInsets.hashCode();
162             result = 31 * result + touchableRegion.hashCode();
163             result = 31 * result + mTouchableInsets;
164             return result;
165         }
166 
167         @Override
equals(@ullable Object o)168         public boolean equals(@Nullable Object o) {
169             if (this == o) return true;
170             if (o == null || getClass() != o.getClass()) return false;
171 
172             final InsetsInfo other = (InsetsInfo) o;
173             return mTouchableInsets == other.mTouchableInsets &&
174                     contentInsets.equals(other.contentInsets) &&
175                     visibleInsets.equals(other.visibleInsets) &&
176                     touchableRegion.equals(other.touchableRegion);
177         }
178     }
179 }
180