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 
18 package com.android.systemui.keyguard.data.repository
19 
20 import androidx.constraintlayout.widget.ConstraintSet
21 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
22 import com.android.systemui.dagger.SysUISingleton
23 import com.android.systemui.dagger.qualifiers.Application
24 import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
25 import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule
26 import java.io.PrintWriter
27 import javax.inject.Inject
28 import kotlinx.coroutines.CoroutineScope
29 import kotlinx.coroutines.flow.Flow
30 import kotlinx.coroutines.flow.MutableSharedFlow
31 import kotlinx.coroutines.flow.asSharedFlow
32 import kotlinx.coroutines.launch
33 
34 /**
35  * Manages blueprint changes for the lockscreen.
36  *
37  * To add a blueprint, create a class that implements LockscreenBlueprint and bind it to the map in
38  * the dagger module:
39  *
40  * A Blueprint determines how the layout should be constrained on a high level.
41  *
42  * A Section is a modular piece of code that implements the constraints. The blueprint uses the
43  * sections to define the constraints.
44  *
45  * @see KeyguardBlueprintModule
46  */
47 @SysUISingleton
48 class KeyguardBlueprintRepository
49 @Inject
50 constructor(
51     configurationRepository: ConfigurationRepository,
52     blueprints: Set<@JvmSuppressWildcards KeyguardBlueprint>,
53     @Application private val applicationScope: CoroutineScope,
54 ) {
55     private val blueprintIdMap: Map<String, KeyguardBlueprint> = blueprints.associateBy { it.id }
56     private val _blueprint: MutableSharedFlow<KeyguardBlueprint> = MutableSharedFlow(replay = 1)
57     val blueprint: Flow<KeyguardBlueprint> = _blueprint.asSharedFlow()
58 
59     init {
60         applyBlueprint(blueprintIdMap[DEFAULT]!!)
61         applicationScope.launch {
62             configurationRepository.onAnyConfigurationChange.collect { refreshBlueprint() }
63         }
64     }
65 
66     /**
67      * Emits the blueprint value to the collectors.
68      *
69      * @param blueprintId
70      * @return whether the transition has succeeded.
71      */
72     fun applyBlueprint(blueprintId: String?): Boolean {
73         val blueprint = blueprintIdMap[blueprintId] ?: return false
74         applyBlueprint(blueprint)
75         return true
76     }
77 
78     /** Emits the blueprint value to the collectors. */
79     fun applyBlueprint(blueprint: KeyguardBlueprint?) {
80         blueprint?.let { _blueprint.tryEmit(it) }
81     }
82 
83     /** Re-emits the last emitted blueprint value if possible. */
84     fun refreshBlueprint() {
85         if (_blueprint.replayCache.isNotEmpty()) {
86             _blueprint.tryEmit(_blueprint.replayCache.last())
87         }
88     }
89 
90     /** Prints all available blueprints to the PrintWriter. */
91     fun printBlueprints(pw: PrintWriter) {
92         blueprintIdMap.forEach { entry -> pw.println("${entry.key}") }
93     }
94 }
95 
96 /** Determines the constraints for the ConstraintSet in the lockscreen root view. */
97 interface KeyguardBlueprint {
98     val id: String
99 
100     fun apply(constraintSet: ConstraintSet)
101 }
102 
103 /**
104  * Lower level modules that determine constraints for a particular section in the lockscreen root
105  * view.
106  */
107 interface KeyguardSection {
108     fun apply(constraintSet: ConstraintSet)
109 }
110