• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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