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