1 /*
2  * Copyright (C) 2021 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 androidx.constraintlayout.compose
18 
19 import androidx.compose.runtime.Immutable
20 import androidx.compose.ui.layout.Measurable
21 import androidx.constraintlayout.core.parser.CLKey
22 import androidx.constraintlayout.core.parser.CLParser
23 import androidx.constraintlayout.core.parser.CLParsingException
24 import androidx.constraintlayout.core.state.ConstraintSetParser
25 import androidx.constraintlayout.core.state.Transition
26 import org.intellij.lang.annotations.Language
27 
28 @Immutable
29 internal class JSONConstraintSet(
30     @Language("json5") content: String,
31     @Language("json5") overrideVariables: String? = null,
32     override val extendFrom: ConstraintSet? = null
33 ) : EditableJSONLayout(content), DerivedConstraintSet {
34     private val overridedVariables = HashMap<String, Float>()
35     private val overrideVariables = overrideVariables
36     private var _isDirty = true
37 
38     init {
39         initialization()
40     }
41 
equalsnull42     override fun equals(other: Any?): Boolean {
43         if (other is JSONConstraintSet) {
44             return this.getCurrentContent() == other.getCurrentContent()
45         }
46         return false
47     }
48 
isDirtynull49     override fun isDirty(measurables: List<Measurable>): Boolean {
50         return _isDirty
51     }
52 
53     // Only called by MotionLayout in MotionMeasurer
applyTonull54     override fun applyTo(transition: Transition, type: Int) {
55         val layoutVariables = ConstraintSetParser.LayoutVariables()
56         applyLayoutVariables(layoutVariables)
57         ConstraintSetParser.parseJSON(getCurrentContent(), transition, type)
58     }
59 
emitDesignElementsnull60     fun emitDesignElements(designElements: ArrayList<ConstraintSetParser.DesignElement>) {
61         try {
62             designElements.clear()
63             ConstraintSetParser.parseDesignElementsJSON(getCurrentContent(), designElements)
64         } catch (e: Exception) {
65             // nothing (content might be invalid, sent by live edit)
66         }
67     }
68 
69     // Called by both MotionLayout & ConstraintLayout measurers
applyToStatenull70     override fun applyToState(state: State) {
71         val layoutVariables = ConstraintSetParser.LayoutVariables()
72         applyLayoutVariables(layoutVariables)
73         // TODO: Need to better handle half parsed JSON and/or incorrect states.
74         try {
75             ConstraintSetParser.parseJSON(getCurrentContent(), state, layoutVariables)
76             _isDirty = false
77         } catch (e: Exception) {
78             // nothing (content might be invalid, sent by live edit)
79             _isDirty = true
80         }
81     }
82 
onNewContentnull83     override fun onNewContent(content: String) {
84         super.onNewContent(content)
85         _isDirty = true
86     }
87 
overridenull88     override fun override(name: String, value: Float): ConstraintSet {
89         overridedVariables[name] = value
90         return this
91     }
92 
applyLayoutVariablesnull93     private fun applyLayoutVariables(layoutVariables: ConstraintSetParser.LayoutVariables) {
94         if (overrideVariables != null) {
95             try {
96                 val variables = CLParser.parse(overrideVariables)
97                 for (i in 0 until variables.size()) {
98                     val key = variables[i] as CLKey
99                     val variable = key.value.float
100                     // TODO: allow arbitrary override, not just float values
101                     layoutVariables.putOverride(key.content(), variable)
102                 }
103             } catch (e: CLParsingException) {
104                 System.err.println("exception: " + e)
105             }
106         }
107         for (name in overridedVariables.keys) {
108             layoutVariables.putOverride(name, overridedVariables[name]!!)
109         }
110     }
111 
resetForcedProgressnull112     override fun resetForcedProgress() {
113         // Nothing for ConstraintSet
114     }
115 
getForcedProgressnull116     override fun getForcedProgress(): Float {
117         // Nothing for ConstraintSet
118         return 0f
119     }
120 
onNewProgressnull121     override fun onNewProgress(progress: Float) {
122         // Nothing for ConstraintSet
123     }
124 }
125