summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/values/ids.xml10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt103
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt272
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt (renamed from packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt)68
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt)53
11 files changed, 565 insertions, 58 deletions
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 05f4334bbe89..74d435d18823 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -248,4 +248,14 @@
<!-- Tag set on the Compose implementation of the QS footer actions. -->
<item type="id" name="tag_compose_qs_footer_actions" />
+
+ <!--
+ Ids for the device entry icon.
+ device_entry_icon_view: parent view of both device_entry_icon and device_entry_icon_bg
+ device_entry_icon_fg: fp/lock/unlock icon
+ device_entry_icon_bg: background protection behind the icon
+ -->
+ <item type="id" name="device_entry_icon_view" />
+ <item type="id" name="device_entry_icon_fg" />
+ <item type="id" name="device_entry_icon_bg" />
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 272e0f21e74a..934f9f919d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -46,7 +46,6 @@ import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
@@ -240,15 +239,14 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
}
REASON_AUTH_KEYGUARD -> {
if (featureFlags.isEnabled(REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
- udfpsKeyguardViewModels.get().setSensorBounds(sensorBounds)
- UdfpsKeyguardViewController(
- view.addUdfpsView(R.layout.udfps_keyguard_view),
- statusBarStateController,
- primaryBouncerInteractor,
- dialogManager,
- dumpManager,
- alternateBouncerInteractor,
- udfpsKeyguardViewModels.get(),
+ // note: empty controller, currently shows no visual affordance
+ // instead SysUI will show the fingerprint icon in its DeviceEntryIconView
+ UdfpsBpViewController(
+ view.addUdfpsView(R.layout.udfps_bp_view),
+ statusBarStateController,
+ primaryBouncerInteractor,
+ dialogManager,
+ dumpManager
)
} else {
UdfpsKeyguardViewControllerLegacy(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 119ade48d4f7..c56dfde86573 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -48,6 +48,7 @@ import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -72,7 +73,7 @@ constructor(
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
private val context: Context,
private val keyguardIndicationController: KeyguardIndicationController,
- private val lockIconViewController: LockIconViewController,
+ private val lockIconViewController: Lazy<LockIconViewController>,
private val shadeInteractor: ShadeInteractor,
private val interactionJankMonitor: InteractionJankMonitor,
private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
@@ -131,7 +132,9 @@ constructor(
val indicationArea = KeyguardIndicationArea(context, null)
keyguardIndicationController.setIndicationArea(indicationArea)
- lockIconViewController.setLockIconView(LockIconView(context, null))
+ if (!featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ lockIconViewController.get().setLockIconView(LockIconView(context, null))
+ }
}
private fun bindKeyguardRootView() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
new file mode 100644
index 000000000000..e82ea7fecd05
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.annotation.SuppressLint
+import android.content.res.ColorStateList
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.FalsingManager
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+
+@ExperimentalCoroutinesApi
+object DeviceEntryIconViewBinder {
+
+ /**
+ * Updates UI for the device entry icon view (lock, unlock and fingerprint icons) and its
+ * background.
+ */
+ @SuppressLint("ClickableViewAccessibility")
+ @JvmStatic
+ fun bind(
+ view: DeviceEntryIconView,
+ viewModel: DeviceEntryIconViewModel,
+ falsingManager: FalsingManager,
+ ) {
+ val iconView = view.iconView
+ val bgView = view.bgView
+ val longPressHandlingView = view.longPressHandlingView
+ longPressHandlingView.listener =
+ object : LongPressHandlingView.Listener {
+ override fun onLongPressDetected(view: View, x: Int, y: Int) {
+ if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+ viewModel.onLongPress()
+ }
+ }
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.iconViewModel.collect { iconViewModel ->
+ iconView.setImageState(
+ view.getIconState(iconViewModel.type, iconViewModel.useAodVariant),
+ /* merge */ false
+ )
+ iconView.imageTintList = ColorStateList.valueOf(iconViewModel.tint)
+ iconView.alpha = iconViewModel.alpha
+ iconView.setPadding(
+ iconViewModel.padding,
+ iconViewModel.padding,
+ iconViewModel.padding,
+ iconViewModel.padding,
+ )
+ }
+ }
+ launch {
+ viewModel.backgroundViewModel.collect { bgViewModel ->
+ bgView.alpha = bgViewModel.alpha
+ bgView.imageTintList = ColorStateList.valueOf(bgViewModel.tint)
+ }
+ }
+ launch {
+ viewModel.burnInViewModel.collect { burnInViewModel ->
+ view.translationX = burnInViewModel.x.toFloat()
+ view.translationY = burnInViewModel.y.toFloat()
+ view.aodFpDrawable.progress = burnInViewModel.progress
+ }
+ }
+ launch {
+ viewModel.isLongPressEnabled.collect { isEnabled ->
+ longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
+ }
+ }
+ launch {
+ viewModel.accessibilityDelegateHint.collect { hint ->
+ view.accessibilityHintType = hint
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
new file mode 100644
index 000000000000..c9e395402dad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view
+
+import android.content.Context
+import android.graphics.drawable.AnimatedStateListDrawable
+import android.graphics.drawable.AnimatedVectorDrawable
+import android.util.AttributeSet
+import android.util.StateSet
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.FrameLayout
+import android.widget.ImageView
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import com.airbnb.lottie.LottieCompositionFactory
+import com.airbnb.lottie.LottieDrawable
+import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.res.R
+
+class DeviceEntryIconView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ defStyleAttrs: Int = 0,
+) : FrameLayout(context, attrs, defStyleAttrs) {
+ val longPressHandlingView: LongPressHandlingView = LongPressHandlingView(context, attrs)
+ val iconView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_fg }
+ val bgView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_bg }
+ val aodFpDrawable: LottieDrawable = LottieDrawable()
+ var accessibilityHintType: AccessibilityHintType = AccessibilityHintType.NONE
+
+ private var animatedIconDrawable: AnimatedStateListDrawable = AnimatedStateListDrawable()
+
+ init {
+ setupIconStates()
+ setupIconTransitions()
+ setupAccessibilityDelegate()
+
+ // Ordering matters. From background to foreground we want:
+ // bgView, iconView, longpressHandlingView overlay
+ addBgImageView()
+ addIconImageView()
+ addLongpressHandlingView()
+ }
+
+ private fun setupAccessibilityDelegate() {
+ accessibilityDelegate =
+ object : AccessibilityDelegate() {
+ private val accessibilityAuthenticateHint =
+ AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfoCompat.ACTION_CLICK,
+ resources.getString(R.string.accessibility_authenticate_hint)
+ )
+ private val accessibilityEnterHint =
+ AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfoCompat.ACTION_CLICK,
+ resources.getString(R.string.accessibility_enter_hint)
+ )
+ override fun onInitializeAccessibilityNodeInfo(
+ v: View,
+ info: AccessibilityNodeInfo
+ ) {
+ super.onInitializeAccessibilityNodeInfo(v, info)
+ when (accessibilityHintType) {
+ AccessibilityHintType.AUTHENTICATE ->
+ info.addAction(accessibilityAuthenticateHint)
+ AccessibilityHintType.ENTER -> info.addAction(accessibilityEnterHint)
+ AccessibilityHintType.NONE -> return
+ }
+ }
+ }
+ }
+
+ private fun setupIconStates() {
+ // Lockscreen States
+ // LOCK
+ animatedIconDrawable.addState(
+ getIconState(IconType.LOCK, false),
+ context.getDrawable(R.drawable.ic_lock)!!,
+ R.id.locked,
+ )
+ // UNLOCK
+ animatedIconDrawable.addState(
+ getIconState(IconType.UNLOCK, false),
+ context.getDrawable(R.drawable.ic_unlocked)!!,
+ R.id.unlocked,
+ )
+ // FINGERPRINT
+ animatedIconDrawable.addState(
+ getIconState(IconType.FINGERPRINT, false),
+ context.getDrawable(R.drawable.ic_kg_fingerprint)!!,
+ R.id.locked_fp,
+ )
+
+ // AOD states
+ // LOCK
+ animatedIconDrawable.addState(
+ getIconState(IconType.LOCK, true),
+ context.getDrawable(R.drawable.ic_lock_aod)!!,
+ R.id.locked_aod,
+ )
+ // UNLOCK
+ animatedIconDrawable.addState(
+ getIconState(IconType.UNLOCK, true),
+ context.getDrawable(R.drawable.ic_unlocked_aod)!!,
+ R.id.unlocked_aod,
+ )
+ // FINGERPRINT
+ LottieCompositionFactory.fromRawRes(mContext, R.raw.udfps_aod_fp).addListener { result ->
+ aodFpDrawable.setComposition(result)
+ }
+ animatedIconDrawable.addState(
+ getIconState(IconType.FINGERPRINT, true),
+ aodFpDrawable,
+ R.id.udfps_aod_fp,
+ )
+
+ // WILDCARD: should always be the last state added since any states will match with this
+ // and therefore won't get matched with subsequent states.
+ animatedIconDrawable.addState(
+ StateSet.WILD_CARD,
+ context.getDrawable(R.color.transparent)!!,
+ R.id.no_icon,
+ )
+ }
+
+ private fun setupIconTransitions() {
+ // LockscreenFp <=> LockscreenUnlocked
+ animatedIconDrawable.addTransition(
+ R.id.locked_fp,
+ R.id.unlocked,
+ context.getDrawable(R.drawable.fp_to_unlock) as AnimatedVectorDrawable,
+ /* reversible */ false,
+ )
+ animatedIconDrawable.addTransition(
+ R.id.unlocked,
+ R.id.locked_fp,
+ context.getDrawable(R.drawable.unlock_to_fp) as AnimatedVectorDrawable,
+ /* reversible */ false,
+ )
+
+ // LockscreenLocked <=> AodLocked
+ animatedIconDrawable.addTransition(
+ R.id.locked_aod,
+ R.id.locked,
+ context.getDrawable(R.drawable.lock_aod_to_ls) as AnimatedVectorDrawable,
+ /* reversible */ false,
+ )
+ animatedIconDrawable.addTransition(
+ R.id.locked,
+ R.id.locked_aod,
+ context.getDrawable(R.drawable.lock_ls_to_aod) as AnimatedVectorDrawable,
+ /* reversible */ false,
+ )
+
+ // LockscreenUnlocked <=> AodUnlocked
+ animatedIconDrawable.addTransition(
+ R.id.unlocked_aod,
+ R.id.unlocked,
+ context.getDrawable(R.drawable.unlocked_aod_to_ls) as AnimatedVectorDrawable,
+ /* reversible */ false,
+ )
+ animatedIconDrawable.addTransition(
+ R.id.unlocked,
+ R.id.unlocked_aod,
+ context.getDrawable(R.drawable.unlocked_ls_to_aod) as AnimatedVectorDrawable,
+ /* reversible */ false,
+ )
+
+ // LockscreenLocked <=> LockscreenUnlocked
+ animatedIconDrawable.addTransition(
+ R.id.locked,
+ R.id.unlocked,
+ context.getDrawable(R.drawable.lock_to_unlock) as AnimatedVectorDrawable,
+ /* reversible */ false,
+ )
+ animatedIconDrawable.addTransition(
+ R.id.unlocked,
+ R.id.locked,
+ context.getDrawable(R.drawable.unlocked_to_locked) as AnimatedVectorDrawable,
+ /* reversible */ false,
+ )
+
+ // LockscreenFingerprint <=> LockscreenLocked
+ animatedIconDrawable.addTransition(
+ R.id.locked_fp,
+ R.id.locked,
+ context.getDrawable(R.drawable.fp_to_locked) as AnimatedVectorDrawable,
+ /* reversible */ true,
+ )
+
+ // LockscreenUnlocked <=> AodLocked
+ animatedIconDrawable.addTransition(
+ R.id.unlocked,
+ R.id.locked_aod,
+ context.getDrawable(R.drawable.unlocked_to_aod_lock) as AnimatedVectorDrawable,
+ /* reversible */ true,
+ )
+ }
+
+ private fun addLongpressHandlingView() {
+ addView(longPressHandlingView)
+ val lp = longPressHandlingView.layoutParams as LayoutParams
+ lp.height = ViewGroup.LayoutParams.MATCH_PARENT
+ lp.width = ViewGroup.LayoutParams.MATCH_PARENT
+ longPressHandlingView.setLayoutParams(lp)
+ }
+
+ private fun addIconImageView() {
+ iconView.scaleType = ImageView.ScaleType.CENTER_CROP
+ iconView.setImageDrawable(animatedIconDrawable)
+ addView(iconView)
+ val lp = iconView.layoutParams as LayoutParams
+ lp.height = ViewGroup.LayoutParams.MATCH_PARENT
+ lp.width = ViewGroup.LayoutParams.MATCH_PARENT
+ lp.gravity = Gravity.CENTER
+ iconView.setLayoutParams(lp)
+ }
+
+ private fun addBgImageView() {
+ bgView.setImageDrawable(context.getDrawable(R.drawable.fingerprint_bg))
+ addView(bgView)
+ val lp = bgView.layoutParams as LayoutParams
+ lp.height = ViewGroup.LayoutParams.MATCH_PARENT
+ lp.width = ViewGroup.LayoutParams.MATCH_PARENT
+ bgView.setLayoutParams(lp)
+ }
+
+ fun getIconState(icon: IconType, aod: Boolean): IntArray {
+ val lockIconState = IntArray(2)
+ when (icon) {
+ IconType.LOCK -> lockIconState[0] = android.R.attr.state_first
+ IconType.UNLOCK -> lockIconState[0] = android.R.attr.state_last
+ IconType.FINGERPRINT -> lockIconState[0] = android.R.attr.state_middle
+ }
+ if (aod) {
+ lockIconState[1] = android.R.attr.state_single
+ } else {
+ lockIconState[1] = -android.R.attr.state_single
+ }
+ return lockIconState
+ }
+
+ enum class IconType {
+ LOCK,
+ UNLOCK,
+ FINGERPRINT,
+ }
+
+ enum class AccessibilityHintType {
+ NONE,
+ AUTHENTICATE,
+ ENTER,
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index d8e43966990e..21eba56dcd83 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -23,8 +23,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
@@ -44,7 +44,7 @@ class DefaultKeyguardBlueprint
@Inject
constructor(
defaultIndicationAreaSection: DefaultIndicationAreaSection,
- defaultLockIconSection: DefaultLockIconSection,
+ defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
defaultShortcutsSection: DefaultShortcutsSection,
defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
@@ -61,7 +61,7 @@ constructor(
override val sections =
setOf(
defaultIndicationAreaSection,
- defaultLockIconSection,
+ defaultDeviceEntryIconSection,
defaultShortcutsSection,
defaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index ce76f56fad2f..f8dd7c1a58c7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -23,8 +23,8 @@ import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdf
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
@@ -38,7 +38,7 @@ class ShortcutsBesideUdfpsKeyguardBlueprint
@Inject
constructor(
defaultIndicationAreaSection: DefaultIndicationAreaSection,
- defaultLockIconSection: DefaultLockIconSection,
+ defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
@@ -54,7 +54,7 @@ constructor(
override val sections =
setOf(
defaultIndicationAreaSection,
- defaultLockIconSection,
+ defaultDeviceEntryIconSection,
defaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection,
alignShortcutsToUdfpsSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
index f4bc7137b50c..62c5988ff42c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
@@ -33,11 +33,18 @@ import com.android.systemui.biometrics.AuthController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
+import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
-class DefaultLockIconSection
+@ExperimentalCoroutinesApi
+class DefaultDeviceEntryIconSection
@Inject
constructor(
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@@ -46,24 +53,47 @@ constructor(
private val context: Context,
private val notificationPanelView: NotificationPanelView,
private val featureFlags: FeatureFlags,
- private val lockIconViewController: LockIconViewController,
+ private val lockIconViewController: Lazy<LockIconViewController>,
+ private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
+ private val falsingManager: Lazy<FalsingManager>,
) : KeyguardSection() {
- private val lockIconViewId = R.id.lock_icon_view
+ private val deviceEntryIconViewId = R.id.device_entry_icon_view
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ if (
+ !featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON) &&
+ !featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)
+ ) {
return
}
- notificationPanelView.findViewById<View>(lockIconViewId).let {
+
+ notificationPanelView.findViewById<View>(R.id.lock_icon_view).let {
notificationPanelView.removeView(it)
}
- val view = LockIconView(context, null).apply { id = lockIconViewId }
+
+ val view =
+ if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId }
+ } else {
+ // Flags.MIGRATE_LOCK_ICON
+ LockIconView(context, null).apply { id = deviceEntryIconViewId }
+ }
constraintLayout.addView(view)
}
override fun bindData(constraintLayout: ConstraintLayout) {
- constraintLayout.findViewById<LockIconView?>(lockIconViewId)?.let {
- lockIconViewController.setLockIconView(it)
+ if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let {
+ DeviceEntryIconViewBinder.bind(
+ it,
+ deviceEntryIconViewModel.get(),
+ falsingManager.get(),
+ )
+ }
+ } else {
+ constraintLayout.findViewById<LockIconView?>(deviceEntryIconViewId)?.let {
+ lockIconViewController.get().setLockIconView(it)
+ }
}
}
@@ -84,30 +114,30 @@ constructor(
val defaultDensity =
DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
DisplayMetrics.DENSITY_DEFAULT.toFloat()
- val lockIconRadiusPx = (defaultDensity * 36).toInt()
+ val iconRadiusPx = (defaultDensity * 36).toInt()
if (isUdfpsSupported) {
authController.udfpsLocation?.let { udfpsLocation ->
- centerLockIcon(udfpsLocation, authController.udfpsRadius, constraintSet)
+ centerIcon(udfpsLocation, authController.udfpsRadius, constraintSet)
}
} else {
- centerLockIcon(
+ centerIcon(
Point(
(widthPixels / 2).toInt(),
- (heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
+ (heightPixels - ((mBottomPaddingPx + iconRadiusPx) * scaleFactor)).toInt()
),
- lockIconRadiusPx * scaleFactor,
+ iconRadiusPx * scaleFactor,
constraintSet,
)
}
}
override fun removeViews(constraintLayout: ConstraintLayout) {
- constraintLayout.removeView(lockIconViewId)
+ constraintLayout.removeView(deviceEntryIconViewId)
}
@VisibleForTesting
- internal fun centerLockIcon(center: Point, radius: Float, constraintSet: ConstraintSet) {
+ internal fun centerIcon(center: Point, radius: Float, constraintSet: ConstraintSet) {
val sensorRect =
Rect().apply {
set(
@@ -119,17 +149,17 @@ constructor(
}
constraintSet.apply {
- constrainWidth(lockIconViewId, sensorRect.right - sensorRect.left)
- constrainHeight(lockIconViewId, sensorRect.bottom - sensorRect.top)
+ constrainWidth(deviceEntryIconViewId, sensorRect.right - sensorRect.left)
+ constrainHeight(deviceEntryIconViewId, sensorRect.bottom - sensorRect.top)
connect(
- lockIconViewId,
+ deviceEntryIconViewId,
ConstraintSet.TOP,
ConstraintSet.PARENT_ID,
ConstraintSet.TOP,
sensorRect.top
)
connect(
- lockIconViewId,
+ deviceEntryIconViewId,
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
new file mode 100644
index 000000000000..842dde352c71
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.graphics.Color
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+@ExperimentalCoroutinesApi
+class DeviceEntryIconViewModel @Inject constructor() {
+ // TODO: b/305234447 update these states from the data layer
+ val iconViewModel: Flow<IconViewModel> =
+ flowOf(
+ IconViewModel(
+ type = DeviceEntryIconView.IconType.LOCK,
+ useAodVariant = false,
+ tint = Color.WHITE,
+ alpha = 1f,
+ padding = 48,
+ )
+ )
+ val backgroundViewModel: Flow<BackgroundViewModel> =
+ flowOf(BackgroundViewModel(alpha = 1f, tint = Color.GRAY))
+ val burnInViewModel: Flow<BurnInViewModel> = flowOf(BurnInViewModel(0, 0, 0f))
+ val isLongPressEnabled: Flow<Boolean> = flowOf(true)
+ val accessibilityDelegateHint: Flow<DeviceEntryIconView.AccessibilityHintType> =
+ flowOf(DeviceEntryIconView.AccessibilityHintType.NONE)
+
+ fun onLongPress() {
+ // TODO() vibrate & perform action based on current lock/unlock state
+ }
+ data class BurnInViewModel(
+ val x: Int, // current x burn in offset based on the aodTransitionAmount
+ val y: Int, // current y burn in offset based on the aodTransitionAmount
+ val progress: Float, // current progress based on the aodTransitionAmount
+ )
+
+ class IconViewModel(
+ val type: DeviceEntryIconView.IconType,
+ val useAodVariant: Boolean,
+ val tint: Int,
+ val alpha: Float,
+ val padding: Int,
+ )
+
+ class BackgroundViewModel(
+ val alpha: Float,
+ val tint: Int,
+ )
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 50ee02609635..8cfa87d27e57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -30,8 +30,8 @@ import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
@@ -55,7 +55,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
private lateinit var underTest: DefaultKeyguardBlueprint
private lateinit var rootView: KeyguardRootView
@Mock private lateinit var defaultIndicationAreaSection: DefaultIndicationAreaSection
- @Mock private lateinit var defaultLockIconSection: DefaultLockIconSection
+ @Mock private lateinit var mDefaultDeviceEntryIconSection: DefaultDeviceEntryIconSection
@Mock private lateinit var defaultShortcutsSection: DefaultShortcutsSection
@Mock
private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection
@@ -75,7 +75,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
underTest =
DefaultKeyguardBlueprint(
defaultIndicationAreaSection,
- defaultLockIconSection,
+ mDefaultDeviceEntryIconSection,
defaultShortcutsSection,
defaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection,
@@ -101,14 +101,14 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
val prevBlueprint = mock(KeyguardBlueprint::class.java)
val someSection = mock(KeyguardSection::class.java)
whenever(prevBlueprint.sections)
- .thenReturn(underTest.sections.minus(defaultLockIconSection).plus(someSection))
+ .thenReturn(underTest.sections.minus(mDefaultDeviceEntryIconSection).plus(someSection))
val constraintLayout = ConstraintLayout(context, null)
underTest.replaceViews(prevBlueprint, constraintLayout)
- underTest.sections.minus(defaultLockIconSection).forEach {
+ underTest.sections.minus(mDefaultDeviceEntryIconSection).forEach {
verify(it, never()).addViews(constraintLayout)
}
- verify(defaultLockIconSection).addViews(constraintLayout)
+ verify(mDefaultDeviceEntryIconSection).addViews(constraintLayout)
verify(someSection).removeViews(constraintLayout)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
index 74956377d345..5f22c7da3920 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
@@ -24,14 +24,17 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -40,35 +43,54 @@ import org.mockito.Answers
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+@ExperimentalCoroutinesApi
@RunWith(JUnit4::class)
@SmallTest
-class DefaultLockIconSectionTest : SysuiTestCase() {
+class DefaultDeviceEntryIconSectionTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var authController: AuthController
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
@Mock private lateinit var notificationPanelView: NotificationPanelView
- @Mock private lateinit var featureFlags: FeatureFlags
+ private lateinit var featureFlags: FakeFeatureFlags
@Mock private lateinit var lockIconViewController: LockIconViewController
- private lateinit var underTest: DefaultLockIconSection
+ @Mock private lateinit var falsingManager: FalsingManager
+ private lateinit var underTest: DefaultDeviceEntryIconSection
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ featureFlags =
+ FakeFeatureFlagsClassic().apply {
+ set(Flags.MIGRATE_LOCK_ICON, false)
+ set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
+ set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
+ }
underTest =
- DefaultLockIconSection(
+ DefaultDeviceEntryIconSection(
keyguardUpdateMonitor,
authController,
windowManager,
context,
notificationPanelView,
featureFlags,
- lockIconViewController
+ { lockIconViewController },
+ { DeviceEntryIconViewModel() },
+ { falsingManager },
)
}
@Test
- fun addViewsConditionally() {
- whenever(featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)).thenReturn(true)
+ fun addViewsConditionally_migrateFlagOn() {
+ featureFlags.set(Flags.MIGRATE_LOCK_ICON, true)
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.addViews(constraintLayout)
+ assertThat(constraintLayout.childCount).isGreaterThan(0)
+ }
+
+ @Test
+ fun addViewsConditionally_migrateAndRefactorFlagsOn() {
+ featureFlags.set(Flags.MIGRATE_LOCK_ICON, true)
+ featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
assertThat(constraintLayout.childCount).isGreaterThan(0)
@@ -76,7 +98,8 @@ class DefaultLockIconSectionTest : SysuiTestCase() {
@Test
fun addViewsConditionally_migrateFlagOff() {
- whenever(featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)).thenReturn(false)
+ featureFlags.set(Flags.MIGRATE_LOCK_ICON, false)
+ featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
assertThat(constraintLayout.childCount).isEqualTo(0)
@@ -87,18 +110,18 @@ class DefaultLockIconSectionTest : SysuiTestCase() {
val cs = ConstraintSet()
underTest.applyConstraints(cs)
- val constraint = cs.getConstraint(R.id.lock_icon_view)
+ val constraint = cs.getConstraint(R.id.device_entry_icon_view)
assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
}
@Test
- fun testCenterLockIcon() {
+ fun testCenterIcon() {
val cs = ConstraintSet()
- underTest.centerLockIcon(Point(5, 6), 1F, cs)
+ underTest.centerIcon(Point(5, 6), 1F, cs)
- val constraint = cs.getConstraint(R.id.lock_icon_view)
+ val constraint = cs.getConstraint(R.id.device_entry_icon_view)
assertThat(constraint.layout.mWidth).isEqualTo(2)
assertThat(constraint.layout.mHeight).isEqualTo(2)