1 /* 2 * Copyright (C) 2020 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 package androidx.constraintlayout.core.widgets.analyzer; 17 18 import static androidx.constraintlayout.core.widgets.ConstraintWidget.BOTH; 19 import static androidx.constraintlayout.core.widgets.ConstraintWidget.HORIZONTAL; 20 import static androidx.constraintlayout.core.widgets.ConstraintWidget.VERTICAL; 21 22 import androidx.constraintlayout.core.LinearSystem; 23 import androidx.constraintlayout.core.widgets.Chain; 24 import androidx.constraintlayout.core.widgets.ConstraintWidget; 25 import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer; 26 27 import java.lang.ref.WeakReference; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 31 /** 32 * Represents a group of widget for the grouping mechanism. 33 */ 34 public class WidgetGroup { 35 private static final boolean DEBUG = false; 36 ArrayList<ConstraintWidget> mWidgets = new ArrayList<>(); 37 static int sCount = 0; 38 int mId = -1; 39 boolean mAuthoritative = false; 40 int mOrientation = HORIZONTAL; 41 ArrayList<MeasureResult> mResults = null; 42 private int mMoveTo = -1; 43 WidgetGroup(int orientation)44 public WidgetGroup(int orientation) { 45 mId = sCount++; 46 this.mOrientation = orientation; 47 } 48 getOrientation()49 public int getOrientation() { 50 return mOrientation; 51 } 52 getId()53 public int getId() { 54 return mId; 55 } 56 57 // @TODO: add description add(ConstraintWidget widget)58 public boolean add(ConstraintWidget widget) { 59 if (mWidgets.contains(widget)) { 60 return false; 61 } 62 mWidgets.add(widget); 63 return true; 64 } 65 setAuthoritative(boolean isAuthoritative)66 public void setAuthoritative(boolean isAuthoritative) { 67 mAuthoritative = isAuthoritative; 68 } 69 isAuthoritative()70 public boolean isAuthoritative() { 71 return mAuthoritative; 72 } 73 getOrientationString()74 private String getOrientationString() { 75 if (mOrientation == HORIZONTAL) { 76 return "Horizontal"; 77 } else if (mOrientation == VERTICAL) { 78 return "Vertical"; 79 } else if (mOrientation == BOTH) { 80 return "Both"; 81 } 82 return "Unknown"; 83 } 84 85 @Override toString()86 public String toString() { 87 String ret = getOrientationString() + " [" + mId + "] <"; 88 for (ConstraintWidget widget : mWidgets) { 89 ret += " " + widget.getDebugName(); 90 } 91 ret += " >"; 92 return ret; 93 } 94 95 // @TODO: add description moveTo(int orientation, WidgetGroup widgetGroup)96 public void moveTo(int orientation, WidgetGroup widgetGroup) { 97 if (DEBUG) { 98 System.out.println("Move all widgets (" + this + ") from " 99 + mId + " to " + widgetGroup.getId() + "(" + widgetGroup + ")"); 100 System.out.println("" + 101 "do not call "+ measureWrap( orientation, new ConstraintWidget())); 102 } 103 for (ConstraintWidget widget : mWidgets) { 104 widgetGroup.add(widget); 105 if (orientation == HORIZONTAL) { 106 widget.horizontalGroup = widgetGroup.getId(); 107 } else { 108 widget.verticalGroup = widgetGroup.getId(); 109 } 110 } 111 mMoveTo = widgetGroup.mId; 112 } 113 114 // @TODO: add description clear()115 public void clear() { 116 mWidgets.clear(); 117 } 118 measureWrap(int orientation, ConstraintWidget widget)119 private int measureWrap(int orientation, ConstraintWidget widget) { 120 ConstraintWidget.DimensionBehaviour behaviour = widget.getDimensionBehaviour(orientation); 121 if (behaviour == ConstraintWidget.DimensionBehaviour.WRAP_CONTENT 122 || behaviour == ConstraintWidget.DimensionBehaviour.MATCH_PARENT 123 || behaviour == ConstraintWidget.DimensionBehaviour.FIXED) { 124 int dimension; 125 if (orientation == HORIZONTAL) { 126 dimension = widget.getWidth(); 127 } else { 128 dimension = widget.getHeight(); 129 } 130 return dimension; 131 } 132 return -1; 133 } 134 135 // @TODO: add description measureWrap(LinearSystem system, int orientation)136 public int measureWrap(LinearSystem system, int orientation) { 137 int count = mWidgets.size(); 138 if (count == 0) { 139 return 0; 140 } 141 // TODO: add direct wrap computation for simpler cases instead of calling the solver 142 return solverMeasure(system, mWidgets, orientation); 143 } 144 solverMeasure(LinearSystem system, ArrayList<ConstraintWidget> widgets, int orientation)145 private int solverMeasure(LinearSystem system, 146 ArrayList<ConstraintWidget> widgets, 147 int orientation) { 148 ConstraintWidgetContainer container = 149 (ConstraintWidgetContainer) widgets.get(0).getParent(); 150 system.reset(); 151 @SuppressWarnings("unused") boolean prevDebug = LinearSystem.FULL_DEBUG; 152 container.addToSolver(system, false); 153 for (int i = 0; i < widgets.size(); i++) { 154 ConstraintWidget widget = widgets.get(i); 155 widget.addToSolver(system, false); 156 } 157 if (orientation == HORIZONTAL) { 158 if (container.mHorizontalChainsSize > 0) { 159 Chain.applyChainConstraints(container, system, widgets, HORIZONTAL); 160 } 161 } 162 if (orientation == VERTICAL) { 163 if (container.mVerticalChainsSize > 0) { 164 Chain.applyChainConstraints(container, system, widgets, VERTICAL); 165 } 166 } 167 168 try { 169 system.minimize(); 170 } catch (Exception e) { 171 //TODO remove fancy version of e.printStackTrace() 172 System.err.println(e.toString()+"\n"+Arrays.toString(e.getStackTrace()) 173 .replace("["," at ") 174 .replace(",","\n at") 175 .replace("]","")); 176 } 177 178 // save results 179 mResults = new ArrayList<>(); 180 for (int i = 0; i < widgets.size(); i++) { 181 ConstraintWidget widget = widgets.get(i); 182 MeasureResult result = new MeasureResult(widget, system, orientation); 183 mResults.add(result); 184 } 185 186 if (orientation == HORIZONTAL) { 187 int left = system.getObjectVariableValue(container.mLeft); 188 int right = system.getObjectVariableValue(container.mRight); 189 system.reset(); 190 return right - left; 191 } else { 192 int top = system.getObjectVariableValue(container.mTop); 193 int bottom = system.getObjectVariableValue(container.mBottom); 194 system.reset(); 195 return bottom - top; 196 } 197 } 198 setOrientation(int orientation)199 public void setOrientation(int orientation) { 200 this.mOrientation = orientation; 201 } 202 203 // @TODO: add description apply()204 public void apply() { 205 if (mResults == null) { 206 return; 207 } 208 if (!mAuthoritative) { 209 return; 210 } 211 for (int i = 0; i < mResults.size(); i++) { 212 MeasureResult result = mResults.get(i); 213 result.apply(); 214 } 215 } 216 217 // @TODO: add description intersectWith(WidgetGroup group)218 public boolean intersectWith(WidgetGroup group) { 219 for (int i = 0; i < mWidgets.size(); i++) { 220 ConstraintWidget widget = mWidgets.get(i); 221 if (group.contains(widget)) { 222 return true; 223 } 224 } 225 return false; 226 } 227 contains(ConstraintWidget widget)228 private boolean contains(ConstraintWidget widget) { 229 return mWidgets.contains(widget); 230 } 231 232 // @TODO: add description size()233 public int size() { 234 return mWidgets.size(); 235 } 236 237 // @TODO: add description cleanup(ArrayList<WidgetGroup> dependencyLists)238 public void cleanup(ArrayList<WidgetGroup> dependencyLists) { 239 final int count = mWidgets.size(); 240 if (mMoveTo != -1 && count > 0) { 241 for (int i = 0; i < dependencyLists.size(); i++) { 242 WidgetGroup group = dependencyLists.get(i); 243 if (mMoveTo == group.mId) { 244 moveTo(mOrientation, group); 245 } 246 } 247 } 248 if (count == 0) { 249 dependencyLists.remove(this); 250 return; 251 } 252 } 253 254 255 static class MeasureResult { 256 WeakReference<ConstraintWidget> mWidgetRef; 257 int mLeft; 258 int mTop; 259 int mRight; 260 int mBottom; 261 int mBaseline; 262 int mOrientation; 263 MeasureResult(ConstraintWidget widget, LinearSystem system, int orientation)264 MeasureResult(ConstraintWidget widget, LinearSystem system, int orientation) { 265 mWidgetRef = new WeakReference<>(widget); 266 mLeft = system.getObjectVariableValue(widget.mLeft); 267 mTop = system.getObjectVariableValue(widget.mTop); 268 mRight = system.getObjectVariableValue(widget.mRight); 269 mBottom = system.getObjectVariableValue(widget.mBottom); 270 mBaseline = system.getObjectVariableValue(widget.mBaseline); 271 this.mOrientation = orientation; 272 } 273 apply()274 public void apply() { 275 ConstraintWidget widget = mWidgetRef.get(); 276 if (widget != null) { 277 widget.setFinalFrame(mLeft, mTop, mRight, mBottom, mBaseline, mOrientation); 278 } 279 } 280 } 281 } 282