diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/helper/widget/LogJson.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/helper/widget/LogJson.java
new file mode 100644
index 000000000..4e4fb2424
--- /dev/null
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/helper/widget/LogJson.java
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.helper.widget;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import static androidx.constraintlayout.widget.ConstraintSet.Layout.UNSET_GONE_MARGIN;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Environment;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.constraintlayout.motion.widget.Debug;
+import androidx.constraintlayout.widget.ConstraintAttribute;
+import androidx.constraintlayout.widget.ConstraintHelper;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
+import androidx.constraintlayout.widget.R;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This is a class is a debugging/logging utility to write out the constraints in JSON
+ * This is used for debugging purposes
+ *
+ * logJsonTo - defines the output log console or "fileName"
+ * logJsonMode - mode one of:
+ * periodic , delayed , layout or api
+ * logJsonDelay - the duration of the delay or the delay between repeated logs
+ *
+ * logJsonTo supports:
+ *
+ * log - logs using log.v("JSON5", ...)
+ * console - logs using System.out.println(...)
+ * [fileName] - will write to /storage/emulated/0/Download/[fileName].json5
+ *
+ * logJsonMode modes are:
+ *
+ * periodic - after window is attached will log every delay ms
+ * delayed - log once after delay ms
+ * layout - log every time there is a layout call
+ * api - do not automatically log developer will call writeLog
+ *
+ *
+ * The defaults are:
+ *
+ * logJsonTo="log"
+ * logJsonMode="delayed"
+ * logJsonDelay="1000"
+ *
+ * Usage:
+ *
+ *
+ * {@code
+ *
+ * }
+ *
+ *
+ */
+public class LogJson extends ConstraintHelper {
+ private static final String TAG = "JSON5";
+ private int mDelay = 1000;
+ private int mMode = LOG_DELAYED;
+ private String mLogToFile = null;
+ private boolean mLogConsole = true;
+
+ public static final int LOG_PERIODIC = 1;
+ public static final int LOG_DELAYED = 2;
+ public static final int LOG_LAYOUT = 3;
+ public static final int LOG_API = 4;
+ private boolean mPeriodic = false;
+
+ public LogJson(@androidx.annotation.NonNull Context context) {
+ super(context);
+ }
+
+ public LogJson(@androidx.annotation.NonNull Context context,
+ @androidx.annotation.Nullable AttributeSet attrs) {
+ super(context, attrs);
+ initLogJson(attrs);
+
+ }
+
+ public LogJson(@androidx.annotation.NonNull Context context,
+ @androidx.annotation.Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initLogJson(attrs);
+ }
+
+ private void initLogJson(AttributeSet attrs) {
+
+ if (attrs != null) {
+ TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.LogJson);
+ final int count = a.getIndexCount();
+ for (int i = 0; i < count; i++) {
+ int attr = a.getIndex(i);
+ if (attr == R.styleable.LogJson_logJsonDelay) {
+ mDelay = a.getInt(attr, mDelay);
+ } else if (attr == R.styleable.LogJson_logJsonMode) {
+ mMode = a.getInt(attr, mMode);
+ } else if (attr == R.styleable.LogJson_logJsonTo) {
+ TypedValue v = a.peekValue(attr);
+ if (v.type == TypedValue.TYPE_STRING) {
+ String value = a.getString(attr);
+ } else {
+ int value = a.getInt(attr, 0);
+ mLogConsole = value == 2;
+ }
+ }
+ }
+ a.recycle();
+ }
+ setVisibility(GONE);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ switch (mMode) {
+ case LOG_PERIODIC:
+ mPeriodic = true;
+ this.postDelayed(this::periodic, mDelay);
+ break;
+ case LOG_DELAYED:
+ this.postDelayed(this::writeLog, mDelay);
+ break;
+ case LOG_LAYOUT:
+ ConstraintLayout cl = (ConstraintLayout) getParent();
+ cl.addOnLayoutChangeListener((v, a, b, c, d, e, f, g, h) -> logOnLayout());
+ }
+ }
+
+ private void logOnLayout() {
+ if (mMode == LOG_LAYOUT) {
+ writeLog();
+ }
+ }
+
+ /**
+ * Set the duration of periodic logging of constraints
+ *
+ * @param duration the time in ms between writing files
+ */
+ public void setPeriodicDuration(int duration) {
+ mDelay = duration;
+ }
+
+ /**
+ * Start periodic sampling
+ */
+ public void periodicStart() {
+ mPeriodic = true;
+ this.postDelayed(this::periodic, mDelay);
+ }
+
+ /**
+ * Stop periodic sampling
+ */
+ public void periodicStop() {
+ mPeriodic = false;
+ }
+
+ private void periodic() {
+ if (mPeriodic) {
+ writeLog();
+ this.postDelayed(this::periodic, mDelay);
+ }
+ }
+
+ /**
+ * This writes a JSON5 representation of the constraintSet
+ */
+ public void writeLog() {
+ String str = asString((ConstraintLayout) this.getParent());
+ if (mLogToFile == null) {
+ if (mLogConsole) {
+ System.out.println(str);
+ } else {
+ logBigString(str);
+ }
+ } else {
+ String name = toFile(str, mLogToFile);
+ Log.v("JSON", "\"" + name + "\" written!");
+ }
+ }
+
+ /**
+ * This writes the JSON5 description of the constraintLayout to a file named fileName.json5
+ * in the download directory which can be pulled with:
+ * "adb pull "/storage/emulated/0/Download/" ."
+ *
+ * @param str String to write as a file
+ * @param fileName file name
+ * @return full path name of file
+ */
+ private static String toFile(String str, String fileName) {
+ FileOutputStream outputStream;
+ if (!fileName.endsWith(".json5")) {
+ fileName += ".json5";
+ }
+ try {
+ File down =
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+ File file = new File(down, fileName);
+ outputStream = new FileOutputStream(file);
+ outputStream.write(str.getBytes());
+ outputStream.close();
+ return file.getCanonicalPath();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @SuppressLint("LogConditional")
+ private void logBigString(String str) {
+ int len = str.length();
+ for (int i = 0; i < len; i++) {
+ int k = str.indexOf("\n", i);
+ if (k == -1) {
+ Log.v(TAG, str.substring(i));
+ break;
+ }
+ Log.v(TAG, str.substring(i, k));
+ i = k;
+ }
+ }
+
+ /**
+ * Get a JSON5 String that represents the Constraints in a running ConstraintLayout
+ *
+ * @param constraintLayout its constraints are converted to a string
+ * @return JSON5 string
+ */
+ private static String asString(ConstraintLayout constraintLayout) {
+ JsonWriter c = new JsonWriter();
+ return c.constraintLayoutToJson(constraintLayout);
+ }
+
+ // ================================== JSON writer==============================================
+
+ private static class JsonWriter {
+ public static final int UNSET = ConstraintLayout.LayoutParams.UNSET;
+ ConstraintSet mSet;
+ Writer mWriter;
+ ConstraintLayout mLayout;
+ Context mContext;
+ int mUnknownCount = 0;
+ final String mLEFT = "left";
+ final String mRIGHT = "right";
+ final String mBASELINE = "baseline";
+ final String mBOTTOM = "bottom";
+ final String mTOP = "top";
+ final String mSTART = "start";
+ final String mEND = "end";
+ private static final String INDENT = " ";
+ private static final String SMALL_INDENT = " ";
+ HashMap mIdMap = new HashMap<>();
+ private static final String LOG_JSON = LogJson.class.getSimpleName();
+ private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
+ HashMap mNames = new HashMap<>();
+
+ private static int generateViewId() {
+ final int max_id = 0x00FFFFFF;
+ for (;;) {
+ final int result = sNextGeneratedId.get();
+ int newValue = result + 1;
+ if (newValue > max_id) {
+ newValue = 1;
+ }
+ if (sNextGeneratedId.compareAndSet(result, newValue)) {
+ return result;
+ }
+ }
+ }
+
+ private String constraintLayoutToJson(ConstraintLayout constraintLayout) {
+ StringWriter writer = new StringWriter();
+
+ int count = constraintLayout.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View v = constraintLayout.getChildAt(i);
+ String name = v.getClass().getSimpleName();
+ int id = v.getId();
+ if (id == -1) {
+ if (android.os.Build.VERSION.SDK_INT
+ >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ id = View.generateViewId();
+ } else {
+ id = generateViewId();
+ }
+ v.setId(id);
+ if (!LOG_JSON.equals(name)) {
+ name = "noid_" + name;
+ }
+ mNames.put(id, name);
+ } else if (LOG_JSON.equals(name)) {
+ mNames.put(id, name);
+ }
+ }
+ writer.append("{\n");
+
+ writeWidgets(writer, constraintLayout);
+ writer.append(" ConstraintSet:{\n");
+ ConstraintSet set = new ConstraintSet();
+ set.clone(constraintLayout);
+ String name =
+ (constraintLayout.getId() == -1) ? "cset" : Debug.getName(constraintLayout);
+ try {
+ writer.append(name + ":");
+ setup(writer, set, constraintLayout);
+ writeLayout();
+ writer.append("\n");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ writer.append(" }\n");
+ writer.append("}\n");
+ return writer.toString();
+ }
+
+ private void writeWidgets(StringWriter writer, ConstraintLayout constraintLayout) {
+ writer.append("Widgets:{\n");
+ int count = constraintLayout.getChildCount();
+
+ for (int i = -1; i < count; i++) {
+ View v = (i == -1) ? constraintLayout : constraintLayout.getChildAt(i);
+ int id = v.getId();
+ if (LOG_JSON.equals(v.getClass().getSimpleName())) {
+ continue;
+ }
+ String name = (mNames.containsKey(id)) ? mNames.get(id)
+ : ((i == -1) ? "parent" : Debug.getName(v));
+ String cname = v.getClass().getSimpleName();
+ String bounds = ", bounds: [" + v.getLeft() + ", " + v.getTop()
+ + ", " + v.getRight() + ", " + v.getBottom() + "]},\n";
+ writer.append(" " + name + ": { ");
+ if (i == -1) {
+ writer.append("type: '" + v.getClass().getSimpleName() + "' , ");
+
+ try {
+ ViewGroup.LayoutParams p = (ViewGroup.LayoutParams) v.getLayoutParams();
+ String wrap = "'WRAP_CONTENT'";
+ String match = "'MATCH_PARENT'";
+ String w = p.width == MATCH_PARENT ? match :
+ (p.width == WRAP_CONTENT) ? wrap : p.width + "";
+ writer.append("width: " + w + ", ");
+ String h = p.height == MATCH_PARENT ? match :
+ (p.height == WRAP_CONTENT) ? wrap : p.height + "";
+ writer.append("height: ").append(h);
+ } catch (Exception e) {
+ }
+ } else if (cname.contains("Text")) {
+ if (v instanceof TextView) {
+ writer.append("type: 'Text', label: '"
+ + escape(((TextView) v).getText().toString()) + "'");
+ } else {
+ writer.append("type: 'Text' },\n");
+ }
+ } else if (cname.contains("Button")) {
+ if (v instanceof Button) {
+ writer.append("type: 'Button', label: '" + ((Button) v).getText() + "'");
+ } else
+ writer.append("type: 'Button'");
+ } else if (cname.contains("Image")) {
+ writer.append("type: 'Image'");
+ } else if (cname.contains("View")) {
+ writer.append("type: 'Box'");
+ } else {
+ writer.append("type: '" + v.getClass().getSimpleName() + "'");
+ }
+ writer.append(bounds);
+ }
+ writer.append("},\n");
+ }
+
+ private static String escape(String str) {
+ return str.replaceAll("'", "\\'");
+ }
+
+ JsonWriter() {
+
+ }
+
+ void setup(Writer writer,
+ ConstraintSet set,
+ ConstraintLayout layout) throws IOException {
+ this.mWriter = writer;
+ this.mLayout = layout;
+ this.mContext = layout.getContext();
+ this.mSet = set;
+ set.getConstraint(2);
+ }
+
+ private int[] getIDs() {
+ return mSet.getKnownIds();
+ }
+
+ private ConstraintSet.Constraint getConstraint(int id) {
+ return mSet.getConstraint(id);
+ }
+
+ private void writeLayout() throws IOException {
+ mWriter.write("{\n");
+ for (Integer id : getIDs()) {
+ ConstraintSet.Constraint c = getConstraint(id);
+ String idName = getSimpleName(id);
+ if (LOG_JSON.equals(idName)) { // skip LogJson it is for used to log
+ continue;
+ }
+ mWriter.write(SMALL_INDENT + idName + ":{\n");
+ ConstraintSet.Layout l = c.layout;
+ if (l.mReferenceIds != null) {
+ StringBuilder ref =
+ new StringBuilder("type: '_" + idName + "_' , contains: [");
+ for (int r = 0; r < l.mReferenceIds.length; r++) {
+ int rid = l.mReferenceIds[r];
+ ref.append((r == 0) ? "" : ", ").append(getName(rid));
+ }
+ mWriter.write(ref + "]\n");
+ }
+ if (l.mReferenceIdString != null) {
+ StringBuilder ref =
+ new StringBuilder(SMALL_INDENT + "type: '???' , contains: [");
+ String[] rids = l.mReferenceIdString.split(",");
+ for (int r = 0; r < rids.length; r++) {
+ String rid = rids[r];
+ ref.append((r == 0) ? "" : ", ").append("`").append(rid).append("`");
+ }
+ mWriter.write(ref + "]\n");
+ }
+ writeDimension("height", l.mHeight, l.heightDefault, l.heightPercent,
+ l.heightMin, l.heightMax, l.constrainedHeight);
+ writeDimension("width", l.mWidth, l.widthDefault, l.widthPercent,
+ l.widthMin, l.widthMax, l.constrainedWidth);
+
+ writeConstraint(mLEFT, l.leftToLeft, mLEFT, l.leftMargin, l.goneLeftMargin);
+ writeConstraint(mLEFT, l.leftToRight, mRIGHT, l.leftMargin, l.goneLeftMargin);
+ writeConstraint(mRIGHT, l.rightToLeft, mLEFT, l.rightMargin, l.goneRightMargin);
+ writeConstraint(mRIGHT, l.rightToRight, mRIGHT, l.rightMargin, l.goneRightMargin);
+ writeConstraint(mBASELINE, l.baselineToBaseline, mBASELINE, UNSET,
+ l.goneBaselineMargin);
+ writeConstraint(mBASELINE, l.baselineToTop, mTOP, UNSET, l.goneBaselineMargin);
+ writeConstraint(mBASELINE, l.baselineToBottom,
+ mBOTTOM, UNSET, l.goneBaselineMargin);
+
+ writeConstraint(mTOP, l.topToBottom, mBOTTOM, l.topMargin, l.goneTopMargin);
+ writeConstraint(mTOP, l.topToTop, mTOP, l.topMargin, l.goneTopMargin);
+ writeConstraint(mBOTTOM, l.bottomToBottom, mBOTTOM, l.bottomMargin,
+ l.goneBottomMargin);
+ writeConstraint(mBOTTOM, l.bottomToTop, mTOP, l.bottomMargin, l.goneBottomMargin);
+ writeConstraint(mSTART, l.startToStart, mSTART, l.startMargin, l.goneStartMargin);
+ writeConstraint(mSTART, l.startToEnd, mEND, l.startMargin, l.goneStartMargin);
+ writeConstraint(mEND, l.endToStart, mSTART, l.endMargin, l.goneEndMargin);
+ writeConstraint(mEND, l.endToEnd, mEND, l.endMargin, l.goneEndMargin);
+
+ writeVariable("horizontalBias", l.horizontalBias, 0.5f);
+ writeVariable("verticalBias", l.verticalBias, 0.5f);
+
+ writeCircle(l.circleConstraint, l.circleAngle, l.circleRadius);
+
+ writeGuideline(l.orientation, l.guideBegin, l.guideEnd, l.guidePercent);
+ writeVariable("dimensionRatio", l.dimensionRatio);
+ writeVariable("barrierMargin", l.mBarrierMargin);
+ writeVariable("type", l.mHelperType);
+ writeVariable("ReferenceId", l.mReferenceIdString);
+ writeVariable("mBarrierAllowsGoneWidgets",
+ l.mBarrierAllowsGoneWidgets, true);
+ writeVariable("WrapBehavior", l.mWrapBehavior);
+
+ writeVariable("verticalWeight", l.verticalWeight);
+ writeVariable("horizontalWeight", l.horizontalWeight);
+ writeVariable("horizontalChainStyle", l.horizontalChainStyle);
+ writeVariable("verticalChainStyle", l.verticalChainStyle);
+ writeVariable("barrierDirection", l.mBarrierDirection);
+ if (l.mReferenceIds != null) {
+ writeVariable("ReferenceIds", l.mReferenceIds);
+ }
+ writeTransform(c.transform);
+ writeCustom(c.mCustomConstraints);
+
+ mWriter.write(" },\n");
+ }
+ mWriter.write("},\n");
+ }
+
+ private void writeTransform(ConstraintSet.Transform transform) throws IOException {
+ if (transform.applyElevation) {
+ writeVariable("elevation", transform.elevation);
+ }
+ writeVariable("rotationX", transform.rotationX, 0);
+ writeVariable("rotationY", transform.rotationY, 0);
+ writeVariable("rotationZ", transform.rotation, 0);
+ writeVariable("scaleX", transform.scaleX, 1);
+ writeVariable("scaleY", transform.scaleY, 1);
+ writeVariable("translationX", transform.translationX, 0);
+ writeVariable("translationY", transform.translationY, 0);
+ writeVariable("translationZ", transform.translationZ, 0);
+ }
+
+ private void writeCustom(HashMap cset) throws IOException {
+ if (cset != null && cset.size() > 0) {
+ mWriter.write(INDENT + "custom: {\n");
+ for (String s : cset.keySet()) {
+ ConstraintAttribute attr = cset.get(s);
+ if (attr == null) {
+ continue;
+ }
+ String custom = INDENT + SMALL_INDENT + attr.getName() + ": ";
+ switch (attr.getType()) {
+ case INT_TYPE:
+ custom += attr.getIntegerValue();
+ break;
+ case COLOR_TYPE:
+ custom += colorString(attr.getColorValue());
+ break;
+ case FLOAT_TYPE:
+ custom += attr.getFloatValue();
+ break;
+ case STRING_TYPE:
+ custom += "'" + attr.getStringValue() + "'";
+ break;
+ case DIMENSION_TYPE:
+ custom = custom + attr.getFloatValue();
+ break;
+ case REFERENCE_TYPE:
+ case COLOR_DRAWABLE_TYPE:
+ case BOOLEAN_TYPE:
+ custom = null;
+ }
+ if (custom != null) {
+ mWriter.write(custom + ",\n");
+ }
+ }
+ mWriter.write(SMALL_INDENT + " } \n");
+ }
+ }
+
+ private static String colorString(int v) {
+ String str = "00000000" + Integer.toHexString(v);
+ return "#" + str.substring(str.length() - 8);
+ }
+
+ private void writeGuideline(int orientation,
+ int guideBegin,
+ int guideEnd,
+ float guidePercent) throws IOException {
+ writeVariable("orientation", orientation);
+ writeVariable("guideBegin", guideBegin);
+ writeVariable("guideEnd", guideEnd);
+ writeVariable("guidePercent", guidePercent);
+ }
+
+ private void writeDimension(String dimString,
+ int dim,
+ int dimDefault,
+ float dimPercent,
+ int dimMin,
+ int dimMax,
+ boolean unusedConstrainedDim) throws IOException {
+ if (dim == 0) {
+ if (dimMax != UNSET || dimMin != UNSET) {
+ String s = "-----";
+ switch (dimDefault) {
+ case 0: // spread
+ s = INDENT + dimString + ": {value:'spread'";
+ break;
+ case 1: // wrap
+ s = INDENT + dimString + ": {value:'wrap'";
+ break;
+ case 2: // percent
+ s = INDENT + dimString + ": {value: '" + dimPercent + "%'";
+ break;
+ }
+ if (dimMax != UNSET) {
+ s += ", max: " + dimMax;
+ }
+ if (dimMax != UNSET) {
+ s += ", min: " + dimMin;
+ }
+ s += "},\n";
+ mWriter.write(s);
+ return;
+ }
+
+ switch (dimDefault) {
+ case 0: // spread is the default
+ break;
+ case 1: // wrap
+ mWriter.write(INDENT + dimString + ": '???????????',\n");
+ return;
+ case 2: // percent
+ mWriter.write(INDENT + dimString + ": '" + dimPercent + "%',\n");
+ }
+
+ } else if (dim == -2) {
+ mWriter.write(INDENT + dimString + ": 'wrap',\n");
+ } else if (dim == -1) {
+ mWriter.write(INDENT + dimString + ": 'parent',\n");
+ } else {
+ mWriter.write(INDENT + dimString + ": " + dim + ",\n");
+ }
+ }
+
+ private String getSimpleName(int id) {
+ if (mIdMap.containsKey(id)) {
+ return "" + mIdMap.get(id);
+ }
+ if (id == 0) {
+ return "parent";
+ }
+ String name = lookup(id);
+ mIdMap.put(id, name);
+ return "" + name + "";
+ }
+
+ private String getName(int id) {
+ return "'" + getSimpleName(id) + "'";
+ }
+
+ private String lookup(int id) {
+ try {
+ if (mNames.containsKey(id)) {
+ return mNames.get(id);
+ }
+ if (id != -1) {
+ return mContext.getResources().getResourceEntryName(id);
+ } else {
+ return "unknown" + ++mUnknownCount;
+ }
+ } catch (Exception ex) {
+ return "unknown" + ++mUnknownCount;
+ }
+ }
+
+ private void writeConstraint(String my,
+ int constraint,
+ String other,
+ int margin,
+ int goneMargin) throws IOException {
+ if (constraint == UNSET) {
+ return;
+ }
+ mWriter.write(INDENT + my);
+ mWriter.write(":[");
+ mWriter.write(getName(constraint));
+ mWriter.write(", ");
+ mWriter.write("'" + other + "'");
+ if (margin != 0 || goneMargin != UNSET_GONE_MARGIN) {
+ mWriter.write(", " + margin);
+ if (goneMargin != UNSET_GONE_MARGIN) {
+ mWriter.write(", " + goneMargin);
+ }
+ }
+ mWriter.write("],\n");
+ }
+
+ private void writeCircle(int circleConstraint,
+ float circleAngle,
+ int circleRadius) throws IOException {
+ if (circleConstraint == UNSET) {
+ return;
+ }
+ mWriter.write(INDENT + "circle");
+ mWriter.write(":[");
+ mWriter.write(getName(circleConstraint));
+ mWriter.write(", " + circleAngle);
+ mWriter.write(circleRadius + "],\n");
+ }
+
+ private void writeVariable(String name, int value) throws IOException {
+ if (value == 0 || value == -1) {
+ return;
+ }
+ mWriter.write(INDENT + name);
+ mWriter.write(": " + value);
+ mWriter.write(",\n");
+ }
+
+ private void writeVariable(String name, float value) throws IOException {
+ if (value == UNSET) {
+ return;
+ }
+ mWriter.write(INDENT + name);
+ mWriter.write(": " + value);
+ mWriter.write(",\n");
+ }
+
+ private void writeVariable(String name, float value, float def) throws IOException {
+ if (value == def) {
+ return;
+ }
+ mWriter.write(INDENT + name);
+ mWriter.write(": " + value);
+ mWriter.write(",\n");
+ }
+
+ private void writeVariable(String name, boolean value, boolean def) throws IOException {
+ if (value == def) {
+ return;
+ }
+ mWriter.write(INDENT + name);
+ mWriter.write(": " + value);
+ mWriter.write(",\n");
+ }
+
+ private void writeVariable(String name, int[] value) throws IOException {
+ if (value == null) {
+ return;
+ }
+ mWriter.write(INDENT + name);
+ mWriter.write(": ");
+ for (int i = 0; i < value.length; i++) {
+ mWriter.write(((i == 0) ? "[" : ", ") + getName(value[i]));
+ }
+ mWriter.write("],\n");
+ }
+
+ private void writeVariable(String name, String value) throws IOException {
+ if (value == null) {
+ return;
+ }
+ mWriter.write(INDENT + name);
+ mWriter.write(": '" + value);
+ mWriter.write("',\n");
+ }
+ }
+}
diff --git a/constraintlayout/constraintlayout/src/main/res/values/attrs.xml b/constraintlayout/constraintlayout/src/main/res/values/attrs.xml
index e0e76eb6f..5f35e22f7 100644
--- a/constraintlayout/constraintlayout/src/main/res/values/attrs.xml
+++ b/constraintlayout/constraintlayout/src/main/res/values/attrs.xml
@@ -816,6 +816,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/Transition.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/Transition.java
index 877cafd9d..ef40272dc 100644
--- a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/Transition.java
+++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/Transition.java
@@ -74,6 +74,13 @@ public Transition(@NonNull CorePixelDp dpToPixel) {
mToPixel = dpToPixel;
}
+ /**
+ * Create transition with a 1 to 1 DP to pixel (usually used in testing
+ */
+ public Transition() {
+ this((dp) -> dp);
+ }
+
// @TODO: add description
@SuppressWarnings("HiddenTypeParameter")
OnSwipe createOnSwipe() {
diff --git a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckDumpJson.java b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckDumpJson.java
index b0e3e22a1..3c6d9e4f1 100644
--- a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckDumpJson.java
+++ b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckDumpJson.java
@@ -23,7 +23,6 @@
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.constraintlayout.motion.widget.Debug;
import androidx.constraintlayout.motion.widget.MotionLayout;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -75,7 +74,7 @@ private void dumpJson() {
if (mLayout instanceof MotionLayout) {
fileName = MotionLayoutToJason.writeJSonToFile((MotionLayout) mLayout, layout_name);
} else {
- fileName = ConstraintLayoutToJason.writeJSonToFile(mLayout, layout_name);
+ fileName = ConstraintLayoutToJason.toFile(mLayout, layout_name);
}
allFiles.add(fileName);
current++;
diff --git a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/ConstraintLayoutToJason.java b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/ConstraintLayoutToJason.java
index 023c59dad..db6e94b36 100644
--- a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/ConstraintLayoutToJason.java
+++ b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/ConstraintLayoutToJason.java
@@ -7,6 +7,7 @@
import android.os.Environment;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
@@ -24,142 +25,161 @@
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
-public class MotionLayoutToJason {
- MotionLayout mMotionLayout;
- Context mContext;
- private String TAG = "ML_DEBUG";
+public class ConstraintLayoutToJason {
+ private static String TAG = "ML_DEBUG";
+ HashMap names = new HashMap<>();
+ private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
+ private ConstraintLayoutToJason() {
+ }
+ private static String escape(String str) {
+ return str.replaceAll("\'", "\\'");
+ }
- public void writeJSonToFile(MotionLayout motionLayout, String name) {
+ private static int generateViewId() {
+ for (; ; ) {
+ final int result = sNextGeneratedId.get();
+ int newValue = result + 1;
+ if (newValue > 0x00FFFFFF) {
+ newValue = 1;
+ }
+ if (sNextGeneratedId.compareAndSet(result, newValue)) {
+ return result;
+ }
+ }
+ }
+ /**
+ * This writes the JSON5 description of the constraintLayout to a file named fileName.json5
+ * in the download directory which can be pulled with:
+ * "adb pull "/storage/emulated/0/Download/" ."
+ *
+ * @param constraintLayout
+ * @param fileName
+ * @return
+ */
+ public static String toFile(ConstraintLayout constraintLayout, String fileName) {
FileOutputStream outputStream;
+ ConstraintLayoutToJason c = new ConstraintLayoutToJason();
try {
File down = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
- File file = new File(down, name + ".json5");
+ File file = new File(down, fileName + ".json5");
outputStream = new FileOutputStream(file);
- // Write data to file
- outputStream.write(motionLayoutToJson(motionLayout).getBytes());
- // Close the file
+ outputStream.write(c.constraintLayoutToJson(constraintLayout).getBytes());
outputStream.close();
- Log.v(TAG, "\"" + file.getCanonicalPath() + "\"");
+ return file.getCanonicalPath();
} catch (IOException e) {
e.printStackTrace();
}
+ return null;
+ }
+
+ /**
+ * This Logs the json to the LOG
+ *
+ * @param tag The tag use in Log
+ * @param constraintLayout
+ */
+ public static void log(String tag, ConstraintLayout constraintLayout) {
+ ConstraintLayoutToJason c = new ConstraintLayoutToJason();
+ Log.v(tag, c.constraintLayoutToJson(constraintLayout));
}
- public void setMotionLayout(MotionLayout motionLayout) {
- Log.v(TAG, motionLayoutToJson(motionLayout));
+ /**
+ * Get a JSON5 String that represents the Constraints in a running ConstraintLayout
+ *
+ * @param constraintLayout
+ * @return
+ */
+ public static String asString(ConstraintLayout constraintLayout) {
+ ConstraintLayoutToJason c = new ConstraintLayoutToJason();
+ return c.constraintLayoutToJson(constraintLayout);
}
- public String motionLayoutToJson(MotionLayout motionLayout) {
- mMotionLayout = motionLayout;
- mContext = motionLayout.getContext();
- int[] mid = motionLayout.getConstraintSetIds();
+ private String constraintLayoutToJson(ConstraintLayout constraintLayout) {
StringWriter writer = new StringWriter();
- writer.append("{\n");
- writeTransitions(writer);
- writeWidgets(writer, motionLayout);
- writer.append(" ConstraintSets:{\n");
- for (int i = 0; i < mid.length; i++) {
- String name = Debug.getName(motionLayout.getContext(), mid[i]);
- if (name.equals("motion_base")) {
- continue;
- }
- ConstraintSet set = motionLayout.getConstraintSet(mid[i]);
- try {
- writer.append(name + ":");
- new WriteJsonEngine(writer, set, motionLayout, 0).writeLayout();
- writer.append("\n");
- } catch (IOException e) {
- throw new RuntimeException(e);
+ int count = constraintLayout.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View v = constraintLayout.getChildAt(i);
+ if (v.getId() == -1) {
+ int id;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ id = View.generateViewId();
+ } else {
+ id = generateViewId();;
+ }
+ v.setId(id);
+ names.put(id, "noid_" + v.getClass().getSimpleName());
}
}
+ writer.append("{\n");
+
+ writeWidgets(writer, constraintLayout);
+ writer.append(" ConstraintSet:{\n");
+ ConstraintSet set = new ConstraintSet();
+ set.clone(constraintLayout);
+ String name = (constraintLayout.getId() == -1) ? "cset" : Debug.getName(constraintLayout);
+ try {
+ writer.append(name + ":");
+ new WriteJsonEngine(writer, set, constraintLayout, names, 0).writeLayout();
+ writer.append("\n");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
writer.append(" }\n");
writer.append("}\n");
return writer.toString();
-
- }
-
- String[] autoName = {
- "'none'",
- "'jumpToStart'",
- "'jumpToEnd'",
- "'jumpToStart'",
- "'animateToEnd'",
- "'animateToStart'"
- };
-
- String[] arcMode = {
- "'none'", "'startVertical'", "'startHorizontal'", "'flip'", "'arcDown'", "'arcUp'"
- };
-
- private void writeTransitions(StringWriter writer) {
- ArrayList t = mMotionLayout.getDefinedTransitions();
- writer.append("Transitions:{\n");
- int titleCount = 0;
- for (MotionScene.Transition transition : t) {
- int id = transition.getId();
- if (id == -1) {
- writer.append(((titleCount == 0) ? " default" : (" default" + (titleCount + 1))) + ":{\n");
- titleCount++;
- } else {
- writer.append(Debug.getName(mContext, id) + ":{\n");
- }
- int from = transition.getStartConstraintSetId();
- int to = transition.getEndConstraintSetId();
- writer.append(" from: '" + Debug.getName(mContext, from) + "',\n");
- writer.append(" to: '" + Debug.getName(mContext, to) + "',\n");
- int dur = transition.getDuration();
- writer.append(" duration: " + dur + ",\n");
-
- int auto = transition.getAutoTransition();
- if (auto != MotionScene.Transition.AUTO_NONE) {
- writer.append(" auto: " + autoName[auto] + ",\n");
- }
- int arc = transition.getPathMotionArc();
- if (arc != UNSET) {
- writer.append(" pathMotionArc: " + arcMode[arc] + ",\n");
- }
- float stagger = transition.getStagger();
- if (stagger != 0.0f) {
- writer.append(" stagger: " + stagger + ",\n");
-
- }
- writer.append(" }\n");
-
- }
- writer.append("},\n");
}
- private void writeWidgets(StringWriter writer, MotionLayout motionLayout) {
+ private void writeWidgets(StringWriter writer, ConstraintLayout constraintLayout) {
writer.append("Widgets:{\n");
- int count = motionLayout.getChildCount();
+ int count = constraintLayout.getChildCount();
- for (int i = 0; i < count; i++) {
- View v = motionLayout.getChildAt(i);
+ for (int i = -1; i < count; i++) {
+ View v = (i == -1) ? constraintLayout : constraintLayout.getChildAt(i);
int id = v.getId();
- String name = Debug.getName(v);
+ String name = (names.containsKey(id)) ? names.get(id)
+ : ((i == -1) ? "parent" : Debug.getName(v));
String cname = v.getClass().getSimpleName();
- String bounds = ", bounds: [" + v.getLeft() + ", " + v.getTop() + ", " + v.getRight() + ", " + v.getBottom() + "]},\n";
- if (cname.contains("Text")) {
+ String bounds = ", bounds: [" + v.getLeft() + ", " + v.getTop()
+ + ", " + v.getRight() + ", " + v.getBottom() + "]},\n";
+ writer.append(" " + name + ": { ");
+ if (i == -1) {
+ writer.append("type: '" + v.getClass().getSimpleName() + "' , ");
+
+ try {
+ ViewGroup.LayoutParams p = (ViewGroup.LayoutParams) v.getLayoutParams();
+
+ String w = p.width == -1 ? "'MATCH_PARENT'" :
+ (p.width == -2) ? "'WRAP_CONTENT'" : p.width + "";
+ writer.append("width: " + w + ", ");
+ String h = p.height == -1 ? "'MATCH_PARENT'" :
+ (p.height == -2) ? "'WRAP_CONTENT'" : p.height + "";
+ writer.append("height: ").append(h);
+ } catch (Exception e) {
+
+ }
+ } else if (cname.contains("Text")) {
if (v instanceof TextView) {
- writer.append(name + ": { type: 'Text', label: '" + ((TextView) v).getText()+"'" );
+ writer.append("type: 'Text', label: '" + escape(((TextView) v).getText().toString()) + "'");
} else {
- writer.append(name + ": { type: 'Text' },\n");
+ writer.append("type: 'Text' },\n");
}
} else if (cname.contains("Button")) {
if (v instanceof Button) {
- writer.append(name + ": { type: 'Button', label: '" + ((Button) v).getText());
+ writer.append("type: 'Button', label: '" + ((Button) v).getText()+"'");
} else
- writer.append(name + ": { type: 'Button'");
+ writer.append("type: 'Button'");
} else if (cname.contains("Image")) {
- writer.append(name + ": { type: 'Image'");
+ writer.append("type: 'Image'");
} else if (cname.contains("View")) {
- writer.append(name + ": { type: 'Box'");
+ writer.append("type: 'Box'");
} else {
- writer.append(name + ": { type: '"+v.getClass().getSimpleName() +"'");
+ writer.append("type: '" + v.getClass().getSimpleName() + "'");
}
writer.append(bounds);
}
@@ -196,32 +216,38 @@ static class WriteJsonEngine {
final String mTOP = "top";
final String mSTART = "start";
final String mEND = "end";
- private static final String SPACE = " ";
- private static final String SP = " ";
-
- WriteJsonEngine(Writer writer, ConstraintSet set, ConstraintLayout layout, int flags) throws IOException {
+ private static final String INDENT = " ";
+ private static final String SMALL_INDENT = " ";
+ HashMap names;
+
+ WriteJsonEngine(Writer writer,
+ ConstraintSet set,
+ ConstraintLayout layout,
+ HashMap names,
+ int flags) throws IOException {
this.mWriter = writer;
this.mLayout = layout;
+ this.names = names;
this.mContext = layout.getContext();
this.mFlags = flags;
this.set = set;
set.getConstraint(2);
}
- int[] getIDs() {
+ private int[] getIDs() {
return set.getKnownIds();
}
- ConstraintSet.Constraint getConstraint(int id) {
+ private ConstraintSet.Constraint getConstraint(int id) {
return set.getConstraint(id);
}
- void writeLayout() throws IOException {
+ private void writeLayout() throws IOException {
mWriter.write("{\n");
for (Integer id : getIDs()) {
ConstraintSet.Constraint c = getConstraint(id);
String idName = getSimpleName(id);
- mWriter.write(SP + idName + ":{\n");
+ mWriter.write(SMALL_INDENT + idName + ":{\n");
ConstraintSet.Layout l = c.layout;
if (l.mReferenceIds != null) {
String ref = "type: '_" + idName + "_' , contains: [";
@@ -232,7 +258,7 @@ void writeLayout() throws IOException {
mWriter.write(ref + "]\n");
}
if (l.mReferenceIdString != null) {
- String ref = SP + "type: '???' , contains: [";
+ String ref = SMALL_INDENT + "type: '???' , contains: [";
String[] rids = l.mReferenceIdString.split(",");
for (int r = 0; r < rids.length; r++) {
String rid = rids[r];
@@ -289,27 +315,35 @@ void writeLayout() throws IOException {
}
writeTransform(c.transform);
writeCustom(c.mCustomConstraints);
- writeMotion(c.motion);
+
mWriter.write(" },\n");
}
mWriter.write("},\n");
}
- private void writeMotion(ConstraintSet.Motion motion) {
- }
-
- private void writeTransform(ConstraintSet.Transform transform) {
+ private void writeTransform(ConstraintSet.Transform transform) throws IOException {
+ if (transform.applyElevation) {
+ writeVariable("elevation", transform.elevation);
+ }
+ writeVariable("rotationX", transform.rotationX, 0);
+ writeVariable("rotationY", transform.rotationY, 0);
+ writeVariable("rotationZ", transform.rotation, 0);
+ writeVariable("scaleX", transform.scaleX, 1);
+ writeVariable("scaleY", transform.scaleY, 1);
+ writeVariable("translationX", transform.translationX, 0);
+ writeVariable("translationY", transform.translationY, 0);
+ writeVariable("translationZ", transform.translationZ, 0);
}
- void writeCustom(HashMap cset) throws IOException {
+ private void writeCustom(HashMap cset) throws IOException {
if (cset != null && cset.size() > 0) {
- mWriter.write(SPACE + "custom: {\n");
+ mWriter.write(INDENT + "custom: {\n");
for (String s : cset.keySet()) {
ConstraintAttribute attr = cset.get(s);
- String custom = SPACE + SP + attr.getName() + ": ";
+ String custom = INDENT + SMALL_INDENT + attr.getName() + ": ";
switch (attr.getType()) {
case INT_TYPE:
custom += attr.getIntegerValue();
@@ -335,11 +369,11 @@ void writeCustom(HashMap cset) throws IOException {
mWriter.write(custom + ",\n");
}
}
- mWriter.write(SP + " } \n");
+ mWriter.write(SMALL_INDENT + " } \n");
}
}
- static String colorString(int v) {
+ private static String colorString(int v) {
String str = "00000000" + Integer.toHexString(v);
return "#" + str.substring(str.length() - 8);
}
@@ -368,13 +402,13 @@ private void writeDimension(String dimString,
String s = "-----";
switch (dimDefault) {
case 0: // spread
- s = SPACE + dimString + ": {value:'spread'";
+ s = INDENT + dimString + ": {value:'spread'";
break;
case 1: // wrap
- s = SPACE + dimString + ": {value:'wrap'";
+ s = INDENT + dimString + ": {value:'wrap'";
break;
case 2: // percent
- s = SPACE + dimString + ": {value: '" + dimPercent + "%'";
+ s = INDENT + dimString + ": {value: '" + dimPercent + "%'";
break;
}
if (dimMax != UNSET) {
@@ -392,25 +426,24 @@ private void writeDimension(String dimString,
case 0: // spread is the default
break;
case 1: // wrap
- mWriter.write(SPACE + dimString + ": '???????????',\n");
+ mWriter.write(INDENT + dimString + ": '???????????',\n");
return;
case 2: // percent
- mWriter.write(SPACE + dimString + ": '" + dimPercent + "%',\n");
- return;
+ mWriter.write(INDENT + dimString + ": '" + dimPercent + "%',\n");
}
} else if (dim == -2) {
- mWriter.write(SPACE + dimString + ": 'wrap',\n");
+ mWriter.write(INDENT + dimString + ": 'wrap',\n");
} else if (dim == -1) {
- mWriter.write(SPACE + dimString + ": 'parent',\n");
+ mWriter.write(INDENT + dimString + ": 'parent',\n");
} else {
- mWriter.write(SPACE + dimString + ": " + dim + ",\n");
+ mWriter.write(INDENT + dimString + ": " + dim + ",\n");
}
}
HashMap mIdMap = new HashMap<>();
- String getSimpleName(int id) {
+ private String getSimpleName(int id) {
if (mIdMap.containsKey(id)) {
return "" + mIdMap.get(id);
}
@@ -422,12 +455,15 @@ String getSimpleName(int id) {
return "" + name + "";
}
- String getName(int id) {
- return "\'" + getSimpleName(id) + "\'";
+ private String getName(int id) {
+ return "'" + getSimpleName(id) + "'";
}
- String lookup(int id) {
+ private String lookup(int id) {
try {
+ if (names.containsKey(id)) {
+ return names.get(id);
+ }
if (id != -1) {
return mContext.getResources().getResourceEntryName(id);
} else {
@@ -438,103 +474,91 @@ String lookup(int id) {
}
}
- void writeConstraint(String my,
- int leftToLeft,
- String other,
- int margin,
- int goneMargin) throws IOException {
- if (leftToLeft == UNSET) {
+ private void writeConstraint(String my,
+ int constraint,
+ String other,
+ int margin,
+ int goneMargin) throws IOException {
+ if (constraint == UNSET) {
return;
}
- mWriter.write(SPACE + my);
+ mWriter.write(INDENT + my);
mWriter.write(":[");
- mWriter.write(getName(leftToLeft));
+ mWriter.write(getName(constraint));
mWriter.write(", ");
mWriter.write("'" + other + "'");
if (margin != 0 || goneMargin != UNSET_GONE_MARGIN) {
mWriter.write(", " + margin);
- if (goneMargin != 0) {
+ if (goneMargin != UNSET_GONE_MARGIN) {
mWriter.write(", " + goneMargin);
}
}
mWriter.write("],\n");
-
}
- void writeCircle(int circleConstraint,
- float circleAngle,
- int circleRadius) throws IOException {
+ private void writeCircle(int circleConstraint,
+ float circleAngle,
+ int circleRadius) throws IOException {
if (circleConstraint == UNSET) {
return;
}
- mWriter.write(SPACE + "circle");
+ mWriter.write(INDENT + "circle");
mWriter.write(":[");
mWriter.write(getName(circleConstraint));
mWriter.write(", " + circleAngle);
- mWriter.write(circleRadius + "]");
+ mWriter.write(circleRadius + "],\n");
}
- void writeVariable(String name, int value) throws IOException {
+ private void writeVariable(String name, int value) throws IOException {
if (value == 0 || value == -1) {
return;
}
- mWriter.write(SPACE + name);
- mWriter.write(":");
-
- mWriter.write(", " + value);
- mWriter.write("\n");
-
+ mWriter.write(INDENT + name);
+ mWriter.write(": " + value);
+ mWriter.write(",\n");
}
- void writeVariable(String name, float value) throws IOException {
+ private void writeVariable(String name, float value) throws IOException {
if (value == UNSET) {
return;
}
- mWriter.write(SPACE + name);
-
+ mWriter.write(INDENT + name);
mWriter.write(": " + value);
mWriter.write(",\n");
-
}
- void writeVariable(String name, float value, float def) throws IOException {
+ private void writeVariable(String name, float value, float def) throws IOException {
if (value == def) {
return;
}
- mWriter.write(SPACE + name);
-
+ mWriter.write(INDENT + name);
mWriter.write(": " + value);
mWriter.write(",\n");
-
}
- void writeVariable(String name, boolean value) throws IOException {
+ private void writeVariable(String name, boolean value) throws IOException {
if (!value) {
return;
}
- mWriter.write(SPACE + name);
-
+ mWriter.write(INDENT + name);
mWriter.write(": " + value);
mWriter.write(",\n");
-
}
- void writeVariable(String name, boolean value, boolean def) throws IOException {
+ private void writeVariable(String name, boolean value, boolean def) throws IOException {
if (value == def) {
return;
}
- mWriter.write(SPACE + name);
-
+ mWriter.write(INDENT + name);
mWriter.write(": " + value);
mWriter.write(",\n");
-
}
- void writeVariable(String name, int[] value) throws IOException {
+ private void writeVariable(String name, int[] value) throws IOException {
if (value == null) {
return;
}
- mWriter.write(SPACE + name);
+ mWriter.write(INDENT + name);
mWriter.write(": ");
for (int i = 0; i < value.length; i++) {
mWriter.write(((i == 0) ? "[" : ", ") + getName(value[i]));
@@ -542,15 +566,13 @@ void writeVariable(String name, int[] value) throws IOException {
mWriter.write("],\n");
}
- void writeVariable(String name, String value) throws IOException {
+ private void writeVariable(String name, String value) throws IOException {
if (value == null) {
return;
}
- mWriter.write(SPACE + name);
- mWriter.write(":");
- mWriter.write(", " + value);
- mWriter.write("\n");
-
+ mWriter.write(INDENT + name);
+ mWriter.write(": '" + value);
+ mWriter.write("',\n");
}
}
diff --git a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/MotionLayoutToJason.java b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/MotionLayoutToJason.java
index 603849209..706679750 100644
--- a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/MotionLayoutToJason.java
+++ b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/MotionLayoutToJason.java
@@ -78,7 +78,7 @@ public static String writeJSonToFile(MotionLayout motionLayout, String name) {
return null;
}
- public static void setMotionLayout(MotionLayout motionLayout) {
+ public static void logMotionLayout(MotionLayout motionLayout) {
MotionLayoutToJason m = new MotionLayoutToJason();
Log.v(TAG, m.motionLayoutToJson(motionLayout));
diff --git a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java
index 3ebf9890d..66a58a9f6 100644
--- a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java
+++ b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java
@@ -143,9 +143,8 @@ MotionLayout findMotionLayout(ViewGroup group) {
}
public void dumpMotionLayout() {
- MotionLayoutToJason mlJson = new MotionLayoutToJason();
Log.v(TAG,Debug.getLoc()+" MotionLayoutToJason ");
- mlJson.setMotionLayout(mMotionLayout);
+ MotionLayoutToJason.logMotionLayout(mMotionLayout);
}
@Override
diff --git a/projects/MotionLayoutVerification/app/src/main/res/layout/constraint_set_01.xml b/projects/MotionLayoutVerification/app/src/main/res/layout/constraint_set_01.xml
index d30d62366..2bd66364e 100644
--- a/projects/MotionLayoutVerification/app/src/main/res/layout/constraint_set_01.xml
+++ b/projects/MotionLayoutVerification/app/src/main/res/layout/constraint_set_01.xml
@@ -4,8 +4,16 @@
android:id="@+id/rootView"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/white"
- >
+ android:background="@color/white">
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/toolsAndroid/CLAnalyst/app/src/main/java/android/support/clanalyst/DumpCL.java b/toolsAndroid/CLAnalyst/app/src/main/java/android/support/clanalyst/DumpCL.java
index 980c7df01..63d90a7b5 100644
--- a/toolsAndroid/CLAnalyst/app/src/main/java/android/support/clanalyst/DumpCL.java
+++ b/toolsAndroid/CLAnalyst/app/src/main/java/android/support/clanalyst/DumpCL.java
@@ -174,7 +174,7 @@ private void writeWidgets(StringWriter writer, ConstraintLayout constraintLayout
}
} else if (cname.contains("Button")) {
if (v instanceof Button) {
- writer.append("type: 'Button', label: '" + ((Button) v).getText());
+ writer.append("type: 'Button', label: '" + ((Button) v).getText()+"'");
} else
writer.append("type: 'Button'");
} else if (cname.contains("Image")) {
diff --git a/toolsAndroid/CLAnalyst/app/src/main/res/layout/foo.xml b/toolsAndroid/CLAnalyst/app/src/main/res/layout/foo.xml
new file mode 100644
index 000000000..6bf5d4472
--- /dev/null
+++ b/toolsAndroid/CLAnalyst/app/src/main/res/layout/foo.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/toolsAndroid/CLAnalyst/app/src/main/res/xml/foo_scene.xml b/toolsAndroid/CLAnalyst/app/src/main/res/xml/foo_scene.xml
new file mode 100644
index 000000000..c66c786de
--- /dev/null
+++ b/toolsAndroid/CLAnalyst/app/src/main/res/xml/foo_scene.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file