1 /* 2 * Copyright (C) 2022 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.permissioncontroller.safetycenter.ui 18 19 import android.graphics.Rect 20 import android.os.Build 21 import android.view.TouchDelegate 22 import android.view.View 23 import androidx.annotation.DimenRes 24 import androidx.annotation.RequiresApi 25 import com.android.permission.flags.Flags 26 27 /** Class to configure touch targets for Safety Center. */ 28 @RequiresApi(Build.VERSION_CODES.TIRAMISU) 29 object SafetyCenterTouchTarget { 30 /** 31 * Resizes the touch target of views by delegating to the parent component. 32 * 33 * @param view component that will be expanded 34 * @param minTouchTargetSizeResource required minimum touch target size 35 */ 36 @JvmStatic configureSizenull37 fun configureSize(view: View, @DimenRes minTouchTargetSizeResource: Int) { 38 configureSize(view, minTouchTargetSizeResource, useWidthHeightFix = false) 39 } 40 41 @JvmStatic configureSizenull42 fun configureSize( 43 view: View, 44 @DimenRes minTouchTargetSizeResource: Int, 45 useWidthHeightFix: Boolean, 46 ) { 47 if (useWidthHeightFix || Flags.fixSafetyCenterTouchTarget()) { 48 configureSizeUsingBothWidthAndHeight(view, minTouchTargetSizeResource) 49 } else { 50 configureSizeUsingWidthOnly(view, minTouchTargetSizeResource) 51 } 52 } 53 configureSizeUsingWidthOnlynull54 private fun configureSizeUsingWidthOnly(view: View, @DimenRes minTouchTargetSizeResource: Int) { 55 val parent = view.parent as View 56 val res = view.context.resources 57 val minTouchTargetSize = res.getDimensionPixelSize(minTouchTargetSizeResource) 58 59 // Defer getHitRect so that it's called after the parent's children are laid out. 60 parent.post { 61 val hitRect = Rect() 62 view.getHitRect(hitRect) 63 val currentTouchTargetWidth = hitRect.width() 64 if (currentTouchTargetWidth < minTouchTargetSize) { 65 // Divide width difference by two to get adjustment 66 val adjustInsetBy = (minTouchTargetSize - currentTouchTargetWidth) / 2 67 68 // Inset adjustment is applied to top, bottom, left, right 69 hitRect.inset(-adjustInsetBy, -adjustInsetBy) 70 parent.touchDelegate = TouchDelegate(hitRect, view) 71 } 72 } 73 } 74 configureSizeUsingBothWidthAndHeightnull75 private fun configureSizeUsingBothWidthAndHeight( 76 view: View, 77 @DimenRes minTouchTargetSizeResource: Int, 78 ) { 79 val parent = view.parent as View 80 val res = view.context.resources 81 val minTouchTargetSize = res.getDimensionPixelSize(minTouchTargetSizeResource) 82 83 // Defer getHitRect so that it's called after the parent's children are laid out. 84 parent.post { 85 val hitRect = Rect() 86 view.getHitRect(hitRect) 87 val currentTouchTargetWidth = hitRect.width() 88 val adjustWidthInsetBy = 89 if (currentTouchTargetWidth < minTouchTargetSize) { 90 (minTouchTargetSize - currentTouchTargetWidth) / 2 91 } else { 92 0 93 } 94 val currentTouchTargetHeight = hitRect.height() 95 val adjustHeightInsetBy = 96 if (currentTouchTargetHeight < minTouchTargetSize) { 97 (minTouchTargetSize - currentTouchTargetHeight) / 2 98 } else { 99 0 100 } 101 if (adjustWidthInsetBy != 0 || adjustHeightInsetBy != 0) { 102 hitRect.inset(-adjustWidthInsetBy, -adjustHeightInsetBy) 103 parent.touchDelegate = TouchDelegate(hitRect, view) 104 } 105 } 106 } 107 } 108