From 07def553968f88f65a2a07c3aea8e757e49530dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ramos?= Date: Fri, 13 Mar 2020 21:46:45 -0700 Subject: [PATCH] fbshipit-source-id: da15f69185e724eaf7d4bc78dbc61fcdcb3074d5 --- .circleci/config.yml | 16 +- Libraries/Image/RCTAnimatedImage.m | 2 +- .../main/java/com/facebook/fbreact/specs/BUCK | 1 - .../react/uimanager/UIViewOperationQueue.java | 4 +- .../views/text/ReactTextViewManager.java | 29 +-- .../react/views/text/TextAttributeProps.java | 69 +++--- .../react/views/text/TextLayoutManager.java | 117 ++++++++-- .../textinput/ReactTextInputManager.java | 27 +-- .../fabric/attributedstring/conversions.h | 23 +- ReactCommon/fabric/graphics/rounding.h | 1 - .../fabric/mounting/Differentiator.cpp | 4 +- .../platform/android/TextLayoutManager.cpp | 43 +++- .../uimanager/ComponentDescriptorRegistry.cpp | 6 + .../platform/ios/RCTTurboModuleManager.mm | 9 +- gradlew.bat | 206 +++++++++--------- scripts/bump-oss-version.js | 132 ++++++----- scripts/publish-npm.js | 52 ++++- template/android/gradlew.bat | 206 +++++++++--------- 18 files changed, 556 insertions(+), 391 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d4ee7413b1fe51..ff235c7987c80a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -602,6 +602,10 @@ jobs: # Publishes new version onto npm # Only works on stable branches when a properly tagged commit is pushed publish_npm_package: + parameters: + publish_npm_args: + type: string + default: --nonightly executor: reactnativeandroid steps: - restore_cache_checkout: @@ -615,7 +619,7 @@ jobs: git config --global user.email "react-native-bot@users.noreply.github.com" git config --global user.name "npm Deployment Script" echo "machine github.com login react-native-bot password $GITHUB_TOKEN" > ~/.netrc - - run: node ./scripts/publish-npm.js + - run: node ./scripts/publish-npm.js << parameters.publish_npm_args >> # ------------------------- # JOBS: Nightly @@ -746,3 +750,13 @@ workflows: - master jobs: - nightly_job + + - setup: + name: setup_android + checkout_type: android + executor: reactnativeandroid + + - publish_npm_package: + publish_npm_args: --nightly + requires: + - setup_android diff --git a/Libraries/Image/RCTAnimatedImage.m b/Libraries/Image/RCTAnimatedImage.m index cd15c92a178eee..978fc2e905d7c3 100644 --- a/Libraries/Image/RCTAnimatedImage.m +++ b/Libraries/Image/RCTAnimatedImage.m @@ -107,7 +107,7 @@ - (float)frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; - if (delayTimeUnclampedProp != nil) { + if (delayTimeUnclampedProp != nil && [delayTimeUnclampedProp floatValue] != 0.0f) { frameDuration = [delayTimeUnclampedProp floatValue]; } else { NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK index ae430ff3aa56cd..967cd685626485 100644 --- a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK @@ -10,7 +10,6 @@ rn_android_library( react_native_dep("third-party/java/jsr-330:jsr-330"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), - ":FBReactNativeSpec-jni", ], exported_deps = [ react_native_target("java/com/facebook/react/turbomodule/core/interfaces:interfaces"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java index a01cdb032a1808..ed5b7955fc32ae 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java @@ -800,8 +800,8 @@ public void run() { long runStartTime = SystemClock.uptimeMillis(); // All ViewCommands should be executed first as a perf optimization - if (mViewCommandOperations != null) { - for (UIOperation viewCommandOp : mViewCommandOperations) { + if (viewCommandOperations != null) { + for (UIOperation viewCommandOp : viewCommandOperations) { viewCommandOp.execute(); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java index a5472c4339f6c5..c6d739a586f03e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java @@ -8,7 +8,6 @@ package com.facebook.react.views.text; import android.content.Context; -import android.text.Layout; import android.text.Spannable; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableMap; @@ -95,10 +94,7 @@ public Object updateState( view.setSpanned(spanned); int textBreakStrategy = - getTextBreakStrategy(paragraphAttributes.getString("textBreakStrategy")); - - // TODO add justificationMode prop into local Data - int justificationMode = Layout.JUSTIFICATION_MODE_NONE; + TextAttributeProps.getTextBreakStrategy(paragraphAttributes.getString("textBreakStrategy")); return new ReactTextUpdate( spanned, @@ -106,25 +102,7 @@ public Object updateState( false, // TODO add this into local Data TextAttributeProps.getTextAlignment(props), textBreakStrategy, - justificationMode); - } - - private int getTextBreakStrategy(@Nullable String textBreakStrategy) { - int androidTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; - if (textBreakStrategy != null) { - switch (textBreakStrategy) { - case "simple": - androidTextBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; - break; - case "balanced": - androidTextBreakStrategy = Layout.BREAK_STRATEGY_BALANCED; - break; - default: - androidTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; - break; - } - } - return androidTextBreakStrategy; + TextAttributeProps.getJustificationMode(props)); } @Override @@ -154,7 +132,8 @@ public long measure( widthMode, height, heightMode, - mReactTextViewManagerCallback); + mReactTextViewManagerCallback, + attachmentsPositions); } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java index dbfea6bce1c51f..2562f450ea578f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java @@ -37,10 +37,12 @@ public class TextAttributeProps { private static final String PROP_TEXT_TRANSFORM = "textTransform"; private static final int DEFAULT_TEXT_SHADOW_COLOR = 0x55000000; + private static final int DEFAULT_JUSTIFICATION_MODE = + (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) ? 0 : Layout.JUSTIFICATION_MODE_NONE; + + private static final int DEFAULT_BREAK_STRATEGY = + (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) ? 0 : Layout.BREAK_STRATEGY_HIGH_QUALITY; - protected boolean mIsAttachment = false; - protected float mAttachmentWidth = Float.NaN; - protected float mAttachmentHeight = Float.NaN; protected float mLineHeight = Float.NaN; protected boolean mIsColorSet = false; protected boolean mAllowFontScaling = true; @@ -54,10 +56,7 @@ public class TextAttributeProps { protected float mLineHeightInput = UNSET; protected float mLetterSpacingInput = Float.NaN; protected int mTextAlign = Gravity.NO_GRAVITY; - protected int mTextBreakStrategy = - (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) ? 0 : Layout.BREAK_STRATEGY_HIGH_QUALITY; - protected int mJustificationMode = - (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) ? 0 : Layout.JUSTIFICATION_MODE_NONE; + protected TextTransform mTextTransform = TextTransform.UNSET; protected float mTextShadowOffsetDx = 0; @@ -126,14 +125,10 @@ public TextAttributeProps(ReactStylesDiffMap props) { setFontVariant(getArrayProp(ViewProps.FONT_VARIANT)); setIncludeFontPadding(getBooleanProp(ViewProps.INCLUDE_FONT_PADDING, true)); setTextDecorationLine(getStringProp(ViewProps.TEXT_DECORATION_LINE)); - setTextBreakStrategy(getStringProp(ViewProps.TEXT_BREAK_STRATEGY)); setTextShadowOffset(props.hasKey(PROP_SHADOW_OFFSET) ? props.getMap(PROP_SHADOW_OFFSET) : null); setTextShadowRadius(getIntProp(PROP_SHADOW_RADIUS, 1)); setTextShadowColor(getIntProp(PROP_SHADOW_COLOR, DEFAULT_TEXT_SHADOW_COLOR)); setTextTransform(getStringProp(PROP_TEXT_TRANSFORM)); - setAttachmentHeight(getFloatProp(ViewProps.HEIGHT, UNSET)); - setAttachmentWidth(getFloatProp(ViewProps.WIDTH, UNSET)); - setIsAttachment(getBooleanProp(ViewProps.IS_ATTACHMENT, false)); } // TODO T63645393 add support for RTL @@ -161,16 +156,15 @@ public static int getTextAlignment(ReactStylesDiffMap props) { return textAlignment; } - private void setIsAttachment(boolean isAttachment) { - mIsAttachment = isAttachment; - } - - private void setAttachmentWidth(float attachmentWidth) { - mAttachmentWidth = attachmentWidth; - } + public static int getJustificationMode(ReactStylesDiffMap props) { + @Nullable + String textAlignPropValue = + props.hasKey(ViewProps.TEXT_ALIGN) ? props.getString(ViewProps.TEXT_ALIGN) : null; - private void setAttachmentHeight(float attachmentHeight) { - mAttachmentHeight = attachmentHeight; + if ("justify".equals(textAlignPropValue) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return Layout.JUSTIFICATION_MODE_INTER_WORD; + } + return DEFAULT_JUSTIFICATION_MODE; } private boolean getBooleanProp(String name, boolean defaultValue) { @@ -357,23 +351,6 @@ public void setTextDecorationLine(@Nullable String textDecorationLineString) { } } - public void setTextBreakStrategy(@Nullable String textBreakStrategy) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - return; - } - - if (textBreakStrategy == null || "highQuality".equals(textBreakStrategy)) { - mTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; - } else if ("simple".equals(textBreakStrategy)) { - mTextBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; - } else if ("balanced".equals(textBreakStrategy)) { - mTextBreakStrategy = Layout.BREAK_STRATEGY_BALANCED; - } else { - throw new JSApplicationIllegalArgumentException( - "Invalid textBreakStrategy: " + textBreakStrategy); - } - } - public void setTextShadowOffset(ReadableMap offsetMap) { mTextShadowOffsetDx = 0; mTextShadowOffsetDy = 0; @@ -418,6 +395,24 @@ public void setTextTransform(@Nullable String textTransform) { } } + public static int getTextBreakStrategy(@Nullable String textBreakStrategy) { + int androidTextBreakStrategy = DEFAULT_BREAK_STRATEGY; + if (textBreakStrategy != null) { + switch (textBreakStrategy) { + case "simple": + androidTextBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; + break; + case "balanced": + androidTextBreakStrategy = Layout.BREAK_STRATEGY_BALANCED; + break; + default: + androidTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; + break; + } + } + return androidTextBreakStrategy; + } + /** * Return -1 if the input string is not a valid numeric fontWeight (100, 200, ..., 900), otherwise * return the weight. diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java index 78a42967b958c1..93aaf3d4866466 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java @@ -24,6 +24,7 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactStylesDiffMap; +import com.facebook.react.uimanager.ViewProps; import com.facebook.yoga.YogaConstants; import com.facebook.yoga.YogaMeasureMode; import com.facebook.yoga.YogaMeasureOutput; @@ -41,6 +42,8 @@ public class TextLayoutManager { // Specifies the amount of spannable that are stored into the {@link sSpannableCache}. private static final int spannableCacheSize = 100; + private static final String INLINE_VIEW_PLACEHOLDER = "0"; + private static final Object sSpannableCacheLock = new Object(); private static LruCache sSpannableCache = new LruCache<>(spannableCacheSize); @@ -60,10 +63,18 @@ private static void buildSpannableFromFragment( sb.append(TextTransform.apply(fragment.getString("string"), textAttributes.mTextTransform)); - // TODO: add support for TextInlineImage and BaseText - int end = sb.length(); - if (end >= start) { + int reactTag = fragment.getInt("reactTag"); + if (fragment.hasKey(ViewProps.IS_ATTACHMENT) + && fragment.getBoolean(ViewProps.IS_ATTACHMENT)) { + float width = PixelUtil.toPixelFromSP(fragment.getDouble(ViewProps.WIDTH)); + float height = PixelUtil.toPixelFromSP(fragment.getDouble(ViewProps.HEIGHT)); + ops.add( + new SetSpanOperation( + sb.length() - INLINE_VIEW_PLACEHOLDER.length(), + sb.length(), + new TextInlineViewPlaceholderSpan(reactTag, (int) width, (int) height))); + } else if (end >= start) { if (textAttributes.mIsColorSet) { ops.add( new SetSpanOperation( @@ -120,7 +131,6 @@ private static void buildSpannableFromFragment( start, end, new CustomLineHeightSpan(textAttributes.getEffectiveLineHeight()))); } - int reactTag = fragment.getInt("reactTag"); ops.add(new SetSpanOperation(start, end, new ReactTagSpan(reactTag))); } } @@ -189,15 +199,16 @@ public static long measureText( YogaMeasureMode widthYogaMeasureMode, float height, YogaMeasureMode heightYogaMeasureMode, - ReactTextViewManagerCallback reactTextViewManagerCallback) { + ReactTextViewManagerCallback reactTextViewManagerCallback, + @Nullable int[] attachmentsPositions) { // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) TextPaint textPaint = sTextPaintInstance; Spannable preparedSpannableText = getOrCreateSpannableForText(context, attributedString, reactTextViewManagerCallback); - // TODO add these props to paragraph attributes - int textBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; + int textBreakStrategy = + TextAttributeProps.getTextBreakStrategy(paragraphAttributes.getString("textBreakStrategy")); boolean includeFontPadding = true; if (preparedSpannableText == null) { @@ -211,6 +222,7 @@ public static long measureText( boolean unconstrainedWidth = widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || width < 0; Layout layout; + int spanLength = text.length(); if (boring == null && (unconstrainedWidth || (!YogaConstants.isUndefined(desiredWidth) && desiredWidth <= width))) { @@ -230,7 +242,7 @@ public static long measureText( includeFontPadding); } else { layout = - StaticLayout.Builder.obtain(text, 0, text.length(), textPaint, hintWidth) + StaticLayout.Builder.obtain(text, 0, spanLength, textPaint, hintWidth) .setAlignment(Layout.Alignment.ALIGN_NORMAL) .setLineSpacing(0.f, 1.f) .setIncludePad(includeFontPadding) @@ -267,7 +279,7 @@ public static long measureText( includeFontPadding); } else { layout = - StaticLayout.Builder.obtain(text, 0, text.length(), textPaint, (int) width) + StaticLayout.Builder.obtain(text, 0, spanLength, textPaint, (int) width) .setAlignment(Layout.Alignment.ALIGN_NORMAL) .setLineSpacing(0.f, 1.f) .setIncludePad(includeFontPadding) @@ -282,16 +294,95 @@ public static long measureText( ? paragraphAttributes.getInt("maximumNumberOfLines") : UNSET; - width = layout.getWidth(); + int calculatedWidth = layout.getWidth(); + int calculatedHeight; if (maximumNumberOfLines != UNSET && maximumNumberOfLines != 0 && maximumNumberOfLines < layout.getLineCount()) { - height = layout.getLineBottom(maximumNumberOfLines - 1); + calculatedHeight = layout.getLineBottom(maximumNumberOfLines - 1); } else { - height = layout.getHeight(); + calculatedHeight = layout.getHeight(); + } + + // Calculate the positions of the attachments (views) that will be rendered inside the Spanned + // Text. The following logic is only executed when a text contains views inside. This + // follows a similar logic than used in pre-fabric (see ReactTextView.onLayout method). + int attachmentIndex = 0; + int lastAttachmentFoundInSpan; + for (int i = 0; i < spanLength; i = lastAttachmentFoundInSpan) { + lastAttachmentFoundInSpan = + text.nextSpanTransition(i, spanLength, TextInlineViewPlaceholderSpan.class); + TextInlineViewPlaceholderSpan[] placeholders = + text.getSpans(i, lastAttachmentFoundInSpan, TextInlineViewPlaceholderSpan.class); + for (TextInlineViewPlaceholderSpan placeholder : placeholders) { + int start = text.getSpanStart(placeholder); + int line = layout.getLineForOffset(start); + boolean isLineTruncated = layout.getEllipsisCount(line) > 0; + // This truncation check works well on recent versions of Android (tested on 5.1.1 and + // 6.0.1) but not on Android 4.4.4. The reason is that getEllipsisCount is buggy on + // Android 4.4.4. Specifically, it incorrectly returns 0 if an inline view is the first + // thing to be truncated. + if (!(isLineTruncated && start >= layout.getLineStart(line) + layout.getEllipsisStart(line)) + || start >= layout.getLineEnd(line)) { + int placeholderWidth = placeholder.getWidth(); + int placeholderHeight = placeholder.getHeight(); + // Calculate if the direction of the placeholder character is Right-To-Left. + boolean isRtlChar = layout.isRtlCharAt(start); + boolean isRtlParagraph = layout.getParagraphDirection(line) == Layout.DIR_RIGHT_TO_LEFT; + int placeholderLeftPosition; + // There's a bug on Samsung devices where calling getPrimaryHorizontal on + // the last offset in the layout will result in an endless loop. Work around + // this bug by avoiding getPrimaryHorizontal in that case. + if (start == spanLength - 1) { + placeholderLeftPosition = + isRtlParagraph + // Equivalent to `layout.getLineLeft(line)` but `getLineLeft` returns incorrect + // values when the paragraph is RTL and `setSingleLine(true)`. + ? calculatedWidth - (int) layout.getLineWidth(line) + : (int) layout.getLineRight(line) - placeholderWidth; + } else { + // The direction of the paragraph may not be exactly the direction the string is heading + // in at the + // position of the placeholder. So, if the direction of the character is the same as the + // paragraph + // use primary, secondary otherwise. + boolean characterAndParagraphDirectionMatch = isRtlParagraph == isRtlChar; + placeholderLeftPosition = + characterAndParagraphDirectionMatch + ? (int) layout.getPrimaryHorizontal(start) + : (int) layout.getSecondaryHorizontal(start); + if (isRtlParagraph) { + // Adjust `placeholderLeftPosition` to work around an Android bug. + // The bug is when the paragraph is RTL and `setSingleLine(true)`, some layout + // methods such as `getPrimaryHorizontal`, `getSecondaryHorizontal`, and + // `getLineRight` return incorrect values. Their return values seem to be off + // by the same number of pixels so subtracting these values cancels out the error. + // + // The result is equivalent to bugless versions of + // `getPrimaryHorizontal`/`getSecondaryHorizontal`. + placeholderLeftPosition = + calculatedWidth - ((int) layout.getLineRight(line) - placeholderLeftPosition); + } + if (isRtlChar) { + placeholderLeftPosition -= placeholderWidth; + } + } + // Vertically align the inline view to the baseline of the line of text. + int placeholderTopPosition = layout.getLineBaseline(line) - placeholderHeight; + int attachmentPosition = attachmentIndex * 2; + + // The attachment array returns the positions of each of the attachments as + attachmentsPositions[attachmentPosition] = + (int) PixelUtil.toSPFromPixel(placeholderTopPosition); + attachmentsPositions[attachmentPosition + 1] = + (int) PixelUtil.toSPFromPixel(placeholderLeftPosition); + attachmentIndex++; + } + } } - return YogaMeasureOutput.make(PixelUtil.toSPFromPixel(width), PixelUtil.toSPFromPixel(height)); + return YogaMeasureOutput.make( + PixelUtil.toSPFromPixel(calculatedWidth), PixelUtil.toSPFromPixel(calculatedHeight)); } // TODO T31905686: This class should be private diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index 9708023996444e..7d111f7ecd8c30 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -1206,26 +1206,6 @@ public void setPadding(ReactEditText view, int left, int top, int right, int bot view.setPadding(left, top, right, bottom); } - // This is copied from ReactTextViewManager - // TODO: this should be from a common class or a static method somewhere - private int getTextBreakStrategy(@Nullable String textBreakStrategy) { - int androidTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; - if (textBreakStrategy != null) { - switch (textBreakStrategy) { - case "simple": - androidTextBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; - break; - case "balanced": - androidTextBreakStrategy = Layout.BREAK_STRATEGY_BALANCED; - break; - default: - androidTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; - break; - } - } - return androidTextBreakStrategy; - } - /** * May be overriden by subclasses that would like to provide their own instance of the internal * {@code EditText} this class uses to determine the expected size of the view. @@ -1281,10 +1261,7 @@ public Object updateState( view.getContext(), attributedString, mReactTextViewManagerCallback); int textBreakStrategy = - getTextBreakStrategy(paragraphAttributes.getString("textBreakStrategy")); - - // TODO add justificationMode prop into local Data - int justificationMode = Layout.JUSTIFICATION_MODE_NONE; + TextAttributeProps.getTextBreakStrategy(paragraphAttributes.getString("textBreakStrategy")); view.mStateWrapper = stateWrapper; @@ -1294,7 +1271,7 @@ public Object updateState( false, // TODO add this into local Data TextAttributeProps.getTextAlignment(props), textBreakStrategy, - justificationMode, + TextAttributeProps.getJustificationMode(props), attributedString); } } diff --git a/ReactCommon/fabric/attributedstring/conversions.h b/ReactCommon/fabric/attributedstring/conversions.h index 70ddd41d392292..e92f06ace4b5ed 100644 --- a/ReactCommon/fabric/attributedstring/conversions.h +++ b/ReactCommon/fabric/attributedstring/conversions.h @@ -484,6 +484,27 @@ inline folly::dynamic toDynamic( return values; } +inline folly::dynamic toDynamic(const FontVariant &fontVariant) { + auto result = folly::dynamic::array(); + if ((int)fontVariant & (int)FontVariant::SmallCaps) { + result.push_back("small-caps"); + } + if ((int)fontVariant & (int)FontVariant::OldstyleNums) { + result.push_back("oldstyle-nums"); + } + if ((int)fontVariant & (int)FontVariant::LiningNums) { + result.push_back("lining-nums"); + } + if ((int)fontVariant & (int)FontVariant::TabularNums) { + result.push_back("tabular-nums"); + } + if ((int)fontVariant & (int)FontVariant::ProportionalNums) { + result.push_back("proportional-nums"); + } + + return result; +} + inline folly::dynamic toDynamic(const TextAttributes &textAttributes) { auto _textAttributes = folly::dynamic::object(); if (textAttributes.foregroundColor) { @@ -513,7 +534,7 @@ inline folly::dynamic toDynamic(const TextAttributes &textAttributes) { _textAttributes("fontStyle", toString(*textAttributes.fontStyle)); } if (textAttributes.fontVariant.has_value()) { - _textAttributes("fontVariant", toString(*textAttributes.fontVariant)); + _textAttributes("fontVariant", toDynamic(*textAttributes.fontVariant)); } if (textAttributes.allowFontScaling.has_value()) { _textAttributes("allowFontScaling", *textAttributes.allowFontScaling); diff --git a/ReactCommon/fabric/graphics/rounding.h b/ReactCommon/fabric/graphics/rounding.h index 2ef95e3721f47e..4d948213be27cd 100644 --- a/ReactCommon/fabric/graphics/rounding.h +++ b/ReactCommon/fabric/graphics/rounding.h @@ -49,7 +49,6 @@ Rect roundToPixel(Rect value, Float scaleFactor) { /* * GCC-based Android NDK does not have rounding functions as part of STL. */ -#include inline float round(float value) noexcept { return ::roundf(value); diff --git a/ReactCommon/fabric/mounting/Differentiator.cpp b/ReactCommon/fabric/mounting/Differentiator.cpp index 2aebb50bb751b4..7b9bf63af1ad51 100644 --- a/ReactCommon/fabric/mounting/Differentiator.cpp +++ b/ReactCommon/fabric/mounting/Differentiator.cpp @@ -119,7 +119,9 @@ static void sliceChildShadowNodeViewPairsRecursively( for (auto const &sharedChildShadowNode : shadowNode.getChildren()) { auto &childShadowNode = *sharedChildShadowNode; auto shadowView = ShadowView(childShadowNode); - shadowView.layoutMetrics.frame.origin += layoutOffset; + if (shadowView.layoutMetrics != EmptyLayoutMetrics) { + shadowView.layoutMetrics.frame.origin += layoutOffset; + } if (childShadowNode.getTraits().check( ShadowNodeTraits::Trait::FormsStackingContext)) { diff --git a/ReactCommon/fabric/textlayoutmanager/platform/android/TextLayoutManager.cpp b/ReactCommon/fabric/textlayoutmanager/platform/android/TextLayoutManager.cpp index c89eefe552f456..5a1aab941997e1 100644 --- a/ReactCommon/fabric/textlayoutmanager/platform/android/TextLayoutManager.cpp +++ b/ReactCommon/fabric/textlayoutmanager/platform/android/TextLayoutManager.cpp @@ -43,6 +43,15 @@ TextMeasurement TextLayoutManager::doMeasure( const jni::global_ref &fabricUIManager = contextContainer_->at>("FabricUIManager"); + int attachmentsCount = 0; + for (auto fragment : attributedString.getFragments()) { + if (fragment.isAttachment()) { + attachmentsCount++; + } + } + auto env = Environment::current(); + auto attachmentPositions = env->NewIntArray(attachmentsCount * 2); + static auto measure = jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") ->getMethod("measure"); + jfloat, + jintArray)>("measure"); auto minimumSize = layoutConstraints.minimumSize; auto maximumSize = layoutConstraints.maximumSize; + auto serializedAttributedString = toDynamic(attributedString); local_ref componentName = make_jstring("RCTText"); local_ref attributedStringRNM = - ReadableNativeMap::newObjectCxxArgs(toDynamic(attributedString)); + ReadableNativeMap::newObjectCxxArgs(serializedAttributedString); local_ref paragraphAttributesRNM = ReadableNativeMap::newObjectCxxArgs(toDynamic(paragraphAttributes)); @@ -79,9 +90,33 @@ TextMeasurement TextLayoutManager::doMeasure( minimumSize.width, maximumSize.width, minimumSize.height, - maximumSize.height)); + maximumSize.height, + attachmentPositions)); + + jint *attachmentData = env->GetIntArrayElements(attachmentPositions, 0); + + auto attachments = TextMeasurement::Attachments{}; + if (attachmentsCount > 0) { + folly::dynamic fragments = serializedAttributedString["fragments"]; + int attachmentIndex = 0; + for (int i = 0; i < fragments.size(); i++) { + folly::dynamic fragment = fragments[i]; + if (fragment["isAttachment"] == true) { + float top = attachmentData[attachmentIndex * 2]; + float left = attachmentData[attachmentIndex * 2 + 1]; + float width = fragment["width"].getInt(); + float height = fragment["height"].getInt(); - return TextMeasurement{size, {}}; + auto rect = facebook::react::Rect{{left, top}, + facebook::react::Size{width, height}}; + attachments.push_back(TextMeasurement::Attachment{rect, false}); + attachmentIndex++; + } + } + } + // DELETE REF + env->DeleteLocalRef(attachmentPositions); + return TextMeasurement{size, attachments}; } } // namespace react diff --git a/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.cpp b/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.cpp index 851dfe3dee8dd3..778f32fbde30eb 100644 --- a/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.cpp +++ b/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.cpp @@ -66,6 +66,12 @@ static std::string componentNameByReactViewName(std::string viewName) { if (viewName == "Text") { return "Paragraph"; } + + // TODO T63839307: remove this condition after deleting TextInlineImage from + // Paper + if (viewName == "TextInlineImage") { + return "Image"; + } if (viewName == "VirtualText") { return "Text"; } diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm index c9bf5caeb68374..1786815f782b00 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm @@ -429,10 +429,11 @@ - (void)installJSBindingWithRuntime:(jsi::Runtime *)runtime /** * TODO(T63718299): Move this setter into the ObjCTurboModule constructor */ - if (std::shared_ptr objCTurboModule = - std::dynamic_pointer_cast(turboModule)) { - objCTurboModule->setRCTTurboModulePerformanceLogger(performanceLogger); - }; + if (performanceLogger) { + if (auto objCTurboModule = std::dynamic_pointer_cast(turboModule)) { + objCTurboModule->setRCTTurboModulePerformanceLogger(performanceLogger); + }; + } [performanceLogger createTurboModuleEnd:moduleName]; diff --git a/gradlew.bat b/gradlew.bat index 9109989e3cbf66..62bd9b9ccefea2 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,103 +1,103 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/scripts/bump-oss-version.js b/scripts/bump-oss-version.js index cf924e49ffe2f9..7b8af9a97e0084 100755 --- a/scripts/bump-oss-version.js +++ b/scripts/bump-oss-version.js @@ -23,29 +23,42 @@ const yargs = require('yargs'); let argv = yargs.option('r', { alias: 'remote', default: 'origin', +}) +.option('n', { + alias: 'nightly', + type: 'boolean', + default: false, }).argv; -// Check we are in release branch, e.g. 0.33-stable -let branch = exec('git symbolic-ref --short HEAD', { - silent: true, -}).stdout.trim(); - -if (branch.indexOf('-stable') === -1) { - echo('You must be in 0.XX-stable branch to bump a version'); - exit(1); -} - -// e.g. 0.33 -let versionMajor = branch.slice(0, branch.indexOf('-stable')); - -// - check that argument version matches branch -// e.g. 0.33.1 or 0.33.0-rc4 -let version = argv._[0]; -if (!version || version.indexOf(versionMajor) !== 0) { - echo( - `You must pass a tag like 0.${versionMajor}.[X]-rc[Y] to bump a version`, - ); - exit(1); +const nightlyBuild = argv.nightly; + +let version, branch; +if (nightlyBuild) { + const currentCommit = exec('git rev-parse HEAD', {silent: true}).stdout.trim(); + version = `0.0.0-${currentCommit.slice(0, 9)}`; +} else { + // Check we are in release branch, e.g. 0.33-stable + branch = exec('git symbolic-ref --short HEAD', { + silent: true, + }).stdout.trim(); + + if (branch.indexOf('-stable') === -1) { + echo('You must be in 0.XX-stable branch to bump a version'); + exit(1); + } + + // e.g. 0.33 + let versionMajor = branch.slice(0, branch.indexOf('-stable')); + + // - check that argument version matches branch + // e.g. 0.33.1 or 0.33.0-rc4 + version = argv._[0]; + if (!version || version.indexOf(versionMajor) !== 0) { + echo( + `You must pass a tag like 0.${versionMajor}.[X]-rc[Y] to bump a version`, + ); + exit(1); + } } // Generate version files to detect mismatches between JS and native. @@ -135,43 +148,48 @@ let numberOfChangedLinesWithNewVersion = exec( `git diff -U0 | grep '^[+]' | grep -c ${version} `, {silent: true}, ).stdout.trim(); -if (+numberOfChangedLinesWithNewVersion !== 3) { - echo( - 'Failed to update all the files. package.json and gradle.properties must have versions in them', - ); - echo('Fix the issue, revert and try again'); - exec('git diff'); - exit(1); -} -// Make commit [0.21.0-rc] Bump version numbers -if (exec(`git commit -a -m "[${version}] Bump version numbers"`).code) { - echo('failed to commit'); - exit(1); +// Release builds should commit the version bumps, and create tags. +// Nightly builds do not need to do that. +if (!nightlyBuild) { + if (+numberOfChangedLinesWithNewVersion !== 3) { + echo( + 'Failed to update all the files. package.json and gradle.properties must have versions in them', + ); + echo('Fix the issue, revert and try again'); + exec('git diff'); + exit(1); + } + + // Make commit [0.21.0-rc] Bump version numbers + if (exec(`git commit -a -m "[${version}] Bump version numbers"`).code) { + echo('failed to commit'); + exit(1); + } + + // Add tag v0.21.0-rc + if (exec(`git tag v${version}`).code) { + echo( + `failed to tag the commit with v${version}, are you sure this release wasn't made earlier?`, + ); + echo('You may want to rollback the last commit'); + echo('git reset --hard HEAD~1'); + exit(1); + } + + // Push newly created tag + let remote = argv.remote; + exec(`git push ${remote} v${version}`); + + // Tag latest if doing stable release + if (version.indexOf('rc') === -1) { + exec('git tag -d latest'); + exec(`git push ${remote} :latest`); + exec('git tag latest'); + exec(`git push ${remote} latest`); + } + + exec(`git push ${remote} ${branch} --follow-tags`); } -// Add tag v0.21.0-rc -if (exec(`git tag v${version}`).code) { - echo( - `failed to tag the commit with v${version}, are you sure this release wasn't made earlier?`, - ); - echo('You may want to rollback the last commit'); - echo('git reset --hard HEAD~1'); - exit(1); -} - -// Push newly created tag -let remote = argv.remote; -exec(`git push ${remote} v${version}`); - -// Tag latest if doing stable release -if (version.indexOf('rc') === -1) { - exec('git tag -d latest'); - exec(`git push ${remote} :latest`); - exec('git tag latest'); - exec(`git push ${remote} latest`); -} - -exec(`git push ${remote} ${branch} --follow-tags`); - exit(0); diff --git a/scripts/publish-npm.js b/scripts/publish-npm.js index 55f35e6b905ebb..6107a9efd2a1a6 100644 --- a/scripts/publish-npm.js +++ b/scripts/publish-npm.js @@ -51,22 +51,35 @@ /*eslint-disable no-undef */ require('shelljs/global'); +const yargs = require('yargs'); +let argv = yargs.option('n', { + alias: 'nightly', + type: 'boolean', + default: false, +}).argv; + +const nightlyBuild = argv.nightly; const buildTag = process.env.CIRCLE_TAG; const otp = process.env.NPM_CONFIG_OTP; -if (!buildTag) { - echo('Error: We publish only from git tags'); - exit(1); -} +let branchVersion = 0; +if (nightlyBuild) { + branchVersion = 0; +} else { + if (!buildTag) { + echo('Error: We publish only from git tags'); + exit(1); + } -let match = buildTag.match(/^v(\d+\.\d+)\.\d+(?:-.+)?$/); -if (!match) { - echo('Error: We publish only from release version git tags'); - exit(1); + let match = buildTag.match(/^v(\d+\.\d+)\.\d+(?:-.+)?$/); + if (!match) { + echo('Error: We publish only from release version git tags'); + exit(1); + } + [, branchVersion] = match; } // 0.33 -let [, branchVersion] = match; // 34c034298dc9cad5a4553964a5a324450fda0385 const currentCommit = exec('git rev-parse HEAD', {silent: true}).stdout.trim(); @@ -85,14 +98,25 @@ const tagsWithVersion = exec(`git ls-remote origin | grep ${currentCommit}`, { // ['v0.33.0', 'v0.33.0-rc', 'v0.33.0-rc1', 'v0.33.0-rc2'] .map(version => version.slice('refs/tags/'.length)); -if (tagsWithVersion.length === 0) { +if (!nightlyBuild && tagsWithVersion.length === 0) { echo( 'Error: Cannot find version tag in current commit. To deploy to NPM you must add tag v0.XY.Z[-rc] to your commit', ); exit(1); } let releaseVersion; -if (tagsWithVersion[0].indexOf('-rc') === -1) { + +if (nightlyBuild) { + releaseVersion = `0.0.0-${currentCommit.slice(0, 9)}`; + + // Bump version number in various files (package.json, gradle.properties etc) + if ( + exec(`node scripts/bump-oss-version.js --nightly ${releaseVersion}`).code + ) { + echo('Failed to bump version number'); + exit(1); + } +} else if (tagsWithVersion[0].indexOf('-rc') === -1) { // if first tag on this commit is non -rc then we are making a stable release // '0.33.0' releaseVersion = tagsWithVersion[0].slice(1); @@ -130,7 +154,11 @@ artifacts.forEach(name => { }); // if version contains -rc, tag as prerelease -const tagFlag = releaseVersion.indexOf('-rc') === -1 ? '' : '--tag next'; +const tagFlag = nightlyBuild + ? '--tag nightly' + : releaseVersion.indexOf('-rc') === -1 + ? '' + : '--tag next'; // use otp from envvars if available const otpFlag = otp ? `--otp ${otp}` : ''; diff --git a/template/android/gradlew.bat b/template/android/gradlew.bat index 9109989e3cbf66..62bd9b9ccefea2 100644 --- a/template/android/gradlew.bat +++ b/template/android/gradlew.bat @@ -1,103 +1,103 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega