From 6c2d7a9efc56a3e0833caf5dc719e7d507fd3db7 Mon Sep 17 00:00:00 2001 From: John Hoford Date: Wed, 4 Jan 2023 23:17:36 -0800 Subject: [PATCH 01/11] fix dumping jason add toolsAndroid --- .../widget/ConstraintSet.java | 480 ++++++++++++++---- .../support/constraint/app/CheckDumpJson.java | 3 +- .../app/ConstraintLayoutToJason.java | 374 +++++++------- .../constraint/app/MotionLayoutToJason.java | 2 +- .../constraint/app/VerificationActivity.java | 3 +- .../android/support/clanalyst/DumpCL.java | 2 +- .../CLAnalyst/app/src/main/res/layout/foo.xml | 85 ++++ .../app/src/main/res/xml/foo_scene.xml | 78 +++ 8 files changed, 737 insertions(+), 290 deletions(-) create mode 100644 toolsAndroid/CLAnalyst/app/src/main/res/layout/foo.xml create mode 100644 toolsAndroid/CLAnalyst/app/src/main/res/xml/foo_scene.xml diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintSet.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintSet.java index e5e3d2f0e..8261879b8 100644 --- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintSet.java +++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintSet.java @@ -24,6 +24,7 @@ import android.graphics.Color; import android.os.Build; import android.os.Build.VERSION_CODES; +import android.os.Environment; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; @@ -32,6 +33,9 @@ import android.util.Xml; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; import androidx.constraintlayout.core.motion.utils.Easing; import androidx.constraintlayout.core.widgets.ConstraintWidget; @@ -45,7 +49,10 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -55,6 +62,7 @@ import java.util.HashSet; import java.util.Locale; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; /** * This class allows you to define programmatically a set of constraints to be used with @@ -5956,7 +5964,7 @@ public void writeState(Writer writer, ConstraintLayout layout, int flags) throws if ((flags & 1) == 1) { new WriteXmlEngine(writer, layout, flags).writeLayout(); } else { - new WriteJsonEngine(writer, layout, flags).writeLayout(); + log(writer, layout, flags); } writer.write("\n---------------------------------------------\n"); @@ -6260,36 +6268,230 @@ void writeVariable(String name, String value) throws IOException { } // ================================== JSON =============================================== - class WriteJsonEngine { + private static class DumpCL { + private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1); + HashMap names = new HashMap<>(); + 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; + } + } + } + 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); + 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(); + } + 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(); + 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"; + 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("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("\'", "\\'"); + } + } + + /** + * 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 + */ + private static String toFile(ConstraintLayout constraintLayout, String fileName) { + FileOutputStream outputStream; + DumpCL c = new DumpCL(); + try { + File down = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + + File file = new File(down, fileName + ".json5"); + outputStream = new FileOutputStream(file); + outputStream.write(c.constraintLayoutToJson(constraintLayout).getBytes()); + outputStream.close(); + 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 + */ + @SuppressLint("LogConditional") + private static void log(String tag, ConstraintLayout constraintLayout) { + DumpCL c = new DumpCL(); + Log.v(tag, c.constraintLayoutToJson(constraintLayout)); + } + + private static void log(Writer writer, ConstraintLayout layout, int flags) throws IOException { + writer.append(asString(layout)); + } + + /** + * Get a JSON5 String that represents the Constraints in a running ConstraintLayout + * + * @param constraintLayout + * @return + */ + private static String asString(ConstraintLayout constraintLayout) { + DumpCL c = new DumpCL(); + return c.constraintLayoutToJson(constraintLayout); + } + + private static class WriteJsonEngine { + public static final int UNSET = ConstraintLayout.LayoutParams.UNSET; + ConstraintSet set; Writer mWriter; ConstraintLayout mLayout; Context mContext; int mFlags; 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 SPACE = " "; - - WriteJsonEngine(Writer writer, ConstraintLayout layout, int flags) throws IOException { + 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 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); } - void writeLayout() throws IOException { - mWriter.write("\n\'ConstraintSet\':{\n"); - for (Integer id : mConstraints.keySet()) { - Constraint c = mConstraints.get(id); - String idName = getName(id); - mWriter.write(idName + ":{\n"); - Layout l = c.layout; + private int[] getIDs() { + return set.getKnownIds(); + } + + private ConstraintSet.Constraint getConstraint(int id) { + return set.getConstraint(id); + } + private void writeLayout() throws IOException { + mWriter.write("{\n"); + for (Integer id : getIDs()) { + ConstraintSet.Constraint c = getConstraint(id); + String idName = getSimpleName(id); + mWriter.write(SMALL_INDENT + idName + ":{\n"); + ConstraintSet.Layout l = c.layout; + if (l.mReferenceIds != null) { + String ref = "type: '_" + idName + "_' , contains: ["; + for (int r = 0; r < l.mReferenceIds.length; r++) { + int rid = l.mReferenceIds[r]; + ref += ((r == 0) ? "" : ", ") + getName(rid); + } + mWriter.write(ref + "]\n"); + } + if (l.mReferenceIdString != null) { + String ref = SMALL_INDENT + "type: '???' , contains: ["; + String[] rids = l.mReferenceIdString.split(","); + for (int r = 0; r < rids.length; r++) { + String rid = rids[r]; + ref += ((r == 0) ? "" : ", ") + "`" + rid + "`"; + } + 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, @@ -6314,41 +6516,102 @@ void writeLayout() throws IOException { 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); + + 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'", + 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("WrapBehavior", l.mWrapBehavior); - writeVariable("'verticalWeight'", l.verticalWeight); - writeVariable("'horizontalWeight'", l.horizontalWeight); - writeVariable("'horizontalChainStyle'", l.horizontalChainStyle); - writeVariable("'verticalChainStyle'", l.verticalChainStyle); - writeVariable("'barrierDirection'", l.mBarrierDirection); + 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); + writeVariable("ReferenceIds", l.mReferenceIds); } - mWriter.write("}\n"); + writeTransform(c.transform); + writeCustom(c.mCustomConstraints); + + + mWriter.write(" },\n"); } - 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); + 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 += 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); + writeVariable("orientation", orientation); + writeVariable("guideBegin", guideBegin); + writeVariable("guideEnd", guideEnd); + writeVariable("guidePercent", guidePercent); } @@ -6362,20 +6625,26 @@ private void writeDimension(String dimString, boolean unusedConstrainedDim) throws IOException { if (dim == 0) { if (dimMax != UNSET || dimMin != UNSET) { + String s = "-----"; switch (dimDefault) { case 0: // spread - mWriter.write(SPACE + dimString - + ": {'spread' ," + dimMin + ", " + dimMax + "}\n"); + s = INDENT + dimString + ": {value:'spread'"; break; case 1: // wrap - mWriter.write(SPACE + dimString - + ": {'wrap' ," + dimMin + ", " + dimMax + "}\n"); - return; + s = INDENT + dimString + ": {value:'wrap'"; + break; case 2: // percent - mWriter.write(SPACE + dimString + ": {'" + dimPercent - + "'% ," + dimMin + ", " + dimMax + "}\n"); - return; + s = INDENT + dimString + ": {value: '" + dimPercent + "%'"; + break; } + if (dimMax != UNSET) { + s += ", max: " + dimMax; + } + if (dimMax != UNSET) { + s += ", min: " + dimMin; + } + s += "},\n"; + mWriter.write(s); return; } @@ -6383,38 +6652,44 @@ 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 getName(int id) { + private String getSimpleName(int id) { if (mIdMap.containsKey(id)) { - return "\'" + mIdMap.get(id) + "\'"; + return "" + mIdMap.get(id); } if (id == 0) { - return "'parent'"; + return "parent"; } String name = lookup(id); mIdMap.put(id, name); - return "\'" + name + "\'"; + return "" + name + ""; } - String lookup(int id) { + private String getName(int id) { + return "'" + getSimpleName(id) + "'"; + } + + private String lookup(int id) { try { + if (names.containsKey(id)) { + return names.get(id); + } if (id != -1) { return mContext.getResources().getResourceEntryName(id); } else { @@ -6425,99 +6700,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(" , "); - mWriter.write(other); - if (margin != 0) { - mWriter.write(" , " + margin); + mWriter.write(getName(constraint)); + mWriter.write(", "); + mWriter.write("'" + other + "'"); + if (margin != 0 || goneMargin != Layout.UNSET_GONE_MARGIN) { + mWriter.write(", " + margin); + if (goneMargin != Layout.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])); @@ -6525,16 +6792,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/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/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 @@ + + + + + +