From f7324fb71e81fe5a0d71b3097e84289d228f7faf Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Wed, 5 Jul 2023 22:00:16 -0700 Subject: [PATCH] Remove YogaLayout ViewGroup (#1318) Summary: Pull Request resolved: https://github.com/facebook/yoga/pull/1318 We deprecated this as part of the Yoga 2.0 release. The last version is still present as part of the `release-v2.0` branch, and was still published, but we are not carrying the code forward. This removes it. There are a couple followups here: 1. The top-level organization (e.g. a gradle-specific directory called `build-logic` in a multi-language repo) can and should be cleaned up. Yoga Java bindings should be condensed to the `java` folder. 2. We no longer have a sample app excersizing the bindings. We should resolve this by getting the JNI binding UTs working in OSS again. Reviewed By: cortinico Differential Revision: D47136243 fbshipit-source-id: 72f74914effde2c895934ac1100adfd305044d46 --- android/README.md | 21 - android/build.gradle | 41 - android/src/main/AndroidManifest.xml | 14 - .../yoga/android/VirtualYogaLayout.java | 149 ---- .../com/facebook/yoga/android/YogaLayout.java | 816 ------------------ .../yoga/android/YogaViewLayoutFactory.java | 57 -- android/src/main/res/values/attrs.xml | 150 ---- sample/build.gradle | 34 - sample/src/main/AndroidManifest.xml | 28 - .../yoga/sample/BenchmarkActivity.java | 121 --- .../yoga/sample/BenchmarkAggregator.java | 195 ----- .../yoga/sample/BenchmarkFragment.java | 116 --- .../yoga/sample/BenchmarkInflate.java | 61 -- .../facebook/yoga/sample/BenchmarkLayout.java | 70 -- .../yoga/sample/BenchmarkMeasure.java | 74 -- .../facebook/yoga/sample/MainActivity.java | 54 -- .../res/drawable/action_bar_background.xml | 17 - sample/src/main/res/drawable/ic_launcher.png | Bin 28905 -> 0 bytes .../drawable/sample_children_background.xml | 34 - .../main/res/layout/benchmark_fragment.xml | 50 -- .../main/res/layout/benchmark_layout_1.xml | 30 - .../res/layout/benchmark_layout_1_linear.xml | 28 - .../main/res/layout/benchmark_layout_2.xml | 104 --- .../res/layout/benchmark_layout_2_linear.xml | 95 -- .../main/res/layout/benchmark_layout_3.xml | 206 ----- .../res/layout/benchmark_layout_3_linear.xml | 204 ----- .../res/layout/benchmark_select_layout.xml | 7 - sample/src/main/res/layout/main_layout.xml | 137 --- .../main/res/menu/action_bar_benchmark.xml | 8 - sample/src/main/res/menu/action_bar_home.xml | 8 - sample/src/main/res/values/colors.xml | 20 - sample/src/main/res/values/strings.xml | 35 - sample/src/main/res/values/styles.xml | 23 - settings.gradle.kts | 4 +- 34 files changed, 1 insertion(+), 3010 deletions(-) delete mode 100644 android/README.md delete mode 100644 android/build.gradle delete mode 100644 android/src/main/AndroidManifest.xml delete mode 100644 android/src/main/java/com/facebook/yoga/android/VirtualYogaLayout.java delete mode 100644 android/src/main/java/com/facebook/yoga/android/YogaLayout.java delete mode 100644 android/src/main/java/com/facebook/yoga/android/YogaViewLayoutFactory.java delete mode 100644 android/src/main/res/values/attrs.xml delete mode 100644 sample/build.gradle delete mode 100644 sample/src/main/AndroidManifest.xml delete mode 100644 sample/src/main/java/com/facebook/yoga/sample/BenchmarkActivity.java delete mode 100644 sample/src/main/java/com/facebook/yoga/sample/BenchmarkAggregator.java delete mode 100644 sample/src/main/java/com/facebook/yoga/sample/BenchmarkFragment.java delete mode 100644 sample/src/main/java/com/facebook/yoga/sample/BenchmarkInflate.java delete mode 100644 sample/src/main/java/com/facebook/yoga/sample/BenchmarkLayout.java delete mode 100644 sample/src/main/java/com/facebook/yoga/sample/BenchmarkMeasure.java delete mode 100644 sample/src/main/java/com/facebook/yoga/sample/MainActivity.java delete mode 100644 sample/src/main/res/drawable/action_bar_background.xml delete mode 100644 sample/src/main/res/drawable/ic_launcher.png delete mode 100644 sample/src/main/res/drawable/sample_children_background.xml delete mode 100644 sample/src/main/res/layout/benchmark_fragment.xml delete mode 100644 sample/src/main/res/layout/benchmark_layout_1.xml delete mode 100644 sample/src/main/res/layout/benchmark_layout_1_linear.xml delete mode 100644 sample/src/main/res/layout/benchmark_layout_2.xml delete mode 100644 sample/src/main/res/layout/benchmark_layout_2_linear.xml delete mode 100644 sample/src/main/res/layout/benchmark_layout_3.xml delete mode 100644 sample/src/main/res/layout/benchmark_layout_3_linear.xml delete mode 100644 sample/src/main/res/layout/benchmark_select_layout.xml delete mode 100644 sample/src/main/res/layout/main_layout.xml delete mode 100644 sample/src/main/res/menu/action_bar_benchmark.xml delete mode 100644 sample/src/main/res/menu/action_bar_home.xml delete mode 100644 sample/src/main/res/values/colors.xml delete mode 100644 sample/src/main/res/values/strings.xml delete mode 100644 sample/src/main/res/values/styles.xml diff --git a/android/README.md b/android/README.md deleted file mode 100644 index 9a143c8c..00000000 --- a/android/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# YogaLayout [![Platform](https://img.shields.io/badge/platforms-Android-orange.svg)](https://facebook.github.io/yoga/docs/api/android/) [![Languages](https://img.shields.io/badge/languages-Java-orange.svg)](https://facebook.github.io/yoga/docs/api/android/) [![Download](https://img.shields.io/bintray/v/facebook/maven/com.facebook.yoga.android:yoga-layout.svg)](https://bintray.com/facebook/maven/com.facebook.yoga.android%3Ayoga-layout/_latestVersion) - -## Installation - -YogaLayout is available via jcenter: - - implementation 'com.facebook.yoga.android:yoga-layout:1.16.0' - -## Getting Started - -Check out the docs [here](https://yogalayout.com/getting-started/standalone/). - -We also have a sample project. To try it, clone the repo and run (with a device attached) - - buck install -r android/sample - -## Contributing - -We welcome all pull-requests! At Facebook we sync the open source version of YogaKit daily, so we're always testing the latest changes. - -See the CONTRIBUTING file for how to help out. diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index 57b37499..00000000 --- a/android/build.gradle +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -plugins { - id("com.android.library") - id("publish") -} - -android { - namespace 'com.facebook.yoga.android' - - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion - ndkVersion rootProject.ndkVersion - - defaultConfig { - minSdkVersion rootProject.minSdkVersion - targetSdkVersion rootProject.targetSdkVersion - } - - compileOptions { - targetCompatibility rootProject.targetCompatibilityVersion - sourceCompatibility rootProject.sourceCompatibilityVersion - } - - publishing { - multipleVariants { - withSourcesJar() - withJavadocJar() - includeBuildTypeValues('debug', 'release') - } - } -} - -dependencies { - api project(':yoga') -} diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml deleted file mode 100644 index 7513275a..00000000 --- a/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/android/src/main/java/com/facebook/yoga/android/VirtualYogaLayout.java b/android/src/main/java/com/facebook/yoga/android/VirtualYogaLayout.java deleted file mode 100644 index c8ad1c3c..00000000 --- a/android/src/main/java/com/facebook/yoga/android/VirtualYogaLayout.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.yoga.android; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; - -import com.facebook.yoga.YogaNode; -import com.facebook.yoga.YogaNodeFactory; - -/** - * Much like a {@link YogaLayout}, except this class does not render itself (the container) to the - * screen. As a result, do not use this if you wish the container to have a background or - * foreground. However, all of its children will still render as expected. - * - *

- * In practice, this class never added to the View tree, and all its children become children of its - * parent. As a result, all the layout (such as the traversal of the tree) is performed by Yoga - * (and so natively) increasing performance. - */ -public class VirtualYogaLayout extends ViewGroup { - - final private List mChildren = new LinkedList<>(); - final private Map mYogaNodes = new HashMap<>(); - final private YogaNode mYogaNode = YogaNodeFactory.create(); - - public VirtualYogaLayout(Context context) { - super(context); - } - - public VirtualYogaLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public VirtualYogaLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - YogaLayout.LayoutParams lp = new YogaLayout.LayoutParams(context, attrs); - YogaLayout.applyLayoutParams(lp, mYogaNode, this); - } - - public YogaNode getYogaNode() { - return mYogaNode; - } - - /** - * Called to add a view, creating a new yoga node for it and adding that yoga node to the parent. - * If the child is a {@link VirtualYogaLayout}, we simply transfer all its children to this one - * in a manner that maintains the tree, and add its root to the tree. - * - * @param child the View to add - * @param index the position at which to add it (ignored) - * @param params the layout parameters to apply - */ - @Override - public void addView(View child, int index, ViewGroup.LayoutParams params) { - if (child instanceof VirtualYogaLayout) { - ((VirtualYogaLayout) child).transferChildren(this); - - final YogaNode childNode = ((VirtualYogaLayout) child).getYogaNode(); - mYogaNode.addChildAt(childNode, mYogaNode.getChildCount()); - - return; - } - - YogaNode node = YogaNodeFactory.create(); - YogaLayout.LayoutParams lp = new YogaLayout.LayoutParams(params); - YogaLayout.applyLayoutParams(lp, node, child); - node.setData(child); - node.setMeasureFunction(new YogaLayout.ViewMeasureFunction()); - - mYogaNode.addChildAt(node, mYogaNode.getChildCount()); - - addView(child, node); - } - - /** - * Called to add a view with a corresponding node, but not to change the Yoga tree in any way. - * - * @param child the View to add - * @param node the corresponding yoga node - */ - public void addView(View child, YogaNode node) { - mChildren.add(child); - mYogaNodes.put(child, node); - } - - /** - * Gives up children {@code View}s to the parent, maintaining the Yoga tree. This function calls - * {@link YogaLayout#addView(View, YogaNode)} or {@link VirtualYogaLayout#addView(View, YogaNode)} - * on the parent to add the {@code View} without generating new yoga nodes. - * - * @param parent the parent to pass children to (must be a YogaLayout or a VirtualYogaLayout) - */ - protected void transferChildren(ViewGroup parent) { - if (parent instanceof VirtualYogaLayout) { - for (View child : mChildren) { - ((VirtualYogaLayout) parent).addView(child, mYogaNodes.get(child)); - } - } else if (parent instanceof YogaLayout) { - for (View child : mChildren) { - ((YogaLayout) parent).addView(child, mYogaNodes.get(child)); - } - } else { - throw new RuntimeException("VirtualYogaLayout cannot transfer children to ViewGroup of type " - +parent.getClass().getCanonicalName()+". Must either be a VirtualYogaLayout or a " + - "YogaLayout."); - } - mChildren.clear(); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - throw new RuntimeException("Attempting to layout a VirtualYogaLayout"); - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return new YogaLayout.LayoutParams(getContext(), attrs); - } - - @Override - protected ViewGroup.LayoutParams generateDefaultLayoutParams() { - return new YogaLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - } - - @Override - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return new YogaLayout.LayoutParams(p); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof YogaLayout.LayoutParams; - } -} diff --git a/android/src/main/java/com/facebook/yoga/android/YogaLayout.java b/android/src/main/java/com/facebook/yoga/android/YogaLayout.java deleted file mode 100644 index beee6a99..00000000 --- a/android/src/main/java/com/facebook/yoga/android/YogaLayout.java +++ /dev/null @@ -1,816 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.yoga.android; - -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.TypedArray; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.util.AttributeSet; -import android.util.SparseArray; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; -import com.facebook.yoga.YogaAlign; -import com.facebook.yoga.YogaConstants; -import com.facebook.yoga.YogaDirection; -import com.facebook.yoga.YogaDisplay; -import com.facebook.yoga.YogaEdge; -import com.facebook.yoga.YogaFlexDirection; -import com.facebook.yoga.YogaJustify; -import com.facebook.yoga.YogaMeasureFunction; -import com.facebook.yoga.YogaMeasureMode; -import com.facebook.yoga.YogaMeasureOutput; -import com.facebook.yoga.YogaNode; -import com.facebook.yoga.YogaNodeFactory; -import com.facebook.yoga.YogaOverflow; -import com.facebook.yoga.YogaPositionType; -import com.facebook.yoga.YogaWrap; -import java.util.HashMap; -import java.util.Map; - -/** - * A {@code ViewGroup} based on the Yoga layout engine. - * - *

- * This class is designed to be as "plug and play" as possible. That is, you can use it in XML - * like this (note: to use {@code YogaLayout} you need to use the {@link YogaViewLayoutFactory}): - *

- *

{@code
- * 
- *     
- * 
- * }
- * - * Under the hood, all views added to this {@code ViewGroup} are laid out using flexbox rules - * and the Yoga engine. - */ -public class YogaLayout extends ViewGroup { - private final Map mYogaNodes; - private final YogaNode mYogaNode; - - public YogaLayout(Context context) { - this(context, null, 0); - } - - public YogaLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public YogaLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - mYogaNode = YogaNodeFactory.create(); - mYogaNodes = new HashMap<>(); - - mYogaNode.setData(this); - mYogaNode.setMeasureFunction(new ViewMeasureFunction()); - - LayoutParams layoutParams = null; - if (attrs != null) { - layoutParams = new LayoutParams(context, attrs); - } else { - layoutParams = (LayoutParams) generateDefaultLayoutParams(); - } - applyLayoutParams(layoutParams, mYogaNode, this); - } - - public YogaNode getYogaNode() { - return mYogaNode; - } - - public YogaNode getYogaNodeForView(View view) { - return mYogaNodes.get(view); - } - - /** - * Adds a child view with the specified layout parameters. - * - * In the typical View is added, this constructs a {@code YogaNode} for this child and applies all - * the {@code yoga:*} attributes. The Yoga node is added to the Yoga tree and the child is added - * to this ViewGroup. - * - * If the child is a {@link YogaLayout} itself, we do not construct a new Yoga node for that - * child, but use its root node instead. - * - * If the child is a {@link VirtualYogaLayout}, we also use its Yoga node, but we also instruct it - * to transfer all of its children to this {@link YogaLayout} while preserving the Yoga tree (so - * that the layout of its children is correct). The {@link VirtualYogaLayout} is then not added - * to the View hierarchy. - * - *

Note: do not invoke this method from - * {@code #draw(android.graphics.Canvas)}, {@code onDraw(android.graphics.Canvas)}, - * {@code #dispatchDraw(android.graphics.Canvas)} or any related method.

- * - * @param child the child view to add - * @param index the position at which to add the child or -1 to add last - * @param params the layout parameters to set on the child - */ - @Override - public void addView(View child, int index, ViewGroup.LayoutParams params) { - // Internal nodes (which this is now) cannot have measure functions - mYogaNode.setMeasureFunction(null); - - if (child instanceof VirtualYogaLayout) { - ((VirtualYogaLayout) child).transferChildren(this); - final YogaNode childNode = ((VirtualYogaLayout) child).getYogaNode(); - - mYogaNode.addChildAt(childNode, mYogaNode.getChildCount()); - - return; - } - - super.addView(child, index, params); - - // It is possible that addView is being called as part of a transferal of children, in which - // case we already know about the YogaNode and only need the Android View tree to be aware - // that we now own this child. If so, we don't need to do anything further - if (mYogaNodes.containsKey(child)) { - return; - } - - YogaNode childNode; - - if (child instanceof YogaLayout) { - childNode = ((YogaLayout) child).getYogaNode(); - } else { - if(mYogaNodes.containsKey(child)) { - childNode = mYogaNodes.get(child); - } else { - childNode = YogaNodeFactory.create(); - } - - childNode.setData(child); - childNode.setMeasureFunction(new ViewMeasureFunction()); - } - - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - applyLayoutParams(lp, childNode, child); - - mYogaNodes.put(child, childNode); - mYogaNode.addChildAt(childNode, mYogaNode.getChildCount()); - } - - /** - * Adds a view to this {@code ViewGroup} with an already given {@code YogaNode}. Use - * this function if you already have a Yoga node (and perhaps tree) associated with the view you - * are adding, that you would like to preserve. - * - * @param child The view to add - * @param node The Yoga node belonging to the view - */ - public void addView(View child, YogaNode node) { - mYogaNodes.put(child, node); - addView(child); - } - - @Override - public void removeView(View view) { - removeViewFromYogaTree(view, false); - super.removeView(view); - } - - @Override - public void removeViewAt(int index) { - removeViewFromYogaTree(getChildAt(index), false); - super.removeViewAt(index); - } - - @Override - public void removeViewInLayout(View view) { - removeViewFromYogaTree(view, true); - super.removeViewInLayout(view); - } - - @Override - public void removeViews(int start, int count) { - for (int i = start; i < start + count; i++) { - removeViewFromYogaTree(getChildAt(i), false); - } - super.removeViews(start, count); - } - - @Override - public void removeViewsInLayout(int start, int count) { - for (int i = start; i < start + count; i++) { - removeViewFromYogaTree(getChildAt(i), true); - } - super.removeViewsInLayout(start, count); - } - - @Override - public void removeAllViews() { - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - removeViewFromYogaTree(getChildAt(i), false); - } - super.removeAllViews(); - } - - @Override - public void removeAllViewsInLayout() { - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - removeViewFromYogaTree(getChildAt(i), true); - } - super.removeAllViewsInLayout(); - } - - /** - * Marks a particular view as "dirty" and to be relaid out. If the view is not a child of this - * {@link YogaLayout}, the entire tree is traversed to find it. - * - * @param view the view to mark as dirty - */ - public void invalidate(View view) { - if (mYogaNodes.containsKey(view)) { - mYogaNodes.get(view).dirty(); - return; - } - - final int childCount = mYogaNode.getChildCount(); - for (int i = 0; i < childCount; i++) { - final YogaNode yogaNode = mYogaNode.getChildAt(i); - if (yogaNode.getData() instanceof YogaLayout) { - ((YogaLayout) yogaNode.getData()).invalidate(view); - } - } - invalidate(); - } - - private void removeViewFromYogaTree(View view, boolean inLayout) { - final YogaNode node = mYogaNodes.get(view); - if (node == null) { - return; - } - - final YogaNode owner = node.getOwner(); - - for (int i = 0; i < owner.getChildCount(); i++) { - if (owner.getChildAt(i).equals(node)) { - owner.removeChildAt(i); - break; - } - } - - node.setData(null); - mYogaNodes.remove(view); - - if (inLayout) { - mYogaNode.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED); - } - } - - private void applyLayoutRecursive(YogaNode node, float xOffset, float yOffset) { - View view = (View) node.getData(); - - if (view != null && view != this) { - if (view.getVisibility() == GONE) { - return; - } - int left = Math.round(xOffset + node.getLayoutX()); - int top = Math.round(yOffset + node.getLayoutY()); - view.measure( - View.MeasureSpec.makeMeasureSpec( - Math.round(node.getLayoutWidth()), - View.MeasureSpec.EXACTLY), - View.MeasureSpec.makeMeasureSpec( - Math.round(node.getLayoutHeight()), - View.MeasureSpec.EXACTLY)); - view.layout(left, top, left + view.getMeasuredWidth(), top + view.getMeasuredHeight()); - } - - final int childrenCount = node.getChildCount(); - for (int i = 0; i < childrenCount; i++) { - if (this.equals(view)) { - applyLayoutRecursive(node.getChildAt(i), xOffset, yOffset); - } else if (view instanceof YogaLayout) { - continue; - } else { - applyLayoutRecursive( - node.getChildAt(i), - xOffset + node.getLayoutX(), - yOffset + node.getLayoutY()); - } - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - // Either we are a root of a tree, or this function is called by our owner's onLayout, in which - // case our r-l and b-t are the size of our node. - if (!(getParent() instanceof YogaLayout)) { - createLayout( - MeasureSpec.makeMeasureSpec(r - l, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(b - t, MeasureSpec.EXACTLY)); - } - - applyLayoutRecursive(mYogaNode, 0, 0); - } - - /** - * This function is mostly unneeded, because Yoga is doing the measuring. Hence we only need to - * return accurate results if we are the root. - * - * @param widthMeasureSpec the suggested specification for the width - * @param heightMeasureSpec the suggested specification for the height - */ - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (!(getParent() instanceof YogaLayout)) { - createLayout(widthMeasureSpec, heightMeasureSpec); - } - - setMeasuredDimension( - Math.round(mYogaNode.getLayoutWidth()), - Math.round(mYogaNode.getLayoutHeight())); - } - - private void createLayout(int widthMeasureSpec, int heightMeasureSpec) { - final int widthSize = MeasureSpec.getSize(widthMeasureSpec); - final int heightSize = MeasureSpec.getSize(heightMeasureSpec); - final int widthMode = MeasureSpec.getMode(widthMeasureSpec); - final int heightMode = MeasureSpec.getMode(heightMeasureSpec); - - if (heightMode == MeasureSpec.EXACTLY) { - mYogaNode.setHeight(heightSize); - } - if (widthMode == MeasureSpec.EXACTLY) { - mYogaNode.setWidth(widthSize); - } - if (heightMode == MeasureSpec.AT_MOST) { - mYogaNode.setMaxHeight(heightSize); - } - if (widthMode == MeasureSpec.AT_MOST) { - mYogaNode.setMaxWidth(widthSize); - } - mYogaNode.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED); - } - - /** - * Applies the layout parameters to the YogaNode. That is, this function is a translator from - * {@code yoga:X="Y"} to {@code yogaNode.setX(Y);}, with some reasonable defaults. - * - *

- * If the SDK version is high enough, and the {@code yoga:direction} is not set on - * the component, the direction (LTR or RTL) is set according to the locale. - * - *

- * The attributes {@code padding_top}, {@code padding_right} etc. default to those of the view's - * drawable background, if it has one. - * - * @param layoutParameters The source set of params - * @param node The destination node - */ - protected static void applyLayoutParams(LayoutParams layoutParameters, YogaNode node, View view) { - // JELLY_BEAN_MR1 (17) is the first version supporting getLayoutDirection() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - Configuration configuration = view.getResources().getConfiguration(); - if (configuration.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { - node.setDirection(YogaDirection.RTL); - } - } - - Drawable background = view.getBackground(); - if (background != null) { - final Rect backgroundPadding = new Rect(); - if (background.getPadding(backgroundPadding)) { - node.setPadding(YogaEdge.LEFT, backgroundPadding.left); - node.setPadding(YogaEdge.TOP, backgroundPadding.top); - node.setPadding(YogaEdge.RIGHT, backgroundPadding.right); - node.setPadding(YogaEdge.BOTTOM, backgroundPadding.bottom); - } - } - - for (int i = 0; i < layoutParameters.numericAttributes.size(); i++) { - final int attribute = layoutParameters.numericAttributes.keyAt(i); - final float value = layoutParameters.numericAttributes.valueAt(i); - - if (attribute == R.styleable.yoga_yg_alignContent) { - node.setAlignContent(YogaAlign.fromInt(Math.round(value))); - } else if (attribute == R.styleable.yoga_yg_alignItems) { - node.setAlignItems(YogaAlign.fromInt(Math.round(value))); - } else if (attribute == R.styleable.yoga_yg_alignSelf) { - node.setAlignSelf(YogaAlign.fromInt(Math.round(value))); - } else if (attribute == R.styleable.yoga_yg_aspectRatio) { - node.setAspectRatio(value); - } else if (attribute == R.styleable.yoga_yg_borderLeft) { - node.setBorder(YogaEdge.LEFT, value); - } else if (attribute == R.styleable.yoga_yg_borderTop) { - node.setBorder(YogaEdge.TOP, value); - } else if (attribute == R.styleable.yoga_yg_borderRight) { - node.setBorder(YogaEdge.RIGHT, value); - } else if (attribute == R.styleable.yoga_yg_borderBottom) { - node.setBorder(YogaEdge.BOTTOM, value); - } else if (attribute == R.styleable.yoga_yg_borderStart) { - node.setBorder(YogaEdge.START, value); - } else if (attribute == R.styleable.yoga_yg_borderEnd) { - node.setBorder(YogaEdge.END, value); - } else if (attribute == R.styleable.yoga_yg_borderHorizontal) { - node.setBorder(YogaEdge.HORIZONTAL, value); - } else if (attribute == R.styleable.yoga_yg_borderVertical) { - node.setBorder(YogaEdge.VERTICAL, value); - } else if (attribute == R.styleable.yoga_yg_borderAll) { - node.setBorder(YogaEdge.ALL, value); - } else if (attribute == R.styleable.yoga_yg_direction) { - node.setDirection(YogaDirection.fromInt(Math.round(value))); - } else if (attribute == R.styleable.yoga_yg_display) { - node.setDisplay(YogaDisplay.fromInt(Math.round(value))); - } else if (attribute == R.styleable.yoga_yg_flex) { - node.setFlex(value); - } else if (attribute == R.styleable.yoga_yg_flexBasis) { - node.setFlexBasis(value); - } else if (attribute == R.styleable.yoga_yg_flexDirection) { - node.setFlexDirection(YogaFlexDirection.fromInt(Math.round(value))); - } else if (attribute == R.styleable.yoga_yg_flexGrow) { - node.setFlexGrow(value); - } else if (attribute == R.styleable.yoga_yg_flexShrink) { - node.setFlexShrink(value); - } else if (attribute == R.styleable.yoga_yg_height) { - node.setHeight(value); - } else if (attribute == R.styleable.yoga_yg_marginLeft) { - node.setMargin(YogaEdge.LEFT, value); - } else if (attribute == R.styleable.yoga_yg_justifyContent) { - node.setJustifyContent(YogaJustify.fromInt(Math.round(value))); - } else if (attribute == R.styleable.yoga_yg_marginTop) { - node.setMargin(YogaEdge.TOP, value); - } else if (attribute == R.styleable.yoga_yg_marginRight) { - node.setMargin(YogaEdge.RIGHT, value); - } else if (attribute == R.styleable.yoga_yg_marginBottom) { - node.setMargin(YogaEdge.BOTTOM, value); - } else if (attribute == R.styleable.yoga_yg_marginStart) { - node.setMargin(YogaEdge.START, value); - } else if (attribute == R.styleable.yoga_yg_marginEnd) { - node.setMargin(YogaEdge.END, value); - } else if (attribute == R.styleable.yoga_yg_marginHorizontal) { - node.setMargin(YogaEdge.HORIZONTAL, value); - } else if (attribute == R.styleable.yoga_yg_marginVertical) { - node.setMargin(YogaEdge.VERTICAL, value); - } else if (attribute == R.styleable.yoga_yg_marginAll) { - node.setMargin(YogaEdge.ALL, value); - } else if (attribute == R.styleable.yoga_yg_maxHeight) { - node.setMaxHeight(value); - } else if (attribute == R.styleable.yoga_yg_maxWidth) { - node.setMaxWidth(value); - } else if (attribute == R.styleable.yoga_yg_minHeight) { - node.setMinHeight(value); - } else if (attribute == R.styleable.yoga_yg_minWidth) { - node.setMinWidth(value); - } else if (attribute == R.styleable.yoga_yg_overflow) { - node.setOverflow(YogaOverflow.fromInt(Math.round(value))); - } else if (attribute == R.styleable.yoga_yg_paddingLeft) { - node.setPadding(YogaEdge.LEFT, value); - } else if (attribute == R.styleable.yoga_yg_paddingTop) { - node.setPadding(YogaEdge.TOP, value); - } else if (attribute == R.styleable.yoga_yg_paddingRight) { - node.setPadding(YogaEdge.RIGHT, value); - } else if (attribute == R.styleable.yoga_yg_paddingBottom) { - node.setPadding(YogaEdge.BOTTOM, value); - } else if (attribute == R.styleable.yoga_yg_paddingStart) { - node.setPadding(YogaEdge.START, value); - } else if (attribute == R.styleable.yoga_yg_paddingEnd) { - node.setPadding(YogaEdge.END, value); - } else if (attribute == R.styleable.yoga_yg_paddingHorizontal) { - node.setPadding(YogaEdge.HORIZONTAL, value); - } else if (attribute == R.styleable.yoga_yg_paddingVertical) { - node.setPadding(YogaEdge.VERTICAL, value); - } else if (attribute == R.styleable.yoga_yg_paddingAll) { - node.setPadding(YogaEdge.ALL, value); - } else if (attribute == R.styleable.yoga_yg_positionLeft) { - node.setPosition(YogaEdge.LEFT, value); - } else if (attribute == R.styleable.yoga_yg_positionTop) { - node.setPosition(YogaEdge.TOP, value); - } else if (attribute == R.styleable.yoga_yg_positionRight) { - node.setPosition(YogaEdge.RIGHT, value); - } else if (attribute == R.styleable.yoga_yg_positionBottom) { - node.setPosition(YogaEdge.BOTTOM, value); - } else if (attribute == R.styleable.yoga_yg_positionStart) { - node.setPosition(YogaEdge.START, value); - } else if (attribute == R.styleable.yoga_yg_positionEnd) { - node.setPosition(YogaEdge.END, value); - } else if (attribute == R.styleable.yoga_yg_positionHorizontal) { - node.setPosition(YogaEdge.HORIZONTAL, value); - } else if (attribute == R.styleable.yoga_yg_positionVertical) { - node.setPosition(YogaEdge.VERTICAL, value); - } else if (attribute == R.styleable.yoga_yg_positionAll) { - node.setPosition(YogaEdge.ALL, value); - } else if (attribute == R.styleable.yoga_yg_positionType) { - node.setPositionType(YogaPositionType.fromInt(Math.round(value))); - } else if (attribute == R.styleable.yoga_yg_width) { - node.setWidth(value); - } else if (attribute == R.styleable.yoga_yg_wrap) { - node.setWrap(YogaWrap.fromInt(Math.round(value))); - } - } - - for (int i = 0; i < layoutParameters.stringAttributes.size(); i++) { - final int attribute = layoutParameters.stringAttributes.keyAt(i); - final String value = layoutParameters.stringAttributes.valueAt(i); - - if (value.equals("auto")) { - if (attribute == R.styleable.yoga_yg_marginLeft) { - node.setMarginAuto(YogaEdge.LEFT); - } else if (attribute == R.styleable.yoga_yg_marginTop) { - node.setMarginAuto(YogaEdge.TOP); - } else if (attribute == R.styleable.yoga_yg_marginRight) { - node.setMarginAuto(YogaEdge.RIGHT); - } else if (attribute == R.styleable.yoga_yg_marginBottom) { - node.setMarginAuto(YogaEdge.BOTTOM); - } else if (attribute == R.styleable.yoga_yg_marginStart) { - node.setMarginAuto(YogaEdge.START); - } else if (attribute == R.styleable.yoga_yg_marginEnd) { - node.setMarginAuto(YogaEdge.END); - } else if (attribute == R.styleable.yoga_yg_marginHorizontal) { - node.setMarginAuto(YogaEdge.HORIZONTAL); - } else if (attribute == R.styleable.yoga_yg_marginVertical) { - node.setMarginAuto(YogaEdge.VERTICAL); - } else if (attribute == R.styleable.yoga_yg_marginAll) { - node.setMarginAuto(YogaEdge.ALL); - } - } - - if (value.endsWith("%")) { - final float numericValue = Float.parseFloat(value.substring(0, value.length()-1)); - - if (attribute == R.styleable.yoga_yg_flexBasis) { - node.setFlexBasisPercent(numericValue); - } else if (attribute == R.styleable.yoga_yg_height) { - node.setHeightPercent(numericValue); - } else if (attribute == R.styleable.yoga_yg_marginLeft) { - node.setMarginPercent(YogaEdge.LEFT, numericValue); - } else if (attribute == R.styleable.yoga_yg_marginTop) { - node.setMarginPercent(YogaEdge.TOP, numericValue); - } else if (attribute == R.styleable.yoga_yg_marginRight) { - node.setMarginPercent(YogaEdge.RIGHT, numericValue); - } else if (attribute == R.styleable.yoga_yg_marginBottom) { - node.setMarginPercent(YogaEdge.BOTTOM, numericValue); - } else if (attribute == R.styleable.yoga_yg_marginStart) { - node.setMarginPercent(YogaEdge.START, numericValue); - } else if (attribute == R.styleable.yoga_yg_marginEnd) { - node.setMarginPercent(YogaEdge.END, numericValue); - } else if (attribute == R.styleable.yoga_yg_marginHorizontal) { - node.setMarginPercent(YogaEdge.HORIZONTAL, numericValue); - } else if (attribute == R.styleable.yoga_yg_marginVertical) { - node.setMarginPercent(YogaEdge.VERTICAL, numericValue); - } else if (attribute == R.styleable.yoga_yg_marginAll) { - node.setMarginPercent(YogaEdge.ALL, numericValue); - } else if (attribute == R.styleable.yoga_yg_maxHeight) { - node.setMaxHeightPercent(numericValue); - } else if (attribute == R.styleable.yoga_yg_maxWidth) { - node.setMaxWidthPercent(numericValue); - } else if (attribute == R.styleable.yoga_yg_minHeight) { - node.setMinHeightPercent(numericValue); - } else if (attribute == R.styleable.yoga_yg_minWidth) { - node.setMinWidthPercent(numericValue); - } else if (attribute == R.styleable.yoga_yg_paddingLeft) { - node.setPaddingPercent(YogaEdge.LEFT, numericValue); - } else if (attribute == R.styleable.yoga_yg_paddingTop) { - node.setPaddingPercent(YogaEdge.TOP, numericValue); - } else if (attribute == R.styleable.yoga_yg_paddingRight) { - node.setPaddingPercent(YogaEdge.RIGHT, numericValue); - } else if (attribute == R.styleable.yoga_yg_paddingBottom) { - node.setPaddingPercent(YogaEdge.BOTTOM, numericValue); - } else if (attribute == R.styleable.yoga_yg_paddingStart) { - node.setPaddingPercent(YogaEdge.START, numericValue); - } else if (attribute == R.styleable.yoga_yg_paddingEnd) { - node.setPaddingPercent(YogaEdge.END, numericValue); - } else if (attribute == R.styleable.yoga_yg_paddingHorizontal) { - node.setPaddingPercent(YogaEdge.HORIZONTAL, numericValue); - } else if (attribute == R.styleable.yoga_yg_paddingVertical) { - node.setPaddingPercent(YogaEdge.VERTICAL, numericValue); - } else if (attribute == R.styleable.yoga_yg_paddingAll) { - node.setPaddingPercent(YogaEdge.ALL, numericValue); - } else if (attribute == R.styleable.yoga_yg_positionLeft) { - node.setPositionPercent(YogaEdge.LEFT, numericValue); - } else if (attribute == R.styleable.yoga_yg_positionTop) { - node.setPositionPercent(YogaEdge.TOP, numericValue); - } else if (attribute == R.styleable.yoga_yg_positionRight) { - node.setPositionPercent(YogaEdge.RIGHT, numericValue); - } else if (attribute == R.styleable.yoga_yg_positionBottom) { - node.setPositionPercent(YogaEdge.BOTTOM, numericValue); - } else if (attribute == R.styleable.yoga_yg_positionStart) { - node.setPositionPercent(YogaEdge.START, numericValue); - } else if (attribute == R.styleable.yoga_yg_positionEnd) { - node.setPositionPercent(YogaEdge.END, numericValue); - } else if (attribute == R.styleable.yoga_yg_positionHorizontal) { - node.setPositionPercent(YogaEdge.HORIZONTAL, numericValue); - } else if (attribute == R.styleable.yoga_yg_positionVertical) { - node.setPositionPercent(YogaEdge.VERTICAL, numericValue); - } else if (attribute == R.styleable.yoga_yg_positionAll) { - node.setPositionPercent(YogaEdge.ALL, numericValue); - } else if (attribute == R.styleable.yoga_yg_width) { - node.setWidthPercent(numericValue); - } - } - } - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return new YogaLayout.LayoutParams(getContext(), attrs); - } - - @Override - protected ViewGroup.LayoutParams generateDefaultLayoutParams() { - return new YogaLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - } - - @Override - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return new YogaLayout.LayoutParams(p); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams; - } - - /** - * {@code YogaLayout.LayoutParams} are used by views to tell {@link YogaLayout} how they want to - * be laid out. More precisely, the specify the yoga parameters of the view. - * - *

- * This is actually mostly a wrapper around a {@code SparseArray} that holds a mapping between - * styleable id's ({@code R.styleable.yoga_yg_*}) and the float of their values. In cases where - * the value is an enum or an integer, they should first be cast to int (with rounding) before - * using. - */ - public static class LayoutParams extends ViewGroup.LayoutParams { - - /** - * A mapping from attribute keys ({@code R.styleable.yoga_yg_*}) to the float of their values. - * For attributes like position_percent_left (float), this is the native type. For attributes - * like align_self (enums), the integer enum value is cast (rounding is used on the other side - * to prevent precision errors). Dimension attributes are stored as float pixels. - */ - SparseArray numericAttributes; - - /** - * A mapping from attribute keys ({@code R.styleable.yoga_yg_*}) with string values to those - * strings. This is used for values such as "auto". - */ - SparseArray stringAttributes; - - /** - * Constructs a set of layout params from a source set. In the case that the source set is - * actually a {@link YogaLayout.LayoutParams}, we can copy all the yoga attributes. Otherwise - * we start with a blank slate. - * - * @param source The layout params to copy from - */ - public LayoutParams(ViewGroup.LayoutParams source) { - super(source); - if (source instanceof LayoutParams) { - numericAttributes = ((LayoutParams) source).numericAttributes.clone(); - stringAttributes = ((LayoutParams) source).stringAttributes.clone(); - } else { - numericAttributes = new SparseArray<>(); - stringAttributes = new SparseArray<>(); - - // Negative values include MATCH_PARENT and WRAP_CONTENT - if (source.width >= 0) { - numericAttributes.put(R.styleable.yoga_yg_width, (float) width); - } - if (source.height >= 0) { - numericAttributes.put(R.styleable.yoga_yg_height, (float) height); - } - } - } - - /** - * Constructs a set of layout params, given width and height specs. In this case, we can set - * the {@code yoga:width} and {@code yoga:height} if we are given them explicitly. If other - * options (such as {@code match_owner} or {@code wrap_content} are given, then the owner - * LayoutParams will store them, and we deal with them during layout. (see - * {@link YogaLayout#createLayout}) - * - * @param width the requested width, either a pixel size, {@code WRAP_CONTENT} or - * {@code MATCH_PARENT}. - * @param height the requested height, either a pixel size, {@code WRAP_CONTENT} or - * {@code MATCH_PARENT}. - */ - public LayoutParams(int width, int height) { - super(width, height); - numericAttributes = new SparseArray<>(); - stringAttributes = new SparseArray<>(); - // Negative values include MATCH_PARENT and WRAP_CONTENT - if (width >= 0) { - numericAttributes.put(R.styleable.yoga_yg_width, (float) width); - } - if (height >= 0) { - numericAttributes.put(R.styleable.yoga_yg_height, (float) height); - } - } - - /** - * Constructs a set of layout params, given attributes. Grabs all the {@code yoga:*} - * defined in {@code ALL_YOGA_ATTRIBUTES} and collects the ones that are set in {@code attrs}. - * - * @param context the application environment - * @param attrs the set of attributes from which to extract the yoga specific attributes - */ - public LayoutParams(Context context, AttributeSet attrs) { - super(context, attrs); - numericAttributes = new SparseArray<>(); - stringAttributes = new SparseArray<>(); - final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.yoga); - - // Negative values include MATCH_PARENT and WRAP_CONTENT - if (width >= 0) { - numericAttributes.put(R.styleable.yoga_yg_width, (float) width); - } - if (height >= 0) { - numericAttributes.put(R.styleable.yoga_yg_height, (float) height); - } - - final int attributeCount = a.getIndexCount(); - for (int i = 0; i < attributeCount; i++) { - final int attribute = a.getIndex(i); - final TypedValue val = new TypedValue(); - a.getValue(attribute, val); - - if (val.type == TypedValue.TYPE_DIMENSION) { - numericAttributes.put( - attribute, - (float) a.getDimensionPixelSize(attribute, 0)); - } else if (val.type == TypedValue.TYPE_STRING) { - stringAttributes.put(attribute, a.getString(attribute)); - } else { - numericAttributes.put(attribute, a.getFloat(attribute, 0)); - } - } - a.recycle(); - } - } - - /** - * Wrapper around measure function for yoga leaves. - */ - public static class ViewMeasureFunction implements YogaMeasureFunction { - - /** - * A function to measure leaves of the Yoga tree. Yoga needs some way to know how large - * elements want to be. This function passes that question directly through to the relevant - * {@code View}'s measure function. - * - * @param node The yoga node to measure - * @param width The suggested width from the owner - * @param widthMode The type of suggestion for the width - * @param height The suggested height from the owner - * @param heightMode The type of suggestion for the height - * @return A measurement output ({@code YogaMeasureOutput}) for the node - */ - public long measure( - YogaNode node, - float width, - YogaMeasureMode widthMode, - float height, - YogaMeasureMode heightMode) { - final View view = (View) node.getData(); - if (view == null || view instanceof YogaLayout) { - return YogaMeasureOutput.make(0, 0); - } - - final int widthMeasureSpec = MeasureSpec.makeMeasureSpec( - (int) width, - viewMeasureSpecFromYogaMeasureMode(widthMode)); - final int heightMeasureSpec = MeasureSpec.makeMeasureSpec( - (int) height, - viewMeasureSpecFromYogaMeasureMode(heightMode)); - - view.measure(widthMeasureSpec, heightMeasureSpec); - - return YogaMeasureOutput.make(view.getMeasuredWidth(), view.getMeasuredHeight()); - } - - private int viewMeasureSpecFromYogaMeasureMode(YogaMeasureMode mode) { - if (mode == YogaMeasureMode.AT_MOST) { - return MeasureSpec.AT_MOST; - } else if (mode == YogaMeasureMode.EXACTLY) { - return MeasureSpec.EXACTLY; - } else { - return MeasureSpec.UNSPECIFIED; - } - } - } -} diff --git a/android/src/main/java/com/facebook/yoga/android/YogaViewLayoutFactory.java b/android/src/main/java/com/facebook/yoga/android/YogaViewLayoutFactory.java deleted file mode 100644 index e8645351..00000000 --- a/android/src/main/java/com/facebook/yoga/android/YogaViewLayoutFactory.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.yoga.android; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; - -/** - * A layout inflater factory. This provides our custom {@link YogaViewLayoutFactory#onCreateView} - * to the XML inflation system, allowing us to replace XML tags. - */ -public class YogaViewLayoutFactory implements LayoutInflater.Factory { - private static YogaViewLayoutFactory sYogaViewLayoutFactory; - - /** - * Obtains (and initialises if necessary) the singleton {@link YogaViewLayoutFactory}. - * - * @return The singleton instance - */ - public static YogaViewLayoutFactory getInstance() { - if (sYogaViewLayoutFactory == null) { - sYogaViewLayoutFactory = new YogaViewLayoutFactory(); - } - return sYogaViewLayoutFactory; - } - - YogaViewLayoutFactory() {} - - /** - * Hook for inflating from a LayoutInflater. This hook replaces the cumbersome - * {@code com.facebook.etc.YogaLayout} with simply {@code YogaLayout} in your XML and the same - * with {@code VirtualYogaLayout}. - * - * @param name Tag name to be inflated. - * @param context The context the view is being created in. - * @param attrs Inflation attributes as specified in XML file. - * - * @return View Newly created view. Return null for the default behavior. - */ - @Override - public View onCreateView(String name, Context context, AttributeSet attrs) { - if (YogaLayout.class.getSimpleName().equals(name)) { - return new YogaLayout(context, attrs); - } - if (VirtualYogaLayout.class.getSimpleName().equals(name)) { - return new VirtualYogaLayout(context, attrs); - } - return null; - } -} diff --git a/android/src/main/res/values/attrs.xml b/android/src/main/res/values/attrs.xml deleted file mode 100644 index 3fca9997..00000000 --- a/android/src/main/res/values/attrs.xml +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sample/build.gradle b/sample/build.gradle deleted file mode 100644 index c5399fe0..00000000 --- a/sample/build.gradle +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -plugins { - id("com.android.application") -} - -android { - namespace 'com.facebook.yoga.sample' - - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion - ndkVersion rootProject.ndkVersion - - defaultConfig { - minSdkVersion rootProject.minSdkVersion - targetSdkVersion rootProject.targetSdkVersion - } - - compileOptions { - targetCompatibility rootProject.targetCompatibilityVersion - sourceCompatibility rootProject.sourceCompatibilityVersion - } -} - -dependencies { - implementation project(':yoga-layout') - implementation("androidx.appcompat:appcompat:1.6.1") - implementation("com.facebook.soloader:soloader:0.10.4") -} diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml deleted file mode 100644 index fd53d599..00000000 --- a/sample/src/main/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkActivity.java b/sample/src/main/java/com/facebook/yoga/sample/BenchmarkActivity.java deleted file mode 100644 index bbccee52..00000000 --- a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkActivity.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.yoga.sample; - -import android.content.Intent; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; - -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; -import androidx.fragment.app.FragmentTransaction; -import androidx.viewpager.widget.ViewPager; - -import com.facebook.yoga.android.YogaViewLayoutFactory; - -public class BenchmarkActivity extends AppCompatActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - LayoutInflater.from(this).setFactory(YogaViewLayoutFactory.getInstance()); - super.onCreate(savedInstanceState); - - setContentView(R.layout.benchmark_select_layout); - - ViewPager viewPager = findViewById(R.id.viewpager); - viewPager.setAdapter(new PagerAdapter(getSupportFragmentManager())); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - - ActionBar.TabListener tabListener = new ActionBar.TabListener() { - @Override - public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { - ViewPager viewPager = findViewById(R.id.viewpager); - viewPager.setCurrentItem(tab.getPosition()); - } - - @Override - public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { - } - - @Override - public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { - } - }; - actionBar.addTab( - actionBar.newTab() - .setText("Inflate") - .setTabListener(tabListener)); - actionBar.addTab( - actionBar.newTab() - .setText("Measure") - .setTabListener(tabListener)); - actionBar.addTab( - actionBar.newTab() - .setText("Layout") - .setTabListener(tabListener)); - - viewPager.setOnPageChangeListener( - new ViewPager.SimpleOnPageChangeListener() { - @Override - public void onPageSelected(int position) { - // When swiping between pages, select the - // corresponding tab. - actionBar.setSelectedNavigationItem(position); - } - }); - - viewPager.setOffscreenPageLimit(3); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.action_bar_benchmark, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // There is only one option - Intent intent = new Intent(this, MainActivity.class); - startActivity(intent); - this.finish(); - return true; - } - - public static class PagerAdapter extends FragmentPagerAdapter { - public PagerAdapter(FragmentManager fm) { - super(fm); - } - - @Override - public Fragment getItem(int i) { - switch (i) { - case 0: - return new BenchmarkInflate(); - case 1: - return new BenchmarkMeasure(); - default: - return new BenchmarkLayout(); - } - } - - @Override - public int getCount() { - return 3; - } - } -} diff --git a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkAggregator.java b/sample/src/main/java/com/facebook/yoga/sample/BenchmarkAggregator.java deleted file mode 100644 index c1507f78..00000000 --- a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkAggregator.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.yoga.sample; - -import java.io.File; -import java.io.PrintWriter; -import java.lang.Math; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.Date; - -import android.content.Context; -import android.util.Log; -import android.os.Environment; - -import static java.util.Collections.sort; - -public class BenchmarkAggregator { - - private final int GRAPH_WIDTH = 30; - private final int GRAPH_HEIGHT = 6; - - private List times; - private boolean tracing; - private long lastTraceStart; - - private boolean statsFresh; - private long mean; - private long variance; - private long stddev; - private long min; - private long max; - private long p10; - private long p50; - private long p90; - - private String name; - - public BenchmarkAggregator(String name) { - times = new ArrayList<>(); - tracing = false; - this.name = name; - } - - public void startTrace() { - if (tracing) { - throw new RuntimeException("Cannot start trace while running previous one"); - } - tracing = true; - lastTraceStart = System.nanoTime(); - } - - public void endTrace() { - if (!tracing) { - throw new RuntimeException("Cannot stop trace if none are running!"); - } - times.add(System.nanoTime() - lastTraceStart); - tracing = false; - statsFresh = false; - } - - private void computeStats() { - if (statsFresh) { - return; - } - - sort(times); - - min = Long.MAX_VALUE; - max = -1; - mean = 0; - for (long f: times) { - mean += f; - if (f < min) { - min = f; - } - if (f > max) { - max = f; - } - } - mean /= times.size(); - - variance = 0; - for (long f: times) { - variance += (f-mean)*(f-mean); - } - variance /= times.size(); - stddev = (long) Math.sqrt((double) variance); - - p10 = times.get(times.size()*10/100); - p50 = times.get(times.size()*50/100); - p90 = times.get(times.size()*90/100); - - statsFresh = true; - } - - public String toString() { - computeStats(); - return String.format( - "%s:\n" + - "| %d samples\n" + - "| Mean %.3f\u00B1%.3fms\n" + // plusminus - "| Min %.3fms ; Max %.3fms\n" + - "| p10 %.3fms ; p50 %.3fms ; p90 %.3fms\n" + - "%s", - name, - times.size(), - mean/10e6, - stddev/10e6, - min/10e6, - max/10e6, - p10/10e6, - p50/10e6, - p90/10e6, - makeGraph()); - } - - private String makeGraph() { - char canvas[][] = new char[GRAPH_HEIGHT][GRAPH_WIDTH]; - for (int i = 0; i < GRAPH_HEIGHT; i++) - for (int j = 0; j < GRAPH_WIDTH; j++) - canvas[i][j] = ' '; - - long bucketSize = (p90 - p10) / GRAPH_WIDTH+1; - int bucketCount[] = new int[GRAPH_WIDTH]; - for (long time : times) { - if (timep10) { - bucketCount[(int) ((time - p10) / bucketSize)]++; - } - } - - int maxBucket = 0; - for (int i = 0; i < GRAPH_WIDTH; i++) - if (bucketCount[i] > maxBucket) { - maxBucket = bucketCount[i]; - } - - for (int i = 0; i < GRAPH_HEIGHT; i++) - for (int j = 0; j < GRAPH_WIDTH; j++) - if (i < bucketCount[j] * GRAPH_HEIGHT / maxBucket) { - canvas[i][j] = 'Z'; - } - - String graph = new String(); - for (int i = 0; i < GRAPH_HEIGHT; i++) - { - int percentage = 100 * (GRAPH_HEIGHT - i - 1) * maxBucket / (times.size() * GRAPH_HEIGHT); - graph += String.format("| %2d%% ", percentage); - for (int j = 0; j < GRAPH_WIDTH; j++) - graph += canvas[GRAPH_HEIGHT-1-i][j]; - graph += '\n'; - } - - graph += "| p10"; - for (int i = 0; i < GRAPH_WIDTH-6; i++) - graph += " "; - graph += "p90\n"; - return graph; - } - - /** - * Dumps the collected times to a file on the device. This allows us to grab the raw data - * and perform more in-depth analysis. - */ - public void dump(Context context) { - String state = Environment.getExternalStorageState(); - if (!Environment.MEDIA_MOUNTED.equals(state)) { - Log.e("YogaLayoutBenchmark","No external file storage"); - return; - } - - SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); - String filename = format.format(new Date()) + "_" + name.replace(' ','_'); - File file = new File(context.getExternalFilesDir( - Environment.DIRECTORY_DOCUMENTS), filename); - - try { - PrintWriter printWriter = new PrintWriter(file); - for (long l : times) { - printWriter.println(l); - } - printWriter.close(); - - Log.i("YogaLayoutBenchmark","Benchmark data saved in "+file.getPath()); - } catch (java.io.IOException e) { - Log.e("YogaLayoutBenchmark", "Could not save benchmark data", e); - } - } -} diff --git a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkFragment.java b/sample/src/main/java/com/facebook/yoga/sample/BenchmarkFragment.java deleted file mode 100644 index 949a08a4..00000000 --- a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkFragment.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.yoga.sample; - - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.LinearLayout; -import android.widget.Spinner; -import android.widget.TextView; - -import androidx.fragment.app.Fragment; - -import com.facebook.yoga.android.YogaLayout; - -import java.util.Random; - -public class BenchmarkFragment extends Fragment implements AdapterView.OnItemSelectedListener { - private LayoutInflater mInflater; - - protected com.facebook.yoga.android.YogaLayout rootLayout; - protected int yogaLayout; - protected int linearLayout; - - static final Random random = new Random(); - - static void randomizeText(View root) { - if (root instanceof TextView) { - ((TextView) root).setText("" + random.nextInt(1000)); - ((TextView) root).setTextSize(10 + random.nextInt(20)); - ViewParent parent = root.getParent(); - if (parent instanceof YogaLayout) { - ((YogaLayout) parent).invalidate(root); - } - } else if (root instanceof ViewGroup) { - for (int i = 0; i < ((ViewGroup) root).getChildCount(); i++) { - randomizeText(((ViewGroup) root).getChildAt(i)); - } - } - } - - public BenchmarkFragment() { - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Override - public View onCreateView( - LayoutInflater inflater, - ViewGroup container, - Bundle savedInstanceState) { - mInflater = inflater; - - rootLayout = (YogaLayout) inflater.inflate( - R.layout.benchmark_fragment, - container, - false); - - Spinner benchmarkSelect = rootLayout.findViewById(R.id.benchmarkSelect); - String[] items = new String[]{"Basic", "Typical", "Nested"}; - ArrayAdapter adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_dropdown_item, items); - benchmarkSelect.setAdapter(adapter); - benchmarkSelect.setOnItemSelectedListener(this); - return rootLayout; - } - - @Override - public void onItemSelected(AdapterView parent, View view, int pos, long id) { - switch (pos) { - case 0: - yogaLayout = R.layout.benchmark_layout_1; - linearLayout = R.layout.benchmark_layout_1_linear; - break; - case 1: - yogaLayout = R.layout.benchmark_layout_2; - linearLayout = R.layout.benchmark_layout_2_linear; - break; - case 2: - default: - yogaLayout = R.layout.benchmark_layout_3; - linearLayout = R.layout.benchmark_layout_3_linear; - break; - } - updatePreview(); - } - - @Override - public void onNothingSelected(AdapterView parent) { - yogaLayout = R.layout.benchmark_layout_1; - linearLayout = R.layout.benchmark_layout_1_linear; - updatePreview(); - } - - private void updatePreview() { - LinearLayout previewLayout = rootLayout.findViewById(R.id.preview); - View v = mInflater.inflate(yogaLayout, rootLayout, false); - v.setLayoutParams(new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.MATCH_PARENT)); - previewLayout.removeAllViews(); - previewLayout.addView(v); - } -} diff --git a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkInflate.java b/sample/src/main/java/com/facebook/yoga/sample/BenchmarkInflate.java deleted file mode 100644 index 35fe3718..00000000 --- a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkInflate.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.yoga.sample; - -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; - -public class BenchmarkInflate extends BenchmarkFragment { - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - - Button b = rootLayout.findViewById(R.id.btn); - b.setOnClickListener(v -> startBenchmark()); - - return rootLayout; - } - - protected void startBenchmark() { - LayoutInflater inflater = LayoutInflater.from(getActivity()); - TextView textView = rootLayout.findViewById(R.id.text); - - final int ITERATIONS = 500; - - inflater.inflate(yogaLayout, null); - inflater.inflate(linearLayout, null); - - BenchmarkAggregator yogaInflationAggregator = new BenchmarkAggregator("Yoga Inflate"); - BenchmarkAggregator linearInflationAggregator = new BenchmarkAggregator("Linear Inflate"); - for (int i = 0; i < ITERATIONS; i++) { - yogaInflationAggregator.startTrace(); - inflater.inflate(yogaLayout, null); - yogaInflationAggregator.endTrace(); - linearInflationAggregator.startTrace(); - inflater.inflate(linearLayout, null); - linearInflationAggregator.endTrace(); - } - - textView.setText( - yogaInflationAggregator.toString() + - "\n" + - linearInflationAggregator.toString()); - Log.i( - "YogaLayoutBenchmark", - yogaInflationAggregator.toString() + - "\n" + - linearInflationAggregator.toString()); - rootLayout.invalidate(); - } -} diff --git a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkLayout.java b/sample/src/main/java/com/facebook/yoga/sample/BenchmarkLayout.java deleted file mode 100644 index b674d5dd..00000000 --- a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkLayout.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.yoga.sample; - -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; - -public class BenchmarkLayout extends BenchmarkFragment { - - @Override - public View onCreateView( - LayoutInflater inflater, - ViewGroup container, - Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - - Button b = rootLayout.findViewById(R.id.btn); - b.setOnClickListener(v -> startBenchmark()); - - return rootLayout; - } - - protected void startBenchmark() { - LayoutInflater inflater = LayoutInflater.from(getActivity()); - TextView textView = rootLayout.findViewById(R.id.text); - - final int ITERATIONS = 500; - - BenchmarkAggregator yogaInflationAggregator = new BenchmarkAggregator("Yoga Layout"); - BenchmarkAggregator linearInflationAggregator = new BenchmarkAggregator("Linear Layout"); - View yogaView = inflater.inflate(yogaLayout, null); - View linearView = inflater.inflate(linearLayout, null); - for (int i = 0; i < ITERATIONS; i++) { - randomizeText(yogaView); - randomizeText(linearView); - yogaView.measure( - View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY), - View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY)); - linearView.measure( - View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY), - View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY)); - yogaInflationAggregator.startTrace(); - yogaView.layout(0, 0, yogaView.getMeasuredWidth(), yogaView.getMeasuredHeight()); - yogaInflationAggregator.endTrace(); - linearInflationAggregator.startTrace(); - linearView.layout(0, 0, linearView.getMeasuredWidth(), linearView.getMeasuredHeight()); - linearInflationAggregator.endTrace(); - } - - textView.setText( - yogaInflationAggregator + - "\n" + - linearInflationAggregator); - Log.i( - "YogaLayoutBenchmark", - yogaInflationAggregator + - "\n" + - linearInflationAggregator); - } -} diff --git a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkMeasure.java b/sample/src/main/java/com/facebook/yoga/sample/BenchmarkMeasure.java deleted file mode 100644 index 61276eb9..00000000 --- a/sample/src/main/java/com/facebook/yoga/sample/BenchmarkMeasure.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.yoga.sample; - -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; - -import java.util.Random; - -public class BenchmarkMeasure extends BenchmarkFragment { - - @Override - public View onCreateView( - LayoutInflater inflater, - ViewGroup container, - Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - - Button b = rootLayout.findViewById(R.id.btn); - b.setOnClickListener(v -> startBenchmark()); - - return rootLayout; - } - - protected void startBenchmark() { - LayoutInflater inflater = LayoutInflater.from(getActivity()); - TextView textView = (TextView) rootLayout.findViewById(R.id.text); - Random random = new Random(); - - final int ITERATIONS = 500; - - BenchmarkAggregator yogaMeasureAggregator = new BenchmarkAggregator("Yoga Measure"); - BenchmarkAggregator linearMeasureAggregator = new BenchmarkAggregator("Linear Measure"); - View yogaView = inflater.inflate(yogaLayout, null); - View linearView = inflater.inflate(linearLayout, null); - for (int i = 0; i < ITERATIONS; i++) { - randomizeText(yogaView); - randomizeText(linearView); - yogaMeasureAggregator.startTrace(); - yogaView.measure( - View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY), - View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY)); - yogaMeasureAggregator.endTrace(); - linearMeasureAggregator.startTrace(); - linearView.measure( - View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY), - View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY)); - linearMeasureAggregator.endTrace(); - } - - textView.setText( - yogaMeasureAggregator.toString() + - "\n" + - linearMeasureAggregator.toString()); - Log.i( - "YogaLayoutBenchmark", - yogaMeasureAggregator.toString() + - "\n" + - linearMeasureAggregator.toString()); - - yogaMeasureAggregator.dump(getActivity()); - linearMeasureAggregator.dump(getActivity()); - } -} diff --git a/sample/src/main/java/com/facebook/yoga/sample/MainActivity.java b/sample/src/main/java/com/facebook/yoga/sample/MainActivity.java deleted file mode 100644 index 5d5e5210..00000000 --- a/sample/src/main/java/com/facebook/yoga/sample/MainActivity.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.yoga.sample; - -import android.content.Intent; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; - -import com.facebook.soloader.SoLoader; -import com.facebook.yoga.android.YogaViewLayoutFactory; - -/** - * An activity to show off Yoga in Android. This activity shows a simple layout (defined in - * {@code main_layout.xml}) that shows off the awesome functionality of the Yoga layout engine - * as well as some optimisations on layout systems that it facilitates. - */ -public class MainActivity extends AppCompatActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - LayoutInflater.from(this).setFactory(YogaViewLayoutFactory.getInstance()); - super.onCreate(savedInstanceState); - SoLoader.init(this, false); - - setContentView(R.layout.main_layout); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.action_bar_home, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - // There is only one option - Intent intent = new Intent(this, BenchmarkActivity.class); - startActivity(intent); - this.finish(); - return true; - } -} diff --git a/sample/src/main/res/drawable/action_bar_background.xml b/sample/src/main/res/drawable/action_bar_background.xml deleted file mode 100644 index 6c70b26b..00000000 --- a/sample/src/main/res/drawable/action_bar_background.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - diff --git a/sample/src/main/res/drawable/ic_launcher.png b/sample/src/main/res/drawable/ic_launcher.png deleted file mode 100644 index c99f981211e071430153a59be7c2b9d00405649c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28905 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�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 ScLa1tqOPi=QvT${oBsnNDZASM diff --git a/sample/src/main/res/drawable/sample_children_background.xml b/sample/src/main/res/drawable/sample_children_background.xml deleted file mode 100644 index 8d63336a..00000000 --- a/sample/src/main/res/drawable/sample_children_background.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - diff --git a/sample/src/main/res/layout/benchmark_fragment.xml b/sample/src/main/res/layout/benchmark_fragment.xml deleted file mode 100644 index a2c98fdf..00000000 --- a/sample/src/main/res/layout/benchmark_fragment.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - -