• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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