1 /* <lambda>null2 * 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 android.os.Handler 21 import android.util.Log 22 import androidx.annotation.VisibleForTesting 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.dagger.qualifiers.Main 25 import com.android.systemui.keyguard.shared.model.KeyguardBlueprint 26 import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT 27 import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule 28 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config 29 import com.android.systemui.util.ThreadAssert 30 import java.io.PrintWriter 31 import java.util.TreeMap 32 import javax.inject.Inject 33 import kotlinx.coroutines.flow.MutableSharedFlow 34 import kotlinx.coroutines.flow.MutableStateFlow 35 36 /** 37 * Manages blueprint changes for the lockscreen. 38 * 39 * To add a blueprint, create a class that implements LockscreenBlueprint and bind it to the map in 40 * the dagger module: [KeyguardBlueprintModule] 41 * 42 * A Blueprint determines how the layout should be constrained on a high level. 43 * 44 * A Section is a modular piece of code that implements the constraints. The blueprint uses the 45 * sections to define the constraints. 46 */ 47 @SysUISingleton 48 class KeyguardBlueprintRepository 49 @Inject 50 constructor( 51 blueprints: Set<@JvmSuppressWildcards KeyguardBlueprint>, 52 @Main val handler: Handler, 53 val assert: ThreadAssert, 54 ) { 55 // This is TreeMap so that we can order the blueprints and assign numerical values to the 56 // blueprints in the adb tool. 57 private val blueprintIdMap: TreeMap<String, KeyguardBlueprint> = 58 TreeMap<String, KeyguardBlueprint>().apply { putAll(blueprints.associateBy { it.id }) } 59 val blueprint: MutableStateFlow<KeyguardBlueprint> = MutableStateFlow(blueprintIdMap[DEFAULT]!!) 60 val refreshTransition = MutableSharedFlow<Config>(extraBufferCapacity = 1) 61 @VisibleForTesting var targetTransitionConfig: Config? = null 62 63 /** 64 * Emits the blueprint value to the collectors. 65 * 66 * @param blueprintId 67 * @return whether the transition has succeeded. 68 */ 69 fun applyBlueprint(blueprintId: String?): Boolean { 70 val blueprint = blueprintIdMap[blueprintId] 71 if (blueprint == null) { 72 Log.e( 73 TAG, 74 "Could not find blueprint with id: $blueprintId. " + 75 "Perhaps it was not added to KeyguardBlueprintModule?" 76 ) 77 return false 78 } 79 80 if (blueprint == this.blueprint.value) { 81 return true 82 } 83 84 this.blueprint.value = blueprint 85 return true 86 } 87 88 /** 89 * Re-emits the last emitted blueprint value if possible. This is delayed until next frame to 90 * dedupe requests and determine the correct transition to execute. 91 */ 92 fun refreshBlueprint(config: Config = Config.DEFAULT) { 93 fun scheduleCallback() { 94 // We use a handler here instead of a CoroutineDispatcher because the one provided by 95 // @Main CoroutineDispatcher is currently Dispatchers.Main.immediate, which doesn't 96 // delay the callback, and instead runs it immediately. 97 handler.post { 98 assert.isMainThread() 99 targetTransitionConfig?.let { 100 val success = refreshTransition.tryEmit(it) 101 if (!success) { 102 Log.e(TAG, "refreshBlueprint: Failed to emit blueprint refresh: $it") 103 } 104 } 105 targetTransitionConfig = null 106 } 107 } 108 109 assert.isMainThread() 110 if ((targetTransitionConfig?.type?.priority ?: Int.MIN_VALUE) < config.type.priority) { 111 if (targetTransitionConfig == null) scheduleCallback() 112 targetTransitionConfig = config 113 } 114 } 115 116 /** Prints all available blueprints to the PrintWriter. */ 117 fun printBlueprints(pw: PrintWriter) { 118 blueprintIdMap.onEachIndexed { index, entry -> pw.println("$index: ${entry.key}") } 119 } 120 121 companion object { 122 private const val TAG = "KeyguardBlueprintRepository" 123 } 124 } 125