diff --git a/.buckconfig b/.buckconfig
index 74a40a76..095cc442 100644
--- a/.buckconfig
+++ b/.buckconfig
@@ -1,2 +1,9 @@
[cxx]
gtest_dep = //lib/gtest:gtest
+[android]
+ target = Google Inc.:Google APIs:19
+[ndk]
+ ndk_version = r10e
+ compiler = clang
+ app_platform = android-19
+ cpu_abis = armv7, x86
diff --git a/YOGA_DEFS b/YOGA_DEFS
index 8d17d58f..7f2f082a 100644
--- a/YOGA_DEFS
+++ b/YOGA_DEFS
@@ -1,5 +1,6 @@
YOGA_ROOT = '//...'
+JAVA_TARGET = '//java:java'
INFER_ANNOTATIONS_TARGET = '//lib/infer-annotations:infer-annotations'
JSR_305_TARGET = '//lib/jsr-305:jsr-305'
JUNIT_TARGET = '//lib/junit:junit'
@@ -8,6 +9,10 @@ SOLOADER_TARGET = '//lib/soloader:soloader'
GTEST_TARGET = '//lib/gtest:gtest'
JNI_TARGET = '//lib/jni:jni'
FBJNI_TARGET = '//lib/fb:fbjni'
+APPCOMPAT_TARGET = '//lib/appcompat:appcompat'
+ANDROID_SUPPORT_TARGET = '//lib/android-support:android-support'
+ANDROID_SAMPLE_JAVA_TARGET = '//android/sample/java/com/facebook/samples/yoga:yoga'
+ANDROID_SAMPLE_RES_TARGET = '//android/sample/res/com/facebook/samples/yoga:res'
THIS_IS_FBOBJC = False
diff --git a/android/sample/AndroidManifest.xml b/android/sample/AndroidManifest.xml
new file mode 100644
index 00000000..7e3c5284
--- /dev/null
+++ b/android/sample/AndroidManifest.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/sample/BUCK b/android/sample/BUCK
new file mode 100644
index 00000000..5eeb672e
--- /dev/null
+++ b/android/sample/BUCK
@@ -0,0 +1,28 @@
+# Copyright (c) 2014-present, Facebook, Inc.
+# All rights reserved.
+#
+# This source code is licensed under the BSD-style license found in the
+# LICENSE file in the root directory of this source tree. An additional grant
+# of patent rights can be found in the PATENTS file in the same directory.
+
+include_defs('//YOGA_DEFS')
+
+android_binary(
+ name = 'sample',
+ manifest = 'AndroidManifest.xml',
+ keystore = ':debug_keystore',
+ deps = [
+ ANDROID_SAMPLE_JAVA_TARGET,
+ ANDROID_SAMPLE_RES_TARGET,
+ ],
+)
+
+keystore(
+ name='debug_keystore',
+ store='debug.keystore',
+ properties='debug.keystore.properties',
+)
+
+project_config(
+ src_target = ':sample',
+)
diff --git a/android/sample/debug.keystore b/android/sample/debug.keystore
new file mode 100644
index 00000000..3df12b5d
Binary files /dev/null and b/android/sample/debug.keystore differ
diff --git a/android/sample/debug.keystore.properties b/android/sample/debug.keystore.properties
new file mode 100644
index 00000000..3c06c8e4
--- /dev/null
+++ b/android/sample/debug.keystore.properties
@@ -0,0 +1,3 @@
+key.alias=androiddebugkey
+key.store.password=android
+key.alias.password=android
diff --git a/android/sample/java/com/facebook/samples/yoga/BUCK b/android/sample/java/com/facebook/samples/yoga/BUCK
new file mode 100644
index 00000000..bc411285
--- /dev/null
+++ b/android/sample/java/com/facebook/samples/yoga/BUCK
@@ -0,0 +1,27 @@
+# Copyright (c) 2014-present, Facebook, Inc.
+# All rights reserved.
+#
+# This source code is licensed under the BSD-style license found in the
+# LICENSE file in the root directory of this source tree. An additional grant
+# of patent rights can be found in the PATENTS file in the same directory.
+
+include_defs('//YOGA_DEFS')
+
+android_library(
+ name = 'yoga',
+ srcs = glob(['**/*.java']),
+ deps = [
+ ANDROID_SAMPLE_RES_TARGET,
+ ANDROID_SUPPORT_TARGET,
+ APPCOMPAT_TARGET,
+ JAVA_TARGET,
+ SOLOADER_TARGET,
+ ],
+ visibility = [
+ 'PUBLIC',
+ ]
+)
+
+project_config(
+ src_target = ":yoga"
+)
diff --git a/android/sample/java/com/facebook/samples/yoga/MainActivity.java b/android/sample/java/com/facebook/samples/yoga/MainActivity.java
new file mode 100644
index 00000000..12de8c91
--- /dev/null
+++ b/android/sample/java/com/facebook/samples/yoga/MainActivity.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2014-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the license found in the
+ * LICENSE-examples file in the root directory of this source tree.
+ */
+
+package com.facebook.samples.yoga;
+
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+import android.view.LayoutInflater;
+
+import com.facebook.samples.yoga.R;
+
+/**
+ * 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 ActionBarActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ LayoutInflater.from(this).setFactory(YogaViewLayoutFactory.getInstance());
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.main_layout);
+ }
+}
diff --git a/android/sample/java/com/facebook/samples/yoga/SplashScreenActivity.java b/android/sample/java/com/facebook/samples/yoga/SplashScreenActivity.java
new file mode 100644
index 00000000..5553360d
--- /dev/null
+++ b/android/sample/java/com/facebook/samples/yoga/SplashScreenActivity.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2014-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the license found in the
+ * LICENSE-examples file in the root directory of this source tree.
+ */
+
+package com.facebook.samples.yoga;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.v7.app.ActionBarActivity;
+import android.view.LayoutInflater;
+
+import com.facebook.samples.yoga.R;
+import com.facebook.soloader.SoLoader;
+
+/**
+ * A (non-interactive) splash screen. Displays for two seconds before calling the main activity.
+ */
+public class SplashScreenActivity extends ActionBarActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ LayoutInflater.from(this).setFactory(YogaViewLayoutFactory.getInstance());
+ super.onCreate(savedInstanceState);
+ SoLoader.init(this, false);
+
+ setContentView(R.layout.splash_layout);
+
+ new Handler(Looper.getMainLooper()).postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ startMainActivity();
+ }
+ },
+ 2000);
+ }
+
+ private void startMainActivity() {
+ Intent intent = new Intent(this, MainActivity.class);
+ startActivity(intent);
+ this.finish();
+ }
+}
diff --git a/android/sample/java/com/facebook/samples/yoga/VirtualYogaLayout.java b/android/sample/java/com/facebook/samples/yoga/VirtualYogaLayout.java
new file mode 100644
index 00000000..865a02da
--- /dev/null
+++ b/android/sample/java/com/facebook/samples/yoga/VirtualYogaLayout.java
@@ -0,0 +1,150 @@
+/**
+ * Copyright (c) 2014-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.samples.yoga;
+
+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;
+
+/**
+ * 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 = new YogaNode();
+
+ 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 = new YogaNode();
+ 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/sample/java/com/facebook/samples/yoga/YogaLayout.java b/android/sample/java/com/facebook/samples/yoga/YogaLayout.java
new file mode 100644
index 00000000..23dbc747
--- /dev/null
+++ b/android/sample/java/com/facebook/samples/yoga/YogaLayout.java
@@ -0,0 +1,737 @@
+/**
+ * Copyright (c) 2014-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.samples.yoga;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+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.samples.yoga.R;
+import com.facebook.yoga.YogaAlign;
+import com.facebook.yoga.YogaConstants;
+import com.facebook.yoga.YogaDirection;
+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.YogaNodeAPI;
+import com.facebook.yoga.YogaOverflow;
+import com.facebook.yoga.YogaPositionType;
+import com.facebook.yoga.YogaWrap;
+
+/**
+ * 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 = new YogaNode();
+ mYogaNodes = new HashMap<>();
+
+ mYogaNode.setData(this);
+ mYogaNode.setMeasureFunction(new ViewMeasureFunction());
+
+ final LayoutParams layoutParams = new LayoutParams(context, attrs);
+ applyLayoutParams(layoutParams, mYogaNode, this);
+ }
+
+ YogaNode getYogaNode() {
+ return mYogaNode;
+ }
+
+ 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 Toga 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 {
+ childNode = new YogaNode();
+
+ 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();
+ }
+
+ private void removeViewFromYogaTree(View view, boolean inLayout) {
+ final YogaNode node = mYogaNodes.get(view);
+ if (node == null) {
+ return;
+ }
+
+ final YogaNode parent = node.getParent();
+
+ for (int i = 0; i < parent.getChildCount(); i++) {
+ if (parent.getChildAt(i).equals(node)) {
+ parent.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;
+ }
+ view.layout(
+ Math.round(xOffset + node.getLayoutX()),
+ Math.round(yOffset + node.getLayoutY()),
+ Math.round(xOffset + node.getLayoutX() + node.getLayoutWidth()),
+ Math.round(yOffset + node.getLayoutY() + node.getLayoutHeight()));
+ }
+
+ 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 parent'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.attributes.size(); i++) {
+ final int attribute = layoutParameters.attributes.keyAt(i);
+ final float value = layoutParameters.attributes.valueAt(i);
+
+ if (attribute == R.styleable.yoga_align_content) {
+ node.setAlignContent(YogaAlign.fromInt(Math.round(value)));
+ } else if (attribute == R.styleable.yoga_align_items) {
+ node.setAlignItems(YogaAlign.fromInt(Math.round(value)));
+ } else if (attribute == R.styleable.yoga_align_self) {
+ node.setAlignSelf(YogaAlign.fromInt(Math.round(value)));
+ } else if (attribute == R.styleable.yoga_aspect_ratio) {
+ node.setAspectRatio(value);
+ } else if (attribute == R.styleable.yoga_border_left) {
+ node.setBorder(YogaEdge.LEFT, value);
+ } else if (attribute == R.styleable.yoga_border_top) {
+ node.setBorder(YogaEdge.TOP, value);
+ } else if (attribute == R.styleable.yoga_border_right) {
+ node.setBorder(YogaEdge.RIGHT, value);
+ } else if (attribute == R.styleable.yoga_border_bottom) {
+ node.setBorder(YogaEdge.BOTTOM, value);
+ } else if (attribute == R.styleable.yoga_border_start) {
+ node.setBorder(YogaEdge.START, value);
+ } else if (attribute == R.styleable.yoga_border_end) {
+ node.setBorder(YogaEdge.END, value);
+ } else if (attribute == R.styleable.yoga_border_horizontal) {
+ node.setBorder(YogaEdge.HORIZONTAL, value);
+ } else if (attribute == R.styleable.yoga_border_vertical) {
+ node.setBorder(YogaEdge.VERTICAL, value);
+ } else if (attribute == R.styleable.yoga_border_all) {
+ node.setBorder(YogaEdge.ALL, value);
+ } else if (attribute == R.styleable.yoga_direction) {
+ node.setDirection(YogaDirection.fromInt(Math.round(value)));
+ } else if (attribute == R.styleable.yoga_flex) {
+ node.setFlex(value);
+ } else if (attribute == R.styleable.yoga_flex_basis) {
+ node.setFlexBasis(value);
+ } else if (attribute == R.styleable.yoga_flex_basis_percent) {
+ node.setFlexBasisPercent(value);
+ } else if (attribute == R.styleable.yoga_flex_direction) {
+ node.setFlexDirection(YogaFlexDirection.fromInt(Math.round(value)));
+ } else if (attribute == R.styleable.yoga_flex_grow) {
+ node.setFlexGrow(value);
+ } else if (attribute == R.styleable.yoga_flex_shrink) {
+ node.setFlexShrink(value);
+ } else if (attribute == R.styleable.yoga_height) {
+ node.setHeight(value);
+ } else if (attribute == R.styleable.yoga_height_percent) {
+ node.setHeightPercent(value);
+ } else if (attribute == R.styleable.yoga_margin_left) {
+ node.setMargin(YogaEdge.LEFT, value);
+ } else if (attribute == R.styleable.yoga_justify_content) {
+ node.setJustifyContent(YogaJustify.fromInt(Math.round(value)));
+ } else if (attribute == R.styleable.yoga_margin_top) {
+ node.setMargin(YogaEdge.TOP, value);
+ } else if (attribute == R.styleable.yoga_margin_right) {
+ node.setMargin(YogaEdge.RIGHT, value);
+ } else if (attribute == R.styleable.yoga_margin_bottom) {
+ node.setMargin(YogaEdge.BOTTOM, value);
+ } else if (attribute == R.styleable.yoga_margin_start) {
+ node.setMargin(YogaEdge.START, value);
+ } else if (attribute == R.styleable.yoga_margin_end) {
+ node.setMargin(YogaEdge.END, value);
+ } else if (attribute == R.styleable.yoga_margin_horizontal) {
+ node.setMargin(YogaEdge.HORIZONTAL, value);
+ } else if (attribute == R.styleable.yoga_margin_vertical) {
+ node.setMargin(YogaEdge.VERTICAL, value);
+ } else if (attribute == R.styleable.yoga_margin_all) {
+ node.setMargin(YogaEdge.ALL, value);
+ } else if (attribute == R.styleable.yoga_margin_percent_left) {
+ node.setMarginPercent(YogaEdge.LEFT, value);
+ } else if (attribute == R.styleable.yoga_margin_percent_top) {
+ node.setMarginPercent(YogaEdge.TOP, value);
+ } else if (attribute == R.styleable.yoga_margin_percent_right) {
+ node.setMarginPercent(YogaEdge.RIGHT, value);
+ } else if (attribute == R.styleable.yoga_margin_percent_bottom) {
+ node.setMarginPercent(YogaEdge.BOTTOM, value);
+ } else if (attribute == R.styleable.yoga_margin_percent_start) {
+ node.setMarginPercent(YogaEdge.START, value);
+ } else if (attribute == R.styleable.yoga_margin_percent_end) {
+ node.setMarginPercent(YogaEdge.END, value);
+ } else if (attribute == R.styleable.yoga_margin_percent_horizontal) {
+ node.setMarginPercent(YogaEdge.HORIZONTAL, value);
+ } else if (attribute == R.styleable.yoga_margin_percent_vertical) {
+ node.setMarginPercent(YogaEdge.VERTICAL, value);
+ } else if (attribute == R.styleable.yoga_margin_percent_all) {
+ node.setMarginPercent(YogaEdge.ALL, value);
+ } else if (attribute == R.styleable.yoga_max_height) {
+ node.setMaxHeight(value);
+ } else if (attribute == R.styleable.yoga_max_height_percent) {
+ node.setMaxHeightPercent(value);
+ } else if (attribute == R.styleable.yoga_max_width) {
+ node.setMaxWidth(value);
+ } else if (attribute == R.styleable.yoga_max_width_percent) {
+ node.setMaxWidthPercent(value);
+ } else if (attribute == R.styleable.yoga_min_height) {
+ node.setMinHeight(value);
+ } else if (attribute == R.styleable.yoga_min_height_percent) {
+ node.setMinHeightPercent(value);
+ } else if (attribute == R.styleable.yoga_min_width) {
+ node.setMinWidth(value);
+ } else if (attribute == R.styleable.yoga_min_width_percent) {
+ node.setMinWidthPercent(value);
+ } else if (attribute == R.styleable.yoga_overflow) {
+ node.setOverflow(YogaOverflow.fromInt(Math.round(value)));
+ } else if (attribute == R.styleable.yoga_padding_left) {
+ node.setPadding(YogaEdge.LEFT, value);
+ } else if (attribute == R.styleable.yoga_padding_top) {
+ node.setPadding(YogaEdge.TOP, value);
+ } else if (attribute == R.styleable.yoga_padding_right) {
+ node.setPadding(YogaEdge.RIGHT, value);
+ } else if (attribute == R.styleable.yoga_padding_bottom) {
+ node.setPadding(YogaEdge.BOTTOM, value);
+ } else if (attribute == R.styleable.yoga_padding_start) {
+ node.setPadding(YogaEdge.START, value);
+ } else if (attribute == R.styleable.yoga_padding_end) {
+ node.setPadding(YogaEdge.END, value);
+ } else if (attribute == R.styleable.yoga_padding_horizontal) {
+ node.setPadding(YogaEdge.HORIZONTAL, value);
+ } else if (attribute == R.styleable.yoga_padding_vertical) {
+ node.setPadding(YogaEdge.VERTICAL, value);
+ } else if (attribute == R.styleable.yoga_padding_all) {
+ node.setPadding(YogaEdge.ALL, value);
+ } else if (attribute == R.styleable.yoga_padding_percent_left) {
+ node.setPaddingPercent(YogaEdge.LEFT, value);
+ } else if (attribute == R.styleable.yoga_padding_percent_top) {
+ node.setPaddingPercent(YogaEdge.TOP, value);
+ } else if (attribute == R.styleable.yoga_padding_percent_right) {
+ node.setPaddingPercent(YogaEdge.RIGHT, value);
+ } else if (attribute == R.styleable.yoga_padding_percent_bottom) {
+ node.setPaddingPercent(YogaEdge.BOTTOM, value);
+ } else if (attribute == R.styleable.yoga_padding_percent_start) {
+ node.setPaddingPercent(YogaEdge.START, value);
+ } else if (attribute == R.styleable.yoga_padding_percent_end) {
+ node.setPaddingPercent(YogaEdge.END, value);
+ } else if (attribute == R.styleable.yoga_padding_percent_horizontal) {
+ node.setPaddingPercent(YogaEdge.HORIZONTAL, value);
+ } else if (attribute == R.styleable.yoga_padding_percent_vertical) {
+ node.setPaddingPercent(YogaEdge.VERTICAL, value);
+ } else if (attribute == R.styleable.yoga_padding_percent_all) {
+ node.setPaddingPercent(YogaEdge.ALL, value);
+ } else if (attribute == R.styleable.yoga_position_left) {
+ node.setPosition(YogaEdge.LEFT, value);
+ } else if (attribute == R.styleable.yoga_position_top) {
+ node.setPosition(YogaEdge.TOP, value);
+ } else if (attribute == R.styleable.yoga_position_right) {
+ node.setPosition(YogaEdge.RIGHT, value);
+ } else if (attribute == R.styleable.yoga_position_bottom) {
+ node.setPosition(YogaEdge.BOTTOM, value);
+ } else if (attribute == R.styleable.yoga_position_start) {
+ node.setPosition(YogaEdge.START, value);
+ } else if (attribute == R.styleable.yoga_position_end) {
+ node.setPosition(YogaEdge.END, value);
+ } else if (attribute == R.styleable.yoga_position_horizontal) {
+ node.setPosition(YogaEdge.HORIZONTAL, value);
+ } else if (attribute == R.styleable.yoga_position_vertical) {
+ node.setPosition(YogaEdge.VERTICAL, value);
+ } else if (attribute == R.styleable.yoga_position_all) {
+ node.setPosition(YogaEdge.ALL, value);
+ } else if (attribute == R.styleable.yoga_position_percent_left) {
+ node.setPositionPercent(YogaEdge.LEFT, value);
+ } else if (attribute == R.styleable.yoga_position_percent_top) {
+ node.setPositionPercent(YogaEdge.TOP, value);
+ } else if (attribute == R.styleable.yoga_position_percent_right) {
+ node.setPositionPercent(YogaEdge.RIGHT, value);
+ } else if (attribute == R.styleable.yoga_position_percent_bottom) {
+ node.setPositionPercent(YogaEdge.BOTTOM, value);
+ } else if (attribute == R.styleable.yoga_position_percent_start) {
+ node.setPositionPercent(YogaEdge.START, value);
+ } else if (attribute == R.styleable.yoga_position_percent_end) {
+ node.setPositionPercent(YogaEdge.END, value);
+ } else if (attribute == R.styleable.yoga_position_percent_horizontal) {
+ node.setPositionPercent(YogaEdge.HORIZONTAL, value);
+ } else if (attribute == R.styleable.yoga_position_percent_vertical) {
+ node.setPositionPercent(YogaEdge.VERTICAL, value);
+ } else if (attribute == R.styleable.yoga_position_percent_all) {
+ node.setPositionPercent(YogaEdge.ALL, value);
+ } else if (attribute == R.styleable.yoga_position_type) {
+ node.setPositionType(YogaPositionType.fromInt(Math.round(value)));
+ } else if (attribute == R.styleable.yoga_width) {
+ node.setWidth(value);
+ } else if (attribute == R.styleable.yoga_width_percent) {
+ node.setWidthPercent(value);
+ } else if (attribute == R.styleable.yoga_wrap) {
+ node.setWrap(YogaWrap.fromInt(Math.round(value)));
+ }
+ }
+ }
+
+ @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_*}) 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_*}) 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 attributes;
+
+ /**
+ * 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) {
+ attributes = ((LayoutParams) source).attributes.clone();
+ } else {
+ attributes = new SparseArray<>();
+
+ // Negative values include MATCH_PARENT and WRAP_CONTENT
+ if (source.width >= 0) {
+ attributes.put(R.styleable.yoga_width, (float) width);
+ }
+ if (source.height >= 0) {
+ attributes.put(R.styleable.yoga_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_parent} or {@code wrap_content} are given, then the parent
+ * 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);
+ attributes = new SparseArray<>();
+ // Negative values include MATCH_PARENT and WRAP_CONTENT
+ if (width >= 0) {
+ attributes.put(R.styleable.yoga_width, (float) width);
+ }
+ if (height >= 0) {
+ attributes.put(R.styleable.yoga_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);
+ attributes = new SparseArray<>();
+ final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.yoga);
+
+ // Negative values include MATCH_PARENT and WRAP_CONTENT
+ if (width >= 0) {
+ attributes.put(R.styleable.yoga_width, (float) width);
+ }
+ if (height >= 0) {
+ attributes.put(R.styleable.yoga_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) {
+ attributes.put(
+ attribute,
+ (float) a.getDimensionPixelSize(attribute, 0));
+ } else {
+ attributes.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 parent
+ * @param widthMode The type of suggestion for the width
+ * @param height The suggested height from the parent
+ * @param heightMode The type of suggestion for the height
+ * @return A measurement output ({@code YogaMeasureOutput}) for the node
+ */
+ public long measure(
+ YogaNodeAPI 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/sample/java/com/facebook/samples/yoga/YogaViewLayoutFactory.java b/android/sample/java/com/facebook/samples/yoga/YogaViewLayoutFactory.java
new file mode 100644
index 00000000..45df625f
--- /dev/null
+++ b/android/sample/java/com/facebook/samples/yoga/YogaViewLayoutFactory.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2014-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.samples.yoga;
+
+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/sample/res/com/facebook/samples/yoga/BUCK b/android/sample/res/com/facebook/samples/yoga/BUCK
new file mode 100644
index 00000000..83268716
--- /dev/null
+++ b/android/sample/res/com/facebook/samples/yoga/BUCK
@@ -0,0 +1,21 @@
+# Copyright (c) 2014-present, Facebook, Inc.
+# All rights reserved.
+#
+# This source code is licensed under the BSD-style license found in the
+# LICENSE file in the root directory of this source tree. An additional grant
+# of patent rights can be found in the PATENTS file in the same directory.
+
+include_defs('//YOGA_DEFS')
+
+android_resource(
+ name = 'res',
+ res = 'res',
+ package = 'com.facebook.samples.yoga',
+ visibility = [
+ 'PUBLIC',
+ ],
+)
+
+project_config(
+ src_target = ':res'
+)
diff --git a/android/sample/res/com/facebook/samples/yoga/res/drawable/action_bar_background.xml b/android/sample/res/com/facebook/samples/yoga/res/drawable/action_bar_background.xml
new file mode 100644
index 00000000..6c70b26b
--- /dev/null
+++ b/android/sample/res/com/facebook/samples/yoga/res/drawable/action_bar_background.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/android/sample/res/com/facebook/samples/yoga/res/drawable/ic_launcher.png b/android/sample/res/com/facebook/samples/yoga/res/drawable/ic_launcher.png
new file mode 100644
index 00000000..c99f9812
Binary files /dev/null and b/android/sample/res/com/facebook/samples/yoga/res/drawable/ic_launcher.png differ
diff --git a/android/sample/res/com/facebook/samples/yoga/res/drawable/sample_children_background.xml b/android/sample/res/com/facebook/samples/yoga/res/drawable/sample_children_background.xml
new file mode 100644
index 00000000..8d63336a
--- /dev/null
+++ b/android/sample/res/com/facebook/samples/yoga/res/drawable/sample_children_background.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/sample/res/com/facebook/samples/yoga/res/layout/main_layout.xml b/android/sample/res/com/facebook/samples/yoga/res/layout/main_layout.xml
new file mode 100644
index 00000000..295a3a68
--- /dev/null
+++ b/android/sample/res/com/facebook/samples/yoga/res/layout/main_layout.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/sample/res/com/facebook/samples/yoga/res/layout/splash_layout.xml b/android/sample/res/com/facebook/samples/yoga/res/layout/splash_layout.xml
new file mode 100644
index 00000000..051276ca
--- /dev/null
+++ b/android/sample/res/com/facebook/samples/yoga/res/layout/splash_layout.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/android/sample/res/com/facebook/samples/yoga/res/values/attrs.xml b/android/sample/res/com/facebook/samples/yoga/res/values/attrs.xml
new file mode 100644
index 00000000..61c8c643
--- /dev/null
+++ b/android/sample/res/com/facebook/samples/yoga/res/values/attrs.xml
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/sample/res/com/facebook/samples/yoga/res/values/colors.xml b/android/sample/res/com/facebook/samples/yoga/res/values/colors.xml
new file mode 100644
index 00000000..e1a99a61
--- /dev/null
+++ b/android/sample/res/com/facebook/samples/yoga/res/values/colors.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ #FF303846
+ #FF97DCCF
+
+ #FFFFFFFF
+ #665890ff
+ #FF23355b
+
+
diff --git a/android/sample/res/com/facebook/samples/yoga/res/values/strings.xml b/android/sample/res/com/facebook/samples/yoga/res/values/strings.xml
new file mode 100644
index 00000000..8b884c57
--- /dev/null
+++ b/android/sample/res/com/facebook/samples/yoga/res/values/strings.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+ Yoga
+ Hello. I am Yoga!
+ I am a layout engine!
+ I run natively.
+ So I\'m fast.
+ Who are you?
+
diff --git a/android/sample/res/com/facebook/samples/yoga/res/values/styles.xml b/android/sample/res/com/facebook/samples/yoga/res/values/styles.xml
new file mode 100644
index 00000000..0276b8df
--- /dev/null
+++ b/android/sample/res/com/facebook/samples/yoga/res/values/styles.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/android-support/BUCK b/lib/android-support/BUCK
new file mode 100644
index 00000000..64a3434f
--- /dev/null
+++ b/lib/android-support/BUCK
@@ -0,0 +1,14 @@
+# Copyright (c) 2014-present, Facebook, Inc.
+# All rights reserved.
+#
+# This source code is licensed under the BSD-style license found in the
+# LICENSE file in the root directory of this source tree. An additional grant
+# of patent rights can be found in the PATENTS file in the same directory.
+
+include_defs('//YOGA_DEFS')
+
+prebuilt_jar(
+ name = 'android-support',
+ binary_jar = 'android-support-v4.jar',
+ visibility = [YOGA_ROOT],
+)
diff --git a/lib/android-support/android-support-v4.jar b/lib/android-support/android-support-v4.jar
new file mode 100644
index 00000000..aa0b1a5c
Binary files /dev/null and b/lib/android-support/android-support-v4.jar differ
diff --git a/lib/appcompat/BUCK b/lib/appcompat/BUCK
new file mode 100644
index 00000000..45df9d74
--- /dev/null
+++ b/lib/appcompat/BUCK
@@ -0,0 +1,14 @@
+# Copyright (c) 2014-present, Facebook, Inc.
+# All rights reserved.
+#
+# This source code is licensed under the BSD-style license found in the
+# LICENSE file in the root directory of this source tree. An additional grant
+# of patent rights can be found in the PATENTS file in the same directory.
+
+include_defs('//YOGA_DEFS')
+
+android_prebuilt_aar(
+ name = 'appcompat',
+ aar = 'appcompat-v7-19.1.0.aar',
+ visibility = [YOGA_ROOT],
+)
diff --git a/lib/appcompat/appcompat-v7-19.1.0.aar b/lib/appcompat/appcompat-v7-19.1.0.aar
new file mode 100644
index 00000000..bf15013b
Binary files /dev/null and b/lib/appcompat/appcompat-v7-19.1.0.aar differ