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