1 /* 2 * Copyright (C) 2023 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.car.builtin.view; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.annotation.UiThread; 23 import android.graphics.Rect; 24 import android.graphics.Region; 25 import android.view.View; 26 import android.view.ViewTreeObserver.InternalInsetsInfo; 27 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; 28 29 /** 30 * Calculates {@link InternalInsetsInfo#TOUCHABLE_INSETS_REGION} for the given {@link View}. 31 * <p>The touch events on the View will pass through the host and be delivered to the window 32 * below it. 33 * 34 * <p>It also provides the api {@link #setObscuredTouchRegion(Region)} to specify the region which 35 * the view host can accept the touch events on it. 36 * @hide 37 */ 38 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) 39 @UiThread 40 public final class TouchableInsetsProvider { 41 private static final String TAG = TouchableInsetsProvider.class.getSimpleName(); 42 private final View mView; 43 private final OnComputeInternalInsetsListener mListener = this::onComputeInternalInsets; 44 private final int[] mLocation = new int[2]; 45 private final Rect mRect = new Rect(); 46 47 @Nullable private Region mObscuredTouchRegion; 48 TouchableInsetsProvider(@onNull View view)49 public TouchableInsetsProvider(@NonNull View view) { 50 mView = view; 51 } 52 53 /** 54 * Specifies the region of the view which the view host can accept the touch events. 55 * 56 * @param obscuredRegion the obscured region of the view. 57 */ setObscuredTouchRegion(@ullable Region obscuredRegion)58 public void setObscuredTouchRegion(@Nullable Region obscuredRegion) { 59 mObscuredTouchRegion = obscuredRegion; 60 } 61 onComputeInternalInsets(InternalInsetsInfo inoutInfo)62 private void onComputeInternalInsets(InternalInsetsInfo inoutInfo) { 63 if (!mView.isVisibleToUser()) { 64 return; 65 } 66 if (inoutInfo.touchableRegion.isEmpty()) { 67 // This is the first View to set touchableRegion, then set the entire Window as 68 // touchableRegion first, then subtract each View's region from it. 69 inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 70 View root = mView.getRootView(); 71 root.getLocationInWindow(mLocation); 72 mRect.set(mLocation[0], mLocation[1], 73 mLocation[0] + root.getWidth(), mLocation[1] + root.getHeight()); 74 inoutInfo.touchableRegion.set(mRect); 75 } 76 mView.getLocationInWindow(mLocation); 77 mRect.set(mLocation[0], mLocation[1], 78 mLocation[0] + mView.getWidth(), mLocation[1] + mView.getHeight()); 79 inoutInfo.touchableRegion.op(mRect, Region.Op.DIFFERENCE); 80 81 if (mObscuredTouchRegion != null) { 82 inoutInfo.touchableRegion.op(mObscuredTouchRegion, Region.Op.UNION); 83 } 84 }; 85 86 /** Registers this to the internal insets computation callback. */ addToViewTreeObserver()87 public void addToViewTreeObserver() { 88 mView.getViewTreeObserver().addOnComputeInternalInsetsListener(mListener); 89 } 90 91 /** Removes this from the internal insets computation callback. */ removeFromViewTreeObserver()92 public void removeFromViewTreeObserver() { 93 mView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mListener); 94 } 95 96 @Override toString()97 public String toString() { 98 return TAG + "(rect=" + mRect + ", obscuredTouch=" + mObscuredTouchRegion + ")"; 99 } 100 } 101 102