1 /*
2  * Copyright (C) 2019 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.core.state;
18 
19 import static androidx.constraintlayout.core.widgets.ConstraintWidget.HORIZONTAL;
20 import static androidx.constraintlayout.core.widgets.ConstraintWidget.UNKNOWN;
21 import static androidx.constraintlayout.core.widgets.ConstraintWidget.VERTICAL;
22 
23 import androidx.constraintlayout.core.motion.utils.TypedBundle;
24 import androidx.constraintlayout.core.motion.utils.TypedValues;
25 import androidx.constraintlayout.core.state.helpers.Facade;
26 import androidx.constraintlayout.core.widgets.ConstraintAnchor;
27 import androidx.constraintlayout.core.widgets.ConstraintWidget;
28 
29 import org.jspecify.annotations.Nullable;
30 
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 
34 public class ConstraintReference implements Reference {
35 
36     private Object mKey;
37 
38     @Override
setKey(Object key)39     public void setKey(Object key) {
40         this.mKey = key;
41     }
42 
43     @Override
getKey()44     public Object getKey() {
45         return mKey;
46     }
47 
setTag(String tag)48     public void setTag(String tag) {
49         mTag = tag;
50     }
51 
getTag()52     public String getTag() {
53         return mTag;
54     }
55 
56     public interface ConstraintReferenceFactory {
57         // @TODO: add description
create(State state)58         ConstraintReference create(State state);
59     }
60 
61     final State mState;
62 
63     String mTag = null;
64 
65     Facade mFacade = null;
66 
67     int mHorizontalChainStyle = ConstraintWidget.CHAIN_SPREAD;
68     int mVerticalChainStyle = ConstraintWidget.CHAIN_SPREAD;
69 
70     float mHorizontalChainWeight = UNKNOWN;
71     float mVerticalChainWeight = UNKNOWN;
72 
73     protected float mHorizontalBias = 0.5f;
74     protected float mVerticalBias = 0.5f;
75 
76     protected int mMarginLeft = 0;
77     protected int mMarginRight = 0;
78     protected int mMarginStart = 0;
79     protected int mMarginEnd = 0;
80     protected int mMarginTop = 0;
81     protected int mMarginBottom = 0;
82 
83     protected int mMarginLeftGone = 0;
84     protected int mMarginRightGone = 0;
85     protected int mMarginStartGone = 0;
86     protected int mMarginEndGone = 0;
87     protected int mMarginTopGone = 0;
88     protected int mMarginBottomGone = 0;
89 
90     int mMarginBaseline = 0;
91     int mMarginBaselineGone = 0;
92 
93     float mPivotX = Float.NaN;
94     float mPivotY = Float.NaN;
95 
96     float mRotationX = Float.NaN;
97     float mRotationY = Float.NaN;
98     float mRotationZ = Float.NaN;
99 
100     float mTranslationX = Float.NaN;
101     float mTranslationY = Float.NaN;
102     float mTranslationZ = Float.NaN;
103 
104     float mAlpha = Float.NaN;
105 
106     float mScaleX = Float.NaN;
107     float mScaleY = Float.NaN;
108 
109     int mVisibility = ConstraintWidget.VISIBLE;
110 
111     protected Object mLeftToLeft = null;
112     protected Object mLeftToRight = null;
113     protected Object mRightToLeft = null;
114     protected Object mRightToRight = null;
115     protected Object mStartToStart = null;
116     protected Object mStartToEnd = null;
117     protected Object mEndToStart = null;
118     protected Object mEndToEnd = null;
119     protected Object mTopToTop = null;
120     protected Object mTopToBottom = null;
121     @Nullable Object mTopToBaseline = null;
122     protected Object mBottomToTop = null;
123     protected Object mBottomToBottom = null;
124     @Nullable Object mBottomToBaseline = null;
125     Object mBaselineToBaseline = null;
126     Object mBaselineToTop = null;
127     Object mBaselineToBottom = null;
128     Object mCircularConstraint = null;
129     private float mCircularAngle;
130     private float mCircularDistance;
131 
132     State.Constraint mLast = null;
133 
134     Dimension mHorizontalDimension = Dimension.createFixed(Dimension.WRAP_DIMENSION);
135     Dimension mVerticalDimension = Dimension.createFixed(Dimension.WRAP_DIMENSION);
136 
137     private Object mView;
138     private ConstraintWidget mConstraintWidget;
139 
140     private HashMap<String, Integer> mCustomColors = new HashMap<>();
141     private HashMap<String, Float> mCustomFloats = new HashMap<>();
142 
143     TypedBundle mMotionProperties = null;
144 
145     // @TODO: add description
setView(Object view)146     public void setView(Object view) {
147         mView = view;
148         if (mConstraintWidget != null) {
149             mConstraintWidget.setCompanionWidget(mView);
150         }
151     }
152 
getView()153     public Object getView() {
154         return mView;
155     }
156 
157     // @TODO: add description
setFacade(Facade facade)158     public void setFacade(Facade facade) {
159         mFacade = facade;
160         if (facade != null) {
161             setConstraintWidget(facade.getConstraintWidget());
162         }
163     }
164 
165     @Override
getFacade()166     public Facade getFacade() {
167         return mFacade;
168     }
169 
170     // @TODO: add description
171     @Override
setConstraintWidget(ConstraintWidget widget)172     public void setConstraintWidget(ConstraintWidget widget) {
173         if (widget == null) {
174             return;
175         }
176         mConstraintWidget = widget;
177         mConstraintWidget.setCompanionWidget(mView);
178     }
179 
180     @Override
getConstraintWidget()181     public ConstraintWidget getConstraintWidget() {
182         if (mConstraintWidget == null) {
183             mConstraintWidget = createConstraintWidget();
184             mConstraintWidget.setCompanionWidget(mView);
185         }
186         return mConstraintWidget;
187     }
188 
189     // @TODO: add description
createConstraintWidget()190     public ConstraintWidget createConstraintWidget() {
191         return new ConstraintWidget(
192                 getWidth().getValue(),
193                 getHeight().getValue());
194     }
195 
196     static class IncorrectConstraintException extends Exception {
197 
198         private final ArrayList<String> mErrors;
199 
IncorrectConstraintException(ArrayList<String> errors)200         IncorrectConstraintException(ArrayList<String> errors) {
201             mErrors = errors;
202         }
203 
getErrors()204         public ArrayList<String> getErrors() {
205             return mErrors;
206         }
207 
208         @Override
getMessage()209         public String  getMessage() { return toString(); }
210 
211         @Override
toString()212         public String toString() {
213             return "IncorrectConstraintException: " + mErrors.toString();
214         }
215     }
216 
217     /**
218      * Validate the constraints
219      */
validate()220     public void validate() throws IncorrectConstraintException {
221         ArrayList<String> errors = new ArrayList<>();
222         if (mLeftToLeft != null && mLeftToRight != null) {
223             errors.add("LeftToLeft and LeftToRight both defined");
224         }
225         if (mRightToLeft != null && mRightToRight != null) {
226             errors.add("RightToLeft and RightToRight both defined");
227         }
228         if (mStartToStart != null && mStartToEnd != null) {
229             errors.add("StartToStart and StartToEnd both defined");
230         }
231         if (mEndToStart != null && mEndToEnd != null) {
232             errors.add("EndToStart and EndToEnd both defined");
233         }
234         if ((mLeftToLeft != null || mLeftToRight != null
235                 || mRightToLeft != null || mRightToRight != null)
236                 && (mStartToStart != null || mStartToEnd != null
237                 || mEndToStart != null || mEndToEnd != null)) {
238             errors.add("Both left/right and start/end constraints defined");
239         }
240         if (errors.size() > 0) {
241             throw new IncorrectConstraintException(errors);
242         }
243     }
244 
get(Object reference)245     private Object get(Object reference) {
246         if (reference == null) {
247             return null;
248         }
249         if (!(reference instanceof ConstraintReference)) {
250             return mState.reference(reference);
251         }
252         return reference;
253     }
254 
ConstraintReference(State state)255     public ConstraintReference(State state) {
256         mState = state;
257     }
258 
setHorizontalChainStyle(int chainStyle)259     public void setHorizontalChainStyle(int chainStyle) {
260         mHorizontalChainStyle = chainStyle;
261     }
262 
getHorizontalChainStyle()263     public int getHorizontalChainStyle() {
264         return mHorizontalChainStyle;
265     }
266 
setVerticalChainStyle(int chainStyle)267     public void setVerticalChainStyle(int chainStyle) {
268         mVerticalChainStyle = chainStyle;
269     }
270 
271     // @TODO: add description
getVerticalChainStyle(int chainStyle)272     public int getVerticalChainStyle(int chainStyle) {
273         return mVerticalChainStyle;
274     }
275 
getHorizontalChainWeight()276     public float getHorizontalChainWeight() {
277         return mHorizontalChainWeight;
278     }
279 
setHorizontalChainWeight(float weight)280     public void setHorizontalChainWeight(float weight) {
281         mHorizontalChainWeight = weight;
282     }
283 
getVerticalChainWeight()284     public float getVerticalChainWeight() {
285         return mVerticalChainWeight;
286     }
287 
setVerticalChainWeight(float weight)288     public void setVerticalChainWeight(float weight) {
289         mVerticalChainWeight = weight;
290     }
291 
292     // @TODO: add description
clearVertical()293     public ConstraintReference clearVertical() {
294         top().clear();
295         baseline().clear();
296         bottom().clear();
297         return this;
298     }
299 
300     // @TODO: add description
clearHorizontal()301     public ConstraintReference clearHorizontal() {
302         start().clear();
303         end().clear();
304         left().clear();
305         right().clear();
306         return this;
307     }
308 
getTranslationX()309     public float getTranslationX() {
310         return mTranslationX;
311     }
312 
getTranslationY()313     public float getTranslationY() {
314         return mTranslationY;
315     }
316 
getTranslationZ()317     public float getTranslationZ() {
318         return mTranslationZ;
319     }
320 
getScaleX()321     public float getScaleX() {
322         return mScaleX;
323     }
324 
getScaleY()325     public float getScaleY() {
326         return mScaleY;
327     }
328 
getAlpha()329     public float getAlpha() {
330         return mAlpha;
331     }
332 
getPivotX()333     public float getPivotX() {
334         return mPivotX;
335     }
336 
getPivotY()337     public float getPivotY() {
338         return mPivotY;
339     }
340 
getRotationX()341     public float getRotationX() {
342         return mRotationX;
343     }
344 
getRotationY()345     public float getRotationY() {
346         return mRotationY;
347     }
348 
getRotationZ()349     public float getRotationZ() {
350         return mRotationZ;
351     }
352 
353     // @TODO: add description
pivotX(float x)354     public ConstraintReference pivotX(float x) {
355         mPivotX = x;
356         return this;
357     }
358 
359     // @TODO: add description
pivotY(float y)360     public ConstraintReference pivotY(float y) {
361         mPivotY = y;
362         return this;
363     }
364 
365     // @TODO: add description
rotationX(float x)366     public ConstraintReference rotationX(float x) {
367         mRotationX = x;
368         return this;
369     }
370 
371     // @TODO: add description
rotationY(float y)372     public ConstraintReference rotationY(float y) {
373         mRotationY = y;
374         return this;
375     }
376 
377     // @TODO: add description
rotationZ(float z)378     public ConstraintReference rotationZ(float z) {
379         mRotationZ = z;
380         return this;
381     }
382 
383     // @TODO: add description
translationX(float x)384     public ConstraintReference translationX(float x) {
385         mTranslationX = x;
386         return this;
387     }
388 
389     // @TODO: add description
translationY(float y)390     public ConstraintReference translationY(float y) {
391         mTranslationY = y;
392         return this;
393     }
394 
395     // @TODO: add description
translationZ(float z)396     public ConstraintReference translationZ(float z) {
397         mTranslationZ = z;
398         return this;
399     }
400 
401     // @TODO: add description
scaleX(float x)402     public ConstraintReference scaleX(float x) {
403         mScaleX = x;
404         return this;
405     }
406 
407     // @TODO: add description
scaleY(float y)408     public ConstraintReference scaleY(float y) {
409         mScaleY = y;
410         return this;
411     }
412 
413     // @TODO: add description
alpha(float alpha)414     public ConstraintReference alpha(float alpha) {
415         mAlpha = alpha;
416         return this;
417     }
418 
419     // @TODO: add description
visibility(int visibility)420     public ConstraintReference visibility(int visibility) {
421         mVisibility = visibility;
422         return this;
423     }
424 
425     // @TODO: add description
left()426     public ConstraintReference left() {
427         if (mLeftToLeft != null) {
428             mLast = State.Constraint.LEFT_TO_LEFT;
429         } else {
430             mLast = State.Constraint.LEFT_TO_RIGHT;
431         }
432         return this;
433     }
434 
435     // @TODO: add description
right()436     public ConstraintReference right() {
437         if (mRightToLeft != null) {
438             mLast = State.Constraint.RIGHT_TO_LEFT;
439         } else {
440             mLast = State.Constraint.RIGHT_TO_RIGHT;
441         }
442         return this;
443     }
444 
445     // @TODO: add description
start()446     public ConstraintReference start() {
447         if (mStartToStart != null) {
448             mLast = State.Constraint.START_TO_START;
449         } else {
450             mLast = State.Constraint.START_TO_END;
451         }
452         return this;
453     }
454 
455     // @TODO: add description
end()456     public ConstraintReference end() {
457         if (mEndToStart != null) {
458             mLast = State.Constraint.END_TO_START;
459         } else {
460             mLast = State.Constraint.END_TO_END;
461         }
462         return this;
463     }
464 
465     // @TODO: add description
top()466     public ConstraintReference top() {
467         if (mTopToTop != null) {
468             mLast = State.Constraint.TOP_TO_TOP;
469         } else {
470             mLast = State.Constraint.TOP_TO_BOTTOM;
471         }
472         return this;
473     }
474 
475     // @TODO: add description
bottom()476     public ConstraintReference bottom() {
477         if (mBottomToTop != null) {
478             mLast = State.Constraint.BOTTOM_TO_TOP;
479         } else {
480             mLast = State.Constraint.BOTTOM_TO_BOTTOM;
481         }
482         return this;
483     }
484 
485     // @TODO: add description
baseline()486     public ConstraintReference baseline() {
487         mLast = State.Constraint.BASELINE_TO_BASELINE;
488         return this;
489     }
490 
491     // @TODO: add description
addCustomColor(String name, int color)492     public void addCustomColor(String name, int color) {
493         mCustomColors.put(name, color);
494     }
495 
496     // @TODO: add description
addCustomFloat(String name, float value)497     public void addCustomFloat(String name, float value) {
498         if (mCustomFloats == null) {
499             mCustomFloats = new HashMap<>();
500         }
501         mCustomFloats.put(name, value);
502     }
503 
dereference()504     private void dereference() {
505         mLeftToLeft = get(mLeftToLeft);
506         mLeftToRight = get(mLeftToRight);
507         mRightToLeft = get(mRightToLeft);
508         mRightToRight = get(mRightToRight);
509         mStartToStart = get(mStartToStart);
510         mStartToEnd = get(mStartToEnd);
511         mEndToStart = get(mEndToStart);
512         mEndToEnd = get(mEndToEnd);
513         mTopToTop = get(mTopToTop);
514         mTopToBottom = get(mTopToBottom);
515         mBottomToTop = get(mBottomToTop);
516         mBottomToBottom = get(mBottomToBottom);
517         mBaselineToBaseline = get(mBaselineToBaseline);
518         mBaselineToTop = get(mBaselineToTop);
519         mBaselineToBottom = get(mBaselineToBottom);
520     }
521 
522     // @TODO: add description
leftToLeft(Object reference)523     public ConstraintReference leftToLeft(Object reference) {
524         mLast = State.Constraint.LEFT_TO_LEFT;
525         mLeftToLeft = reference;
526         return this;
527     }
528 
529     // @TODO: add description
leftToRight(Object reference)530     public ConstraintReference leftToRight(Object reference) {
531         mLast = State.Constraint.LEFT_TO_RIGHT;
532         mLeftToRight = reference;
533         return this;
534     }
535 
536     // @TODO: add description
rightToLeft(Object reference)537     public ConstraintReference rightToLeft(Object reference) {
538         mLast = State.Constraint.RIGHT_TO_LEFT;
539         mRightToLeft = reference;
540         return this;
541     }
542 
543     // @TODO: add description
rightToRight(Object reference)544     public ConstraintReference rightToRight(Object reference) {
545         mLast = State.Constraint.RIGHT_TO_RIGHT;
546         mRightToRight = reference;
547         return this;
548     }
549 
550     // @TODO: add description
startToStart(Object reference)551     public ConstraintReference startToStart(Object reference) {
552         mLast = State.Constraint.START_TO_START;
553         mStartToStart = reference;
554         return this;
555     }
556 
557     // @TODO: add description
startToEnd(Object reference)558     public ConstraintReference startToEnd(Object reference) {
559         mLast = State.Constraint.START_TO_END;
560         mStartToEnd = reference;
561         return this;
562     }
563 
564     // @TODO: add description
endToStart(Object reference)565     public ConstraintReference endToStart(Object reference) {
566         mLast = State.Constraint.END_TO_START;
567         mEndToStart = reference;
568         return this;
569     }
570 
571     // @TODO: add description
endToEnd(Object reference)572     public ConstraintReference endToEnd(Object reference) {
573         mLast = State.Constraint.END_TO_END;
574         mEndToEnd = reference;
575         return this;
576     }
577 
578     // @TODO: add description
topToTop(Object reference)579     public ConstraintReference topToTop(Object reference) {
580         mLast = State.Constraint.TOP_TO_TOP;
581         mTopToTop = reference;
582         return this;
583     }
584 
585     // @TODO: add description
topToBottom(Object reference)586     public ConstraintReference topToBottom(Object reference) {
587         mLast = State.Constraint.TOP_TO_BOTTOM;
588         mTopToBottom = reference;
589         return this;
590     }
591 
topToBaseline(Object reference)592     ConstraintReference topToBaseline(Object reference) {
593         mLast = State.Constraint.TOP_TO_BASELINE;
594         mTopToBaseline = reference;
595         return this;
596     }
597 
598     // @TODO: add description
bottomToTop(Object reference)599     public ConstraintReference bottomToTop(Object reference) {
600         mLast = State.Constraint.BOTTOM_TO_TOP;
601         mBottomToTop = reference;
602         return this;
603     }
604 
605     // @TODO: add description
bottomToBottom(Object reference)606     public ConstraintReference bottomToBottom(Object reference) {
607         mLast = State.Constraint.BOTTOM_TO_BOTTOM;
608         mBottomToBottom = reference;
609         return this;
610     }
611 
bottomToBaseline(Object reference)612     ConstraintReference bottomToBaseline(Object reference) {
613         mLast = State.Constraint.BOTTOM_TO_BASELINE;
614         mBottomToBaseline = reference;
615         return this;
616     }
617 
618     // @TODO: add description
baselineToBaseline(Object reference)619     public ConstraintReference baselineToBaseline(Object reference) {
620         mLast = State.Constraint.BASELINE_TO_BASELINE;
621         mBaselineToBaseline = reference;
622         return this;
623     }
624 
625     // @TODO: add description
baselineToTop(Object reference)626     public ConstraintReference baselineToTop(Object reference) {
627         mLast = State.Constraint.BASELINE_TO_TOP;
628         mBaselineToTop = reference;
629         return this;
630     }
631 
632     // @TODO: add description
baselineToBottom(Object reference)633     public ConstraintReference baselineToBottom(Object reference) {
634         mLast = State.Constraint.BASELINE_TO_BOTTOM;
635         mBaselineToBottom = reference;
636         return this;
637     }
638 
639     // @TODO: add description
centerHorizontally(Object reference)640     public ConstraintReference centerHorizontally(Object reference) {
641         Object ref = get(reference);
642         mStartToStart = ref;
643         mEndToEnd = ref;
644         mLast = State.Constraint.CENTER_HORIZONTALLY;
645         mHorizontalBias = 0.5f;
646         return this;
647     }
648 
649     // @TODO: add description
centerVertically(Object reference)650     public ConstraintReference centerVertically(Object reference) {
651         Object ref = get(reference);
652         mTopToTop = ref;
653         mBottomToBottom = ref;
654         mLast = State.Constraint.CENTER_VERTICALLY;
655         mVerticalBias = 0.5f;
656         return this;
657     }
658 
659     // @TODO: add description
circularConstraint(Object reference, float angle, float distance)660     public ConstraintReference circularConstraint(Object reference, float angle, float distance) {
661         Object ref = get(reference);
662         mCircularConstraint = ref;
663         mCircularAngle = angle;
664         mCircularDistance = distance;
665         mLast = State.Constraint.CIRCULAR_CONSTRAINT;
666         return this;
667     }
668 
669     // @TODO: add description
width(Dimension dimension)670     public ConstraintReference width(Dimension dimension) {
671         return setWidth(dimension);
672     }
673 
674     // @TODO: add description
height(Dimension dimension)675     public ConstraintReference height(Dimension dimension) {
676         return setHeight(dimension);
677     }
678 
getWidth()679     public Dimension getWidth() {
680         return mHorizontalDimension;
681     }
682 
683     // @TODO: add description
setWidth(Dimension dimension)684     public ConstraintReference setWidth(Dimension dimension) {
685         mHorizontalDimension = dimension;
686         return this;
687     }
688 
getHeight()689     public Dimension getHeight() {
690         return mVerticalDimension;
691     }
692 
693     // @TODO: add description
setHeight(Dimension dimension)694     public ConstraintReference setHeight(Dimension dimension) {
695         mVerticalDimension = dimension;
696         return this;
697     }
698 
699     // @TODO: add description
margin(Object marginValue)700     public ConstraintReference margin(Object marginValue) {
701         return margin(mState.convertDimension(marginValue));
702     }
703 
704     // @TODO: add description
marginGone(Object marginGoneValue)705     public ConstraintReference marginGone(Object marginGoneValue) {
706         return marginGone(mState.convertDimension(marginGoneValue));
707     }
708 
709     // @TODO: add description
margin(int value)710     public ConstraintReference margin(int value) {
711         if (mLast != null) {
712             switch (mLast) {
713                 case LEFT_TO_LEFT:
714                 case LEFT_TO_RIGHT: {
715                     mMarginLeft = value;
716                 }
717                 break;
718                 case RIGHT_TO_LEFT:
719                 case RIGHT_TO_RIGHT: {
720                     mMarginRight = value;
721                 }
722                 break;
723                 case START_TO_START:
724                 case START_TO_END: {
725                     mMarginStart = value;
726                 }
727                 break;
728                 case END_TO_START:
729                 case END_TO_END: {
730                     mMarginEnd = value;
731                 }
732                 break;
733                 case TOP_TO_TOP:
734                 case TOP_TO_BOTTOM:
735                 case TOP_TO_BASELINE: {
736                     mMarginTop = value;
737                 }
738                 break;
739                 case BOTTOM_TO_TOP:
740                 case BOTTOM_TO_BOTTOM:
741                 case BOTTOM_TO_BASELINE: {
742                     mMarginBottom = value;
743                 }
744                 break;
745                 case BASELINE_TO_BOTTOM:
746                 case BASELINE_TO_TOP:
747                 case BASELINE_TO_BASELINE: {
748                     mMarginBaseline = value;
749                 }
750                 break;
751                 case CIRCULAR_CONSTRAINT: {
752                     mCircularDistance = value;
753                 }
754                 break;
755                 default:
756                     break;
757             }
758         } else {
759             mMarginLeft = value;
760             mMarginRight = value;
761             mMarginStart = value;
762             mMarginEnd = value;
763             mMarginTop = value;
764             mMarginBottom = value;
765         }
766         return this;
767     }
768 
769     // @TODO: add description
marginGone(int value)770     public ConstraintReference marginGone(int value) {
771         if (mLast != null) {
772             switch (mLast) {
773                 case LEFT_TO_LEFT:
774                 case LEFT_TO_RIGHT: {
775                     mMarginLeftGone = value;
776                 }
777                 break;
778                 case RIGHT_TO_LEFT:
779                 case RIGHT_TO_RIGHT: {
780                     mMarginRightGone = value;
781                 }
782                 break;
783                 case START_TO_START:
784                 case START_TO_END: {
785                     mMarginStartGone = value;
786                 }
787                 break;
788                 case END_TO_START:
789                 case END_TO_END: {
790                     mMarginEndGone = value;
791                 }
792                 break;
793                 case TOP_TO_TOP:
794                 case TOP_TO_BOTTOM:
795                 case TOP_TO_BASELINE: {
796                     mMarginTopGone = value;
797                 }
798                 break;
799                 case BOTTOM_TO_TOP:
800                 case BOTTOM_TO_BOTTOM:
801                 case BOTTOM_TO_BASELINE: {
802                     mMarginBottomGone = value;
803                 }
804                 break;
805                 case BASELINE_TO_TOP:
806                 case BASELINE_TO_BOTTOM:
807                 case BASELINE_TO_BASELINE: {
808                     mMarginBaselineGone = value;
809                 }
810                 break;
811                 default:
812                     break;
813             }
814         } else {
815             mMarginLeftGone = value;
816             mMarginRightGone = value;
817             mMarginStartGone = value;
818             mMarginEndGone = value;
819             mMarginTopGone = value;
820             mMarginBottomGone = value;
821         }
822         return this;
823     }
824 
825     // @TODO: add description
horizontalBias(float value)826     public ConstraintReference horizontalBias(float value) {
827         mHorizontalBias = value;
828         return this;
829     }
830 
831     // @TODO: add description
verticalBias(float value)832     public ConstraintReference verticalBias(float value) {
833         mVerticalBias = value;
834         return this;
835     }
836 
837     // @TODO: add description
bias(float value)838     public ConstraintReference bias(float value) {
839         if (mLast == null) {
840             return this;
841         }
842         switch (mLast) {
843             case CENTER_HORIZONTALLY:
844             case LEFT_TO_LEFT:
845             case LEFT_TO_RIGHT:
846             case RIGHT_TO_LEFT:
847             case RIGHT_TO_RIGHT:
848             case START_TO_START:
849             case START_TO_END:
850             case END_TO_START:
851             case END_TO_END: {
852                 mHorizontalBias = value;
853             }
854             break;
855             case CENTER_VERTICALLY:
856             case TOP_TO_TOP:
857             case TOP_TO_BOTTOM:
858             case TOP_TO_BASELINE:
859             case BOTTOM_TO_TOP:
860             case BOTTOM_TO_BOTTOM:
861             case BOTTOM_TO_BASELINE: {
862                 mVerticalBias = value;
863             }
864             break;
865             default:
866                 break;
867         }
868         return this;
869     }
870 
871     /**
872      * Clears all constraints.
873      */
clearAll()874     public ConstraintReference clearAll() {
875         mLeftToLeft = null;
876         mLeftToRight = null;
877         mMarginLeft = 0;
878         mRightToLeft = null;
879         mRightToRight = null;
880         mMarginRight = 0;
881         mStartToStart = null;
882         mStartToEnd = null;
883         mMarginStart = 0;
884         mEndToStart = null;
885         mEndToEnd = null;
886         mMarginEnd = 0;
887         mTopToTop = null;
888         mTopToBottom = null;
889         mMarginTop = 0;
890         mBottomToTop = null;
891         mBottomToBottom = null;
892         mMarginBottom = 0;
893         mBaselineToBaseline = null;
894         mCircularConstraint = null;
895         mHorizontalBias = 0.5f;
896         mVerticalBias = 0.5f;
897         mMarginLeftGone = 0;
898         mMarginRightGone = 0;
899         mMarginStartGone = 0;
900         mMarginEndGone = 0;
901         mMarginTopGone = 0;
902         mMarginBottomGone = 0;
903         return this;
904     }
905 
906     // @TODO: add description
clear()907     public ConstraintReference clear() {
908         if (mLast != null) {
909             switch (mLast) {
910                 case LEFT_TO_LEFT:
911                 case LEFT_TO_RIGHT: {
912                     mLeftToLeft = null;
913                     mLeftToRight = null;
914                     mMarginLeft = 0;
915                     mMarginLeftGone = 0;
916                 }
917                 break;
918                 case RIGHT_TO_LEFT:
919                 case RIGHT_TO_RIGHT: {
920                     mRightToLeft = null;
921                     mRightToRight = null;
922                     mMarginRight = 0;
923                     mMarginRightGone = 0;
924                 }
925                 break;
926                 case START_TO_START:
927                 case START_TO_END: {
928                     mStartToStart = null;
929                     mStartToEnd = null;
930                     mMarginStart = 0;
931                     mMarginStartGone = 0;
932                 }
933                 break;
934                 case END_TO_START:
935                 case END_TO_END: {
936                     mEndToStart = null;
937                     mEndToEnd = null;
938                     mMarginEnd = 0;
939                     mMarginEndGone = 0;
940                 }
941                 break;
942                 case TOP_TO_TOP:
943                 case TOP_TO_BOTTOM:
944                 case TOP_TO_BASELINE: {
945                     mTopToTop = null;
946                     mTopToBottom = null;
947                     mTopToBaseline = null;
948                     mMarginTop = 0;
949                     mMarginTopGone = 0;
950                 }
951                 break;
952                 case BOTTOM_TO_TOP:
953                 case BOTTOM_TO_BOTTOM:
954                 case BOTTOM_TO_BASELINE: {
955                     mBottomToTop = null;
956                     mBottomToBottom = null;
957                     mBottomToBaseline = null;
958                     mMarginBottom = 0;
959                     mMarginBottomGone = 0;
960                 }
961                 break;
962                 case BASELINE_TO_BASELINE: {
963                     mBaselineToBaseline = null;
964                 }
965                 break;
966                 case CIRCULAR_CONSTRAINT: {
967                     mCircularConstraint = null;
968                 }
969                 break;
970                 default:
971                     break;
972             }
973         } else {
974             clearAll();
975         }
976         return this;
977     }
978 
getTarget(Object target)979     private ConstraintWidget getTarget(Object target) {
980         if (target instanceof Reference) {
981             Reference referenceTarget = (Reference) target;
982             return referenceTarget.getConstraintWidget();
983         }
984         return null;
985     }
986 
applyConnection(ConstraintWidget widget, Object opaqueTarget, State.Constraint type)987     private void applyConnection(ConstraintWidget widget,
988             Object opaqueTarget,
989             State.Constraint type) {
990         ConstraintWidget target = getTarget(opaqueTarget);
991         if (target == null) {
992             return;
993         }
994         switch (type) {
995             // TODO: apply RTL
996             default:
997                 break;
998         }
999         switch (type) {
1000             case START_TO_START: {
1001                 widget.getAnchor(ConstraintAnchor.Type.LEFT).connect(target.getAnchor(
1002                         ConstraintAnchor.Type.LEFT), mMarginStart, mMarginStartGone, false);
1003             }
1004             break;
1005             case START_TO_END: {
1006                 widget.getAnchor(ConstraintAnchor.Type.LEFT).connect(target.getAnchor(
1007                         ConstraintAnchor.Type.RIGHT), mMarginStart, mMarginStartGone, false);
1008             }
1009             break;
1010             case END_TO_START: {
1011                 widget.getAnchor(ConstraintAnchor.Type.RIGHT).connect(target.getAnchor(
1012                         ConstraintAnchor.Type.LEFT), mMarginEnd, mMarginEndGone, false);
1013             }
1014             break;
1015             case END_TO_END: {
1016                 widget.getAnchor(ConstraintAnchor.Type.RIGHT).connect(target.getAnchor(
1017                         ConstraintAnchor.Type.RIGHT), mMarginEnd, mMarginEndGone, false);
1018             }
1019             break;
1020             case LEFT_TO_LEFT: {
1021                 widget.getAnchor(ConstraintAnchor.Type.LEFT).connect(target.getAnchor(
1022                         ConstraintAnchor.Type.LEFT), mMarginLeft, mMarginLeftGone, false);
1023             }
1024             break;
1025             case LEFT_TO_RIGHT: {
1026                 widget.getAnchor(ConstraintAnchor.Type.LEFT).connect(target.getAnchor(
1027                         ConstraintAnchor.Type.RIGHT), mMarginLeft, mMarginLeftGone, false);
1028             }
1029             break;
1030             case RIGHT_TO_LEFT: {
1031                 widget.getAnchor(ConstraintAnchor.Type.RIGHT).connect(target.getAnchor(
1032                         ConstraintAnchor.Type.LEFT), mMarginRight, mMarginRightGone, false);
1033             }
1034             break;
1035             case RIGHT_TO_RIGHT: {
1036                 widget.getAnchor(ConstraintAnchor.Type.RIGHT).connect(target.getAnchor(
1037                         ConstraintAnchor.Type.RIGHT), mMarginRight, mMarginRightGone, false);
1038             }
1039             break;
1040             case TOP_TO_TOP: {
1041                 widget.getAnchor(ConstraintAnchor.Type.TOP).connect(target.getAnchor(
1042                         ConstraintAnchor.Type.TOP), mMarginTop, mMarginTopGone, false);
1043             }
1044             break;
1045             case TOP_TO_BOTTOM: {
1046                 widget.getAnchor(ConstraintAnchor.Type.TOP).connect(target.getAnchor(
1047                         ConstraintAnchor.Type.BOTTOM), mMarginTop, mMarginTopGone, false);
1048             }
1049             break;
1050             case TOP_TO_BASELINE: {
1051                 widget.immediateConnect(ConstraintAnchor.Type.TOP, target,
1052                         ConstraintAnchor.Type.BASELINE, mMarginTop, mMarginTopGone);
1053             }
1054             break;
1055             case BOTTOM_TO_TOP: {
1056                 widget.getAnchor(ConstraintAnchor.Type.BOTTOM).connect(target.getAnchor(
1057                         ConstraintAnchor.Type.TOP), mMarginBottom, mMarginBottomGone, false);
1058             }
1059             break;
1060             case BOTTOM_TO_BOTTOM: {
1061                 widget.getAnchor(ConstraintAnchor.Type.BOTTOM).connect(target.getAnchor(
1062                         ConstraintAnchor.Type.BOTTOM), mMarginBottom, mMarginBottomGone, false);
1063             }
1064             break;
1065             case BOTTOM_TO_BASELINE: {
1066                 widget.immediateConnect(ConstraintAnchor.Type.BOTTOM, target,
1067                         ConstraintAnchor.Type.BASELINE, mMarginBottom, mMarginBottomGone);
1068             }
1069             break;
1070             case BASELINE_TO_BASELINE: {
1071                 widget.immediateConnect(ConstraintAnchor.Type.BASELINE, target,
1072                         ConstraintAnchor.Type.BASELINE, mMarginBaseline, mMarginBaselineGone);
1073             }
1074             break;
1075             case BASELINE_TO_TOP: {
1076                 widget.immediateConnect(ConstraintAnchor.Type.BASELINE,
1077                         target, ConstraintAnchor.Type.TOP, mMarginBaseline, mMarginBaselineGone);
1078             }
1079             break;
1080             case BASELINE_TO_BOTTOM: {
1081                 widget.immediateConnect(ConstraintAnchor.Type.BASELINE, target,
1082                         ConstraintAnchor.Type.BOTTOM, mMarginBaseline, mMarginBaselineGone);
1083             }
1084             break;
1085             case CIRCULAR_CONSTRAINT: {
1086                 widget.connectCircularConstraint(target, mCircularAngle, (int) mCircularDistance);
1087             }
1088             break;
1089             default:
1090                 break;
1091         }
1092     }
1093 
1094     /**
1095      * apply all the constraints attributes of the mConstraintWidget
1096      */
applyWidgetConstraints()1097     public void applyWidgetConstraints() {
1098         applyConnection(mConstraintWidget, mLeftToLeft, State.Constraint.LEFT_TO_LEFT);
1099         applyConnection(mConstraintWidget, mLeftToRight, State.Constraint.LEFT_TO_RIGHT);
1100         applyConnection(mConstraintWidget, mRightToLeft, State.Constraint.RIGHT_TO_LEFT);
1101         applyConnection(mConstraintWidget, mRightToRight, State.Constraint.RIGHT_TO_RIGHT);
1102         applyConnection(mConstraintWidget, mStartToStart, State.Constraint.START_TO_START);
1103         applyConnection(mConstraintWidget, mStartToEnd, State.Constraint.START_TO_END);
1104         applyConnection(mConstraintWidget, mEndToStart, State.Constraint.END_TO_START);
1105         applyConnection(mConstraintWidget, mEndToEnd, State.Constraint.END_TO_END);
1106         applyConnection(mConstraintWidget, mTopToTop, State.Constraint.TOP_TO_TOP);
1107         applyConnection(mConstraintWidget, mTopToBottom, State.Constraint.TOP_TO_BOTTOM);
1108         applyConnection(mConstraintWidget, mTopToBaseline, State.Constraint.TOP_TO_BASELINE);
1109         applyConnection(mConstraintWidget, mBottomToTop, State.Constraint.BOTTOM_TO_TOP);
1110         applyConnection(mConstraintWidget, mBottomToBottom, State.Constraint.BOTTOM_TO_BOTTOM);
1111         applyConnection(mConstraintWidget, mBottomToBaseline, State.Constraint.BOTTOM_TO_BASELINE);
1112         applyConnection(mConstraintWidget, mBaselineToBaseline,
1113                 State.Constraint.BASELINE_TO_BASELINE);
1114         applyConnection(mConstraintWidget, mBaselineToTop, State.Constraint.BASELINE_TO_TOP);
1115         applyConnection(mConstraintWidget, mBaselineToBottom, State.Constraint.BASELINE_TO_BOTTOM);
1116         applyConnection(mConstraintWidget, mCircularConstraint,
1117                 State.Constraint.CIRCULAR_CONSTRAINT);
1118     }
1119 
1120     // @TODO: add description
1121     @Override
apply()1122     public void apply() {
1123         if (mConstraintWidget == null) {
1124             return;
1125         }
1126         if (mFacade != null) {
1127             mFacade.apply();
1128         }
1129         mHorizontalDimension.apply(mState, mConstraintWidget, HORIZONTAL);
1130         mVerticalDimension.apply(mState, mConstraintWidget, VERTICAL);
1131         dereference();
1132 
1133         applyWidgetConstraints();
1134 
1135         if (mHorizontalChainStyle != ConstraintWidget.CHAIN_SPREAD) {
1136             mConstraintWidget.setHorizontalChainStyle(mHorizontalChainStyle);
1137         }
1138         if (mVerticalChainStyle != ConstraintWidget.CHAIN_SPREAD) {
1139             mConstraintWidget.setVerticalChainStyle(mVerticalChainStyle);
1140         }
1141         if (mHorizontalChainWeight != UNKNOWN) {
1142             mConstraintWidget.setHorizontalWeight(mHorizontalChainWeight);
1143         }
1144         if (mVerticalChainWeight != UNKNOWN) {
1145             mConstraintWidget.setVerticalWeight(mVerticalChainWeight);
1146         }
1147 
1148         mConstraintWidget.setHorizontalBiasPercent(mHorizontalBias);
1149         mConstraintWidget.setVerticalBiasPercent(mVerticalBias);
1150 
1151         mConstraintWidget.frame.pivotX = mPivotX;
1152         mConstraintWidget.frame.pivotY = mPivotY;
1153         mConstraintWidget.frame.rotationX = mRotationX;
1154         mConstraintWidget.frame.rotationY = mRotationY;
1155         mConstraintWidget.frame.rotationZ = mRotationZ;
1156         mConstraintWidget.frame.translationX = mTranslationX;
1157         mConstraintWidget.frame.translationY = mTranslationY;
1158         mConstraintWidget.frame.translationZ = mTranslationZ;
1159         mConstraintWidget.frame.scaleX = mScaleX;
1160         mConstraintWidget.frame.scaleY = mScaleY;
1161         mConstraintWidget.frame.alpha = mAlpha;
1162         mConstraintWidget.frame.visibility = mVisibility;
1163         mConstraintWidget.setVisibility(mVisibility);
1164         mConstraintWidget.frame.setMotionAttributes(mMotionProperties);
1165         if (mCustomColors != null) {
1166             for (String key : mCustomColors.keySet()) {
1167                 Integer color = mCustomColors.get(key);
1168                 mConstraintWidget.frame.setCustomAttribute(key,
1169                         TypedValues.Custom.TYPE_COLOR, color);
1170             }
1171         }
1172         if (mCustomFloats != null) {
1173             for (String key : mCustomFloats.keySet()) {
1174                 float value = mCustomFloats.get(key);
1175                 mConstraintWidget.frame.setCustomAttribute(key,
1176                         TypedValues.Custom.TYPE_FLOAT, value);
1177             }
1178         }
1179     }
1180 }
1181