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 com.android.systemui.keyguard.ui.binder 18 19 import android.annotation.DrawableRes 20 import android.view.View 21 import android.view.ViewGroup 22 import androidx.lifecycle.Lifecycle 23 import androidx.lifecycle.repeatOnLifecycle 24 import com.android.app.animation.Interpolators 25 import com.android.systemui.R 26 import com.android.systemui.common.shared.model.Icon 27 import com.android.systemui.common.shared.model.Text 28 import com.android.systemui.common.shared.model.TintedIcon 29 import com.android.systemui.flags.FeatureFlags 30 import com.android.systemui.flags.Flags 31 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel 32 import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel 33 import com.android.systemui.lifecycle.repeatWhenAttached 34 import com.android.systemui.statusbar.StatusBarState 35 import com.android.systemui.statusbar.policy.KeyguardStateController 36 import com.android.systemui.temporarydisplay.ViewPriority 37 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator 38 import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo 39 import kotlinx.coroutines.DisposableHandle 40 import kotlinx.coroutines.ExperimentalCoroutinesApi 41 import kotlinx.coroutines.launch 42 43 /** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */ 44 @ExperimentalCoroutinesApi 45 object KeyguardRootViewBinder { 46 @JvmStatic 47 fun bind( 48 view: ViewGroup, 49 viewModel: KeyguardRootViewModel, 50 featureFlags: FeatureFlags, 51 occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel, 52 chipbarCoordinator: ChipbarCoordinator, 53 keyguardStateController: KeyguardStateController, 54 ): DisposableHandle { 55 val disposableHandle = 56 view.repeatWhenAttached { 57 if (featureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) { 58 repeatOnLifecycle(Lifecycle.State.CREATED) { 59 launch { 60 occludingAppDeviceEntryMessageViewModel.message.collect { 61 biometricMessage -> 62 if (biometricMessage?.message != null) { 63 chipbarCoordinator.displayView( 64 createChipbarInfo( 65 biometricMessage.message, 66 R.drawable.ic_lock, 67 ) 68 ) 69 } else { 70 chipbarCoordinator.removeView(ID, "occludingAppMsgNull") 71 } 72 } 73 } 74 } 75 } 76 77 if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { 78 repeatOnLifecycle(Lifecycle.State.STARTED) { 79 launch { 80 viewModel.keyguardRootViewVisibilityState.collect { visibilityState -> 81 view.animate().cancel() 82 val goingToFullShade = visibilityState.goingToFullShade 83 val statusBarState = visibilityState.statusBarState 84 val isOcclusionTransitionRunning = 85 visibilityState.occlusionTransitionRunning 86 if (goingToFullShade) { 87 view.animate().alpha(0f).setStartDelay( 88 keyguardStateController.keyguardFadingAwayDelay 89 ).setDuration( 90 keyguardStateController.shortenedFadingAwayDuration 91 ).setInterpolator( 92 Interpolators.ALPHA_OUT 93 ).withEndAction { view.visibility = View.GONE }.start() 94 } else if ( 95 statusBarState == StatusBarState.KEYGUARD || 96 statusBarState == StatusBarState.SHADE_LOCKED 97 ) { 98 view.visibility = View.VISIBLE 99 if (!isOcclusionTransitionRunning) { 100 view.alpha = 1f 101 } 102 } else { 103 view.visibility = View.GONE 104 } 105 } 106 } 107 108 launch { 109 viewModel.alpha.collect { alpha -> 110 view.alpha = alpha 111 } 112 } 113 } 114 } 115 } 116 return disposableHandle 117 } 118 119 /** 120 * Creates an instance of [ChipbarInfo] that can be sent to [ChipbarCoordinator] for display. 121 */ 122 private fun createChipbarInfo(message: String, @DrawableRes icon: Int): ChipbarInfo { 123 return ChipbarInfo( 124 startIcon = 125 TintedIcon( 126 Icon.Resource(icon, null), 127 ChipbarInfo.DEFAULT_ICON_TINT, 128 ), 129 text = Text.Loaded(message), 130 endItem = null, 131 vibrationEffect = null, 132 windowTitle = "OccludingAppUnlockMsgChip", 133 wakeReason = "OCCLUDING_APP_UNLOCK_MSG_CHIP", 134 timeoutMs = 3500, 135 id = ID, 136 priority = ViewPriority.CRITICAL, 137 instanceId = null, 138 ) 139 } 140 141 private const val ID = "occluding_app_device_entry_unlock_msg" 142 } 143