1 /* 2 * Copyright 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.scene.ui.view 18 19 import android.view.Gravity 20 import android.view.View 21 import android.view.ViewGroup 22 import android.widget.FrameLayout 23 import androidx.activity.OnBackPressedDispatcher 24 import androidx.activity.OnBackPressedDispatcherOwner 25 import androidx.activity.setViewTreeOnBackPressedDispatcherOwner 26 import androidx.core.view.isVisible 27 import androidx.lifecycle.Lifecycle 28 import androidx.lifecycle.lifecycleScope 29 import androidx.lifecycle.repeatOnLifecycle 30 import com.android.systemui.R 31 import com.android.systemui.compose.ComposeFacade 32 import com.android.systemui.lifecycle.repeatWhenAttached 33 import com.android.systemui.scene.shared.model.Scene 34 import com.android.systemui.scene.shared.model.SceneContainerConfig 35 import com.android.systemui.scene.shared.model.SceneKey 36 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel 37 import java.time.Instant 38 import kotlinx.coroutines.launch 39 40 object SceneWindowRootViewBinder { 41 42 /** Binds between the view and view-model pertaining to a specific scene container. */ 43 fun bind( 44 view: ViewGroup, 45 viewModel: SceneContainerViewModel, 46 containerConfig: SceneContainerConfig, 47 scenes: Set<Scene>, 48 onVisibilityChangedInternal: (isVisible: Boolean) -> Unit, 49 ) { 50 val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key } 51 val sortedSceneByKey: Map<SceneKey, Scene> = buildMap { 52 containerConfig.sceneKeys.forEach { sceneKey -> 53 val scene = 54 checkNotNull(unsortedSceneByKey[sceneKey]) { 55 "Scene not found for key \"$sceneKey\"!" 56 } 57 58 put(sceneKey, scene) 59 } 60 } 61 62 view.repeatWhenAttached { 63 lifecycleScope.launch { 64 repeatOnLifecycle(Lifecycle.State.CREATED) { 65 view.setViewTreeOnBackPressedDispatcherOwner( 66 object : OnBackPressedDispatcherOwner { 67 override val onBackPressedDispatcher = 68 OnBackPressedDispatcher().apply { 69 setOnBackInvokedDispatcher( 70 view.viewRootImpl.onBackInvokedDispatcher 71 ) 72 } 73 74 override val lifecycle: Lifecycle = this@repeatWhenAttached.lifecycle 75 } 76 ) 77 78 view.addView( 79 ComposeFacade.createSceneContainerView( 80 context = view.context, 81 viewModel = viewModel, 82 sceneByKey = sortedSceneByKey, 83 ) 84 ) 85 86 val legacyView = view.requireViewById<View>(R.id.legacy_window_root) 87 view.addView(createVisibilityToggleView(legacyView)) 88 89 launch { 90 viewModel.isVisible.collect { isVisible -> 91 onVisibilityChangedInternal(isVisible) 92 } 93 } 94 } 95 96 // Here when destroyed. 97 view.removeAllViews() 98 } 99 } 100 } 101 102 private var clickCount = 0 103 private var lastClick = Instant.now() 104 105 /** 106 * A temporary UI to toggle on/off the visibility of the given [otherView]. It is toggled by 107 * tapping 5 times in quick succession on the device camera (top center). 108 */ 109 // TODO(b/291321285): Remove this when the Flexiglass UI is mature enough to turn off legacy 110 // SysUI altogether. 111 private fun createVisibilityToggleView(otherView: View): View { 112 val toggleView = View(otherView.context) 113 toggleView.layoutParams = FrameLayout.LayoutParams(200, 200, Gravity.CENTER_HORIZONTAL) 114 toggleView.setOnClickListener { 115 val now = Instant.now() 116 clickCount = if (now.minusSeconds(2) > lastClick) 1 else clickCount + 1 117 if (clickCount == 5) { 118 otherView.isVisible = !otherView.isVisible 119 clickCount = 0 120 } 121 lastClick = now 122 } 123 return toggleView 124 } 125 } 126