1 /* 2 * Copyright (C) 2022 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.shared.condition 18 19 import android.annotation.IntDef 20 21 /** 22 * Helper for evaluating a collection of [Condition] objects with a given 23 * [Evaluator.ConditionOperand] 24 */ 25 internal object Evaluator { 26 /** Operands for combining multiple conditions together */ 27 @Retention(AnnotationRetention.SOURCE) 28 @IntDef(value = [OP_AND, OP_OR]) 29 annotation class ConditionOperand 30 31 /** 32 * 3-valued logical AND operand, with handling for unknown values (represented as null) 33 * 34 * ``` 35 * +-----+----+---+---+ 36 * | AND | T | F | U | 37 * +-----+----+---+---+ 38 * | T | T | F | U | 39 * | F | F | F | F | 40 * | U | U | F | U | 41 * +-----+----+---+---+ 42 * ``` 43 */ 44 const val OP_AND = 0 45 46 /** 47 * 3-valued logical OR operand, with handling for unknown values (represented as null) 48 * 49 * ``` 50 * +-----+----+---+---+ 51 * | OR | T | F | U | 52 * +-----+----+---+---+ 53 * | T | T | T | T | 54 * | F | T | F | U | 55 * | U | T | U | U | 56 * +-----+----+---+---+ 57 * ``` 58 */ 59 const val OP_OR = 1 60 61 /** 62 * Evaluates a set of conditions with a given operand 63 * 64 * If overriding conditions are present, they take precedence over normal conditions if set. 65 * 66 * @param conditions The collection of conditions to evaluate. If empty, null is returned. 67 * @param operand The operand to use when evaluating. 68 * @return Either true or false if the value is known, or null if value is unknown 69 */ evaluatenull70 fun evaluate(conditions: Collection<Condition>, @ConditionOperand operand: Int): Boolean? { 71 if (conditions.isEmpty()) return null 72 // If there are overriding conditions with values set, they take precedence. 73 val targetConditions = 74 conditions 75 .filter { it.isConditionSet && it.isOverridingCondition } 76 .ifEmpty { conditions } 77 return when (operand) { 78 OP_AND -> 79 threeValuedAndOrOr(conditions = targetConditions, returnValueIfAnyMatches = false) 80 OP_OR -> 81 threeValuedAndOrOr(conditions = targetConditions, returnValueIfAnyMatches = true) 82 else -> null 83 } 84 } 85 86 /** 87 * Helper for evaluating 3-valued logical AND/OR. 88 * 89 * @param returnValueIfAnyMatches AND returns false if any value is false. OR returns true if 90 * any value is true. 91 */ threeValuedAndOrOrnull92 private fun threeValuedAndOrOr( 93 conditions: Collection<Condition>, 94 returnValueIfAnyMatches: Boolean 95 ): Boolean? { 96 var hasUnknown = false 97 for (condition in conditions) { 98 if (!condition.isConditionSet) { 99 hasUnknown = true 100 continue 101 } 102 if (condition.isConditionMet == returnValueIfAnyMatches) { 103 return returnValueIfAnyMatches 104 } 105 } 106 return if (hasUnknown) null else !returnValueIfAnyMatches 107 } 108 } 109