1 /* 2 * Copyright (C) 2024 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.view 18 19 import android.content.Context 20 import android.view.View 21 import android.view.WindowManager 22 import android.widget.FrameLayout 23 import androidx.compose.ui.platform.ComposeView 24 import com.android.systemui.CoreStartable 25 import com.android.systemui.compose.ComposeInitializer 26 import com.android.systemui.dagger.SysUISingleton 27 import com.android.systemui.dagger.qualifiers.Application 28 import com.android.systemui.keyguard.ui.composable.AlternateBouncer 29 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies 30 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel 31 import com.android.systemui.lifecycle.repeatWhenAttached 32 import com.android.systemui.lifecycle.repeatWhenAttachedToWindow 33 import com.android.systemui.scene.shared.flag.SceneContainerFlag 34 import javax.inject.Inject 35 import kotlinx.coroutines.CoroutineScope 36 import kotlinx.coroutines.awaitCancellation 37 import kotlinx.coroutines.flow.distinctUntilChanged 38 import kotlinx.coroutines.flow.filter 39 import kotlinx.coroutines.launch 40 41 /** Drives the showing and hiding of the alternate bouncer window. */ 42 @SysUISingleton 43 class AlternateBouncerWindowViewBinder 44 @Inject 45 constructor( 46 @Application private val applicationScope: CoroutineScope, 47 @Application private val context: Context, 48 private val viewModel: AlternateBouncerViewModel, 49 private val dependencies: AlternateBouncerDependencies, 50 private val windowManager: WindowManager, 51 ) : CoreStartable { 52 startnull53 override fun start() { 54 if (!SceneContainerFlag.isEnabled) { 55 return 56 } 57 58 applicationScope.launch { 59 viewModel.isVisible 60 .distinctUntilChanged() 61 .filter { it } 62 .collect { 63 windowManager.addView( 64 createView(), 65 AlternateBouncerWindowViewLayoutParams.layoutParams, 66 ) 67 } 68 } 69 } 70 createViewnull71 private fun createView(): View { 72 val root = FrameLayout(context) 73 val composeView = 74 ComposeView(context).apply { 75 setContent { 76 AlternateBouncer( 77 alternateBouncerDependencies = dependencies, 78 onHideAnimationFinished = { 79 if (root.isAttachedToWindow) { 80 windowManager.removeView(root) 81 } 82 }, 83 ) 84 } 85 } 86 87 root.repeatWhenAttached { 88 root.repeatWhenAttachedToWindow { 89 try { 90 ComposeInitializer.onAttachedToWindow(root) 91 root.addView(composeView) 92 awaitCancellation() 93 } finally { 94 root.removeView(composeView) 95 ComposeInitializer.onDetachedFromWindow(root) 96 } 97 } 98 } 99 100 return root 101 } 102 } 103