1 /* <lambda>null2 * 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 18 package com.android.systemui.keyguard.ui.view.layout.sections 19 20 import android.content.Context 21 import android.graphics.Point 22 import android.graphics.Rect 23 import android.util.DisplayMetrics 24 import android.view.WindowManager 25 import androidx.annotation.VisibleForTesting 26 import androidx.constraintlayout.widget.ConstraintLayout 27 import androidx.constraintlayout.widget.ConstraintSet 28 import com.android.systemui.biometrics.AuthController 29 import com.android.systemui.customization.R as customR 30 import com.android.systemui.dagger.qualifiers.Application 31 import com.android.systemui.dagger.qualifiers.Main 32 import com.android.systemui.flags.FeatureFlags 33 import com.android.systemui.flags.Flags 34 import com.android.systemui.keyguard.shared.model.KeyguardSection 35 import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder 36 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView 37 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel 38 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel 39 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel 40 import com.android.systemui.log.LogBuffer 41 import com.android.systemui.log.TouchHandlingViewLogger 42 import com.android.systemui.log.core.Logger 43 import com.android.systemui.log.dagger.KeyguardBlueprintLog 44 import com.android.systemui.log.dagger.LongPressTouchLog 45 import com.android.systemui.plugins.FalsingManager 46 import com.android.systemui.res.R 47 import com.android.systemui.shade.NotificationPanelView 48 import com.android.systemui.shade.ShadeDisplayAware 49 import com.android.systemui.statusbar.VibratorHelper 50 import com.google.android.msdl.domain.MSDLPlayer 51 import dagger.Lazy 52 import javax.inject.Inject 53 import kotlinx.coroutines.CoroutineDispatcher 54 import kotlinx.coroutines.CoroutineScope 55 import kotlinx.coroutines.DisposableHandle 56 57 /** Includes the device entry icon. */ 58 class DefaultDeviceEntrySection 59 @Inject 60 constructor( 61 @Application private val applicationScope: CoroutineScope, 62 @Main private val mainDispatcher: CoroutineDispatcher, 63 private val authController: AuthController, 64 private val windowManager: WindowManager, 65 @ShadeDisplayAware private val context: Context, 66 private val notificationPanelView: NotificationPanelView, 67 private val featureFlags: FeatureFlags, 68 private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>, 69 private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>, 70 private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>, 71 private val falsingManager: Lazy<FalsingManager>, 72 private val vibratorHelper: Lazy<VibratorHelper>, 73 private val msdlPlayer: Lazy<MSDLPlayer>, 74 @LongPressTouchLog private val logBuffer: LogBuffer, 75 @KeyguardBlueprintLog blueprintLogBuffer: LogBuffer, 76 ) : KeyguardSection() { 77 private val blueprintLogger = Logger(blueprintLogBuffer, TAG) 78 private val deviceEntryIconViewId = R.id.device_entry_icon_view 79 private var disposableHandle: DisposableHandle? = null 80 81 override fun addViews(constraintLayout: ConstraintLayout) { 82 val view = 83 DeviceEntryIconView( 84 context, 85 null, 86 logger = TouchHandlingViewLogger(logBuffer = logBuffer, TAG), 87 ) 88 .apply { id = deviceEntryIconViewId } 89 90 constraintLayout.addView(view) 91 } 92 93 override fun bindData(constraintLayout: ConstraintLayout) { 94 constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let { 95 disposableHandle?.dispose() 96 disposableHandle = 97 DeviceEntryIconViewBinder.bind( 98 applicationScope, 99 mainDispatcher, 100 it, 101 deviceEntryIconViewModel.get(), 102 deviceEntryForegroundViewModel.get(), 103 deviceEntryBackgroundViewModel.get(), 104 falsingManager.get(), 105 vibratorHelper.get(), 106 msdlPlayer.get(), 107 ) 108 } 109 } 110 111 override fun applyConstraints(constraintSet: ConstraintSet) { 112 val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value 113 blueprintLogger.d({ "isUdfpsSupported=$bool1" }) { bool1 = isUdfpsSupported } 114 115 val scaleFactor: Float = authController.scaleFactor 116 val mBottomPaddingPx = 117 context.resources.getDimensionPixelSize(customR.dimen.lock_icon_margin_bottom) 118 val bounds = windowManager.currentWindowMetrics.bounds 119 var widthPixels = bounds.right.toFloat() 120 if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) { 121 // Assumed to be initially neglected as there are no left or right insets in portrait. 122 // However, on landscape, these insets need to included when calculating the midpoint. 123 val insets = windowManager.currentWindowMetrics.windowInsets 124 widthPixels -= (insets.systemWindowInsetLeft + insets.systemWindowInsetRight).toFloat() 125 } 126 val heightPixels = bounds.bottom.toFloat() 127 val defaultDensity = 128 DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() / 129 DisplayMetrics.DENSITY_DEFAULT.toFloat() 130 val iconRadiusPx = (defaultDensity * 36).toInt() 131 132 if (isUdfpsSupported) { 133 deviceEntryIconViewModel.get().udfpsLocation.value?.let { udfpsLocation -> 134 blueprintLogger.d({ 135 "udfpsLocation=$str1, scaledLocation=$str2, unusedAuthController=$str3" 136 }) { 137 str1 = "$udfpsLocation" 138 str2 = "(${udfpsLocation.centerX}, ${udfpsLocation.centerY})" 139 str3 = "${authController.udfpsLocation}" 140 } 141 centerIcon( 142 Point(udfpsLocation.centerX.toInt(), udfpsLocation.centerY.toInt()), 143 udfpsLocation.radius, 144 constraintSet, 145 ) 146 } 147 } else { 148 centerIcon( 149 Point( 150 (widthPixels / 2).toInt(), 151 (heightPixels - ((mBottomPaddingPx + iconRadiusPx) * scaleFactor)).toInt(), 152 ), 153 iconRadiusPx * scaleFactor, 154 constraintSet, 155 ) 156 } 157 } 158 159 override fun removeViews(constraintLayout: ConstraintLayout) { 160 constraintLayout.removeView(deviceEntryIconViewId) 161 disposableHandle?.dispose() 162 } 163 164 @VisibleForTesting 165 internal fun centerIcon(center: Point, radius: Float, constraintSet: ConstraintSet) { 166 val sensorRect = 167 Rect().apply { 168 set( 169 center.x - radius.toInt(), 170 center.y - radius.toInt(), 171 center.x + radius.toInt(), 172 center.y + radius.toInt(), 173 ) 174 } 175 176 val iconId = deviceEntryIconViewId 177 178 constraintSet.apply { 179 constrainWidth(iconId, sensorRect.right - sensorRect.left) 180 constrainHeight(iconId, sensorRect.bottom - sensorRect.top) 181 connect( 182 iconId, 183 ConstraintSet.TOP, 184 ConstraintSet.PARENT_ID, 185 ConstraintSet.TOP, 186 sensorRect.top, 187 ) 188 connect( 189 iconId, 190 ConstraintSet.START, 191 ConstraintSet.PARENT_ID, 192 ConstraintSet.START, 193 sensorRect.left, 194 ) 195 } 196 } 197 198 companion object { 199 private const val TAG = "DefaultDeviceEntrySection" 200 } 201 } 202