diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cba8ae6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.gradle +.DS_Store +.idea +build/ +local.properties +localhost/ +obj/ +*.iml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..45a0ba9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing to TextLayoutBuilder +We want to make contributing to this project as easy and transparent as +possible. + + +## Issues +We use GitHub issues to track public bugs. + +When you report an issue the more information the better. Here are some things that will help you get an answer faster: + +- A *title* as well as a body for the issue. +- A screenshot or video of the problem. +- Logcat output, if your app is crashing. +- A snippet of the code in question +- Place code in blocks so that it reads like code: + +``` +```java (or xml) +your code here +```(terminating backticks) +``` + +#### Security bugs + +Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe +disclosure of security bugs. In those cases, please go through the process +outlined on that page and do not file a public issue. + +## Pull Requests +We actively welcome your pull requests. + +1. Fork the repo and create your branch from `master`. +2. If you've added code that should be tested, add tests +3. If you've changed APIs, update the documentation. +4. Ensure the test suite passes. +5. Make sure your code lints. +6. If you haven't already, complete the Contributor License Agreement ("CLA"). + +## Contributor License Agreement ("CLA") +In order to accept your pull request, we need you to submit a CLA. You only need +to do this once to work on any of Facebook's open source projects. + +Complete your CLA here: . + +## Our Development Process +Each pull request is first submitted into Facebook's internal repositories by a +Facebook team member. Once the commit has successfully passed Facebook's internal +test suite, it will be exported back out from Facebook's repository. We endeavour +to do this as soon as possible for all commits. + +## Coding Style +* 2 spaces for indentation rather than tabs +* 100 character line length +* Although officially archived, we still follow the practice of Oracle's +[Coding Conventions for the Java Programming Language](http://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-136057.html). + +## License +By contributing to TextLayoutBuilder, you agree that your contributions will be licensed +under its BSD license. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2b70c95 --- /dev/null +++ b/LICENSE @@ -0,0 +1,30 @@ +BSD License + +For TextLayoutBuilder software + +Copyright (c) 2016-present, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/PATENTS b/PATENTS new file mode 100644 index 0000000..14ccaca --- /dev/null +++ b/PATENTS @@ -0,0 +1,33 @@ +Additional Grant of Patent Rights Version 2 + +"Software" means the TextLayoutBuilder software contributed by Facebook, Inc. + +Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software +("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable +(subject to the termination provision below) license under any Necessary +Claims, to make, have made, use, sell, offer to sell, import, and otherwise +transfer the Software. For avoidance of doubt, no license is granted under +Facebook’s rights in any patent claims that are infringed by (i) modifications +to the Software made by you or any third party or (ii) the Software in +combination with any software or other technology. + +The license granted hereunder will terminate, automatically and without notice, +if you (or any of your subsidiaries, corporate affiliates or agents) initiate +directly or indirectly, or take a direct financial interest in, any Patent +Assertion: (i) against Facebook or any of its subsidiaries or corporate +affiliates, (ii) against any party if such Patent Assertion arises in whole or +in part from any software, technology, product or service of Facebook or any of +its subsidiaries or corporate affiliates, or (iii) against any party relating +to the Software. Notwithstanding the foregoing, if Facebook or any of its +subsidiaries or corporate affiliates files a lawsuit alleging patent +infringement against you in the first instance, and you respond by filing a +patent infringement counterclaim in that lawsuit against that party that is +unrelated to the Software, the license granted hereunder will not terminate +under section (i) of this paragraph due to such counterclaim. + +A "Necessary Claim" is a claim of a patent owned by Facebook that is +necessarily infringed by the Software standing alone. + +A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, +or contributory infringement or inducement to infringe any patent, including a +cross-claim or counterclaim. diff --git a/README.md b/README.md new file mode 100644 index 0000000..14037c6 --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +TextLayoutBuilder +================= +Build text [Layout](https://developer.android.com/reference/android/text/Layout.html)s easily on Android. + +![TextLayoutBuilder logo](./docs/logo.png) + +Features +-------- +- Create text `Layout`s easily. +- Reuse builders to create similarly styled `Layout`s. +- Cache `Layout`s of commonly used strings. +- Improve performance using glyph warming. + +Download +-------- +If using Gradle, add this to your `build.gradle`: + +```groovy +compile 'com.facebook.fbui.textlayoutbuilder:textlayoutbuilder:1.0.0' +``` + +or, if using Maven: + +```xml + + com.facebook.fbui.textlayoutbuilder + textlayoutbuilder + 1.0.0 + aar + +``` + +Usage +----- +1. Set the properties on the `TextLayoutBuilder`: + ```java + TextLayoutBuilder builder = new TextLayoutBuilder() + .setTextAppearance(context, resId) + .setText("TextLayoutBuilder makes life easy") + .setWidth(400); + ``` + +2. Call `build()` on the builder to get a `Layout`: + ```java + Layout layout = builder.build(); + ``` + +3. Use the `Layout` in your code: + ```java + public class CustomView extends View { + private Layout mLayout; + + public CustomView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setLayout(Layout layout) { + mLayout = layout; + } + + @Override + protected void onDraw(Canvas canvas) { + super.draw(canvas); + + // Draw the layout. + mLayout.draw(canvas); + } + } + ``` + +Additional Usage +---------------- +1. Cache the layouts for commonly used strings by turning on caching in the `TextLayoutBuilder`. + ```java + mTextLayoutBuilder.setShouldCacheLayout(true); + ``` + +2. Glyph warming provides significant performance boost for large blurbs of text. +Turn this on and pass in a `GlyphWarmer` for the `TextLayoutBuilder`. + ```java + mTextLayoutBuilder + .setShouldWarmText(true) + .setGlyphWarmer(new GlyphWarmerImpl()); + ``` + +3. Import a style defined in XML into a `TextLayoutBuilder` object. + ```java + ResourceTextLayoutHelper.updateFromStyleResource( + mTextLayoutBuilder, // builder object + mContext, // Activity context + resId); // style resource id + ``` diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..187adf0 --- /dev/null +++ b/build.gradle @@ -0,0 +1,26 @@ +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.1.3' + } +} + +allprojects { + repositories { + jcenter() + } +} + +ext { + minSdkVersion = 9 + targetSdkVersion = 24 + compileSdkVersion = 24 + buildToolsVersion = '24.0.2' + + junitVersion = '4.12' + mockitoCoreVersion = '1.10.19' + robolectricVersion = '3.0' + supportLibVersion = '24.2.1' +} diff --git a/docs/api.html b/docs/api.html new file mode 100644 index 0000000..b55aa83 --- /dev/null +++ b/docs/api.html @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + TextLayoutBuilder - API + + + + + + + + +
+
+ + +

TextLayoutBuilder

+
+ +
+ + +
+
+ + +
+ +
+ + +
+ +
+ + + + + diff --git a/docs/favicon.png b/docs/favicon.png new file mode 100644 index 0000000..002a3ef Binary files /dev/null and b/docs/favicon.png differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..73834d3 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,248 @@ + + + + + + + + + + + + + + + + + + TextLayoutBuilder + + + + + + + +
+
+ + +

TextLayoutBuilder

+
+ +
+ + +
+
+ + +
+
+
+

Build text layouts easily on Android!

+
+
+

TextLayoutBuilder uses a builder pattern to configure the properties + required to create a Layout. The methods on this builder class are + similar to TextView's. On calling build(), the + TextLayoutBuilder creates a text Layout based on + the properties set on it.

+
    +
  • Create text layouts easily.
  • +
  • Reuse builders to create similarly styled layouts.
  • +
  • Cache layouts of commonly used strings.
  • +
  • Improve performance with glyph warming.
  • +
+
+
+
+Layout layout = new TextLayoutBuilder()
+    .setTextAppearance(context, resId)
+    .setText("TextLayoutBuilder makes life easy")
+    .setWidth(400)
+    .build();
+
+
+
+
+ +
+
+
+ + +
+
+
+

Features

+
+
+

Builder

+

TextLayoutBuilder uses a builder pattern to configure the properties + for the Layout. Gone are those days of trying to set properties on + a StaticLayout.

+
+
+

Caching

+

Same properties will return same Layout on subsequent build() + calls. This reduces allocation on commonly used texts.

+
+
+

Glyph Warming

+

On 4.0+ devices, Android uses a texture cache to warm up the glyphs. By drawing these + glyphs on a background thread onto a Picture, TextLayoutBuilder warms + these glyphs and can help them render at 0ms.

+
+
+
+
+
+ + +
+
+
+

Download

+
    +
  • + If using Gradle, add this to your build.gradle: +
    compile 'com.facebook.fbui.textlayoutbuilder:textlayoutbuilder:1.0.0'
    +
  • +
  • + or, if using Maven: +
    +<dependency>
    +    <groupId>com.facebook.fbui.textlayoutbuilder</groupId>
    +    <artifactId>textlayoutbuilder</artifactId>
    +    <version>1.0.0</version>
    +    <typen>aar</type>
    +</dependency>
    +
  • +
+
+
+
+ + +
+
+
+

Usage

+
    +
  1. + Set the properties on the TextLayoutBuilder: +
    +TextLayoutBuilder builder = new TextLayoutBuilder()
    +    .setTextAppearance(context, resId)
    +    .setText("TextLayoutBuilder makes life easy")
    +    .setWidth(400);
    +
  2. +
  3. + Call build() on the builder to get a Layout: +
    Layout layout = builder.build();
    +
  4. +
  5. + Use the Layout in your code: +
    layout.draw(canvas);
    +
  6. +
+
+
+
+ + +
+
+
+

Additional Usage

+
    +
  1. + Cache the layouts for commonly used strings by turning on caching in the TextLayoutBuilder. +
    mTextLayoutBuilder.setShouldCacheLayout(true);
    +
  2. +
  3. + Glyph warming provides significant performance boost for large blurbs of text. + Turn this on and pass in a GlyphWarmer for the TextLayoutBuilder. +
    +mTextLayoutBuilder
    +    .setShouldWarmText(true)
    +    .setGlyphWarmer(new GlyphWarmerImpl());
    +
  4. +
  5. + Import a style defined in XML into a TextLayoutBuilder object. +
    +ResourceTextLayoutHelper.updateFromStyleResource(
    +    mTextLayoutBuilder,
    +    mContext,
    +    resId);
    +
  6. +
+
+
+
+ + +
+ +
+ + + + + diff --git a/docs/javadoc/allclasses-frame.html b/docs/javadoc/allclasses-frame.html new file mode 100644 index 0000000..29fefa9 --- /dev/null +++ b/docs/javadoc/allclasses-frame.html @@ -0,0 +1,24 @@ + + + + + +All Classes (TextLayoutBuilder API) + + + + + +

All Classes

+
+ +
+ + diff --git a/docs/javadoc/allclasses-noframe.html b/docs/javadoc/allclasses-noframe.html new file mode 100644 index 0000000..0efdee0 --- /dev/null +++ b/docs/javadoc/allclasses-noframe.html @@ -0,0 +1,24 @@ + + + + + +All Classes (TextLayoutBuilder API) + + + + + +

All Classes

+
+ +
+ + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/GlyphWarmer.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/GlyphWarmer.html new file mode 100644 index 0000000..eda3755 --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/GlyphWarmer.html @@ -0,0 +1,233 @@ + + + + + +GlyphWarmer (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
com.facebook.fbui.textlayoutbuilder
+

Interface GlyphWarmer

+
+
+
+
    +
  • +
    +
    All Known Implementing Classes:
    +
    GlyphWarmerImpl
    +
    +
    +
    +
    public interface GlyphWarmer
    +
    Specifies an interface that a class has to implement + to warm the text Layout in the background. + This approach helps in drawing text in post Android 4.0 devices.
    +
  • +
+
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        warmLayout

        +
        void warmLayout(Layout layout)
        +
        Warms the text layout.
        +
        +
        Parameters:
        +
        layout - The layout
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/ResourceTextLayoutHelper.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/ResourceTextLayoutHelper.html new file mode 100644 index 0000000..885229d --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/ResourceTextLayoutHelper.html @@ -0,0 +1,370 @@ + + + + + +ResourceTextLayoutHelper (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
com.facebook.fbui.textlayoutbuilder
+

Class ResourceTextLayoutHelper

+
+
+ +
+
    +
  • +
    +
    +
    public class ResourceTextLayoutHelper
    +extends Object
    +
    An utility class to update a TextLayoutBuilder from an Android resource.
    +
  • +
+
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        ResourceTextLayoutHelper

        +
        public ResourceTextLayoutHelper()
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        updateFromStyleResource

        +
        public static void updateFromStyleResource(TextLayoutBuilder builder,
        +                                           Context context,
        +                                           int styleRes)
        +
        Sets the values for a TextLayoutBuilder from a style resource.
        +
        +
        Parameters:
        +
        builder - The TextLayoutBuilder
        +
        context - The Context to use for resolving the attributes
        +
        styleRes - The style resource identifier
        +
        +
      • +
      + + + +
        +
      • +

        updateFromStyleResource

        +
        public static void updateFromStyleResource(TextLayoutBuilder builder,
        +                                           Context context,
        +                                           int styleAttr,
        +                                           int styleRes)
        +
        Sets the values for a TextLayoutBuilder from a style resource or a themed attribute.
        +
        +
        Parameters:
        +
        builder - The TextLayoutBuilder
        +
        context - The Context to use for resolving the attributes
        +
        styleAttr - The themed style attribute
        +
        styleRes - The style resource identifier
        +
        +
      • +
      + + + +
        +
      • +

        updateFromStyleResource

        +
        public static void updateFromStyleResource(TextLayoutBuilder builder,
        +                                           Context context,
        +                                           AttributeSet attrs,
        +                                           int styleAttr,
        +                                           int styleRes)
        +
        Sets the values for a TextLayoutBuilder from a style resource or a themed attribute.
        +
        +
        Parameters:
        +
        builder - The TextLayoutBuilder
        +
        context - The Context to use for resolving the attributes
        +
        attrs - The AttributeSet used during inflation
        +
        styleAttr - The themed style attribute
        +
        styleRes - The style resource identifier
        +
        +
      • +
      + + + +
        +
      • +

        setTextAppearance

        +
        public static void setTextAppearance(TextLayoutBuilder builder,
        +                                     Context context,
        +                                     int resId)
        +
        Sets a text appearance for the layout.
        +
        +
        Parameters:
        +
        builder - The TextLayoutBuilder instance
        +
        context - The Context to use for resolving attributes
        +
        resId - The resource identifier of the text appearance
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.MeasureMode.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.MeasureMode.html new file mode 100644 index 0000000..3da4700 --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.MeasureMode.html @@ -0,0 +1,169 @@ + + + + + +TextLayoutBuilder.MeasureMode (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
com.facebook.fbui.textlayoutbuilder
+

Annotation Type TextLayoutBuilder.MeasureMode

+
+
+
+ +
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.html new file mode 100644 index 0000000..f1f8e5f --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.html @@ -0,0 +1,1255 @@ + + + + + +TextLayoutBuilder (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
com.facebook.fbui.textlayoutbuilder
+

Class TextLayoutBuilder

+
+
+ +
+
    +
  • +
    +
    +
    public class TextLayoutBuilder
    +extends Object
    +
    An utility class to create text Layouts easily. +

    + This class uses a Builder pattern to allow re-using the same object + to create text Layouts with similar properties.

    +
  • +
+
+
+ +
+
+
    +
  • + + + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        TextLayoutBuilder

        +
        public TextLayoutBuilder()
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + + + + + +
        +
      • +

        setWidth

        +
        public TextLayoutBuilder setWidth(int width,
        +                                  int measureMode)
        +
        Sets the intended width of the text layout while respecting the measure mode.
        +
        +
        Parameters:
        +
        width - The width of the text layout
        +
        measureMode - The mode with which to treat the given width
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        See Also:
        +
        setWidth(int)
        +
        +
      • +
      + + + +
        +
      • +

        getText

        +
        public CharSequence getText()
        +
        Returns the text that would be packed in a layout by this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The text used by this TextLayoutBuilder
        +
        +
      • +
      + + + + + + + +
        +
      • +

        getTextSize

        +
        public float getTextSize()
        +
        Returns the text size for this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The text size used by this TextLayoutBuilder
        +
        +
      • +
      + + + +
        +
      • +

        setTextSize

        +
        public TextLayoutBuilder setTextSize(int size)
        +
        Sets the text size for the layout.
        +
        +
        Parameters:
        +
        size - The text size in pixels
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        getTextColor

        +
        public int getTextColor()
        +
        Returns the text color for this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The text color used by this TextLayoutBuilder
        +
        +
      • +
      + + + +
        +
      • +

        setTextColor

        +
        public TextLayoutBuilder setTextColor(int color)
        +
        Sets the text color for the layout.
        +
        +
        Parameters:
        +
        color - The text color for the layout
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        setTextColor

        +
        public TextLayoutBuilder setTextColor(ColorStateList colorStateList)
        +
        Sets the text color for the layout.
        +
        +
        Parameters:
        +
        colorStateList - The text color state list for the layout
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        getLinkColor

        +
        public int getLinkColor()
        +
        Returns the link color for this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The link color used by this TextLayoutBuilder
        +
        +
      • +
      + + + +
        +
      • +

        setLinkColor

        +
        public TextLayoutBuilder setLinkColor(int linkColor)
        +
        Sets the link color for the text in the layout.
        +
        +
        Parameters:
        +
        linkColor - The link color
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        getTextSpacingExtra

        +
        public float getTextSpacingExtra()
        +
        Returns the text spacing extra for this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The text spacing extra used by this TextLayoutBuilder
        +
        +
      • +
      + + + +
        +
      • +

        setTextSpacingExtra

        +
        public TextLayoutBuilder setTextSpacingExtra(float spacingExtra)
        +
        Sets the text extra spacing for the layout.
        +
        +
        Parameters:
        +
        spacingExtra - the extra space that is added to the height of each line
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        getTextSpacingMultiplier

        +
        public float getTextSpacingMultiplier()
        +
        Returns the text spacing multiplier for this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The text spacing multiplier used by this TextLayoutBuilder
        +
        +
      • +
      + + + +
        +
      • +

        setTextSpacingMultiplier

        +
        public TextLayoutBuilder setTextSpacingMultiplier(float spacingMultiplier)
        +
        Sets the line spacing multiplier for the layout.
        +
        +
        Parameters:
        +
        spacingMultiplier - the value by which each line's height is multiplied
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        getIncludeFontPadding

        +
        public boolean getIncludeFontPadding()
        +
        Returns whether this TextLayoutBuilder should include font padding.
        +
        +
        Returns:
        +
        Whether this TextLayoutBuilder should include font padding
        +
        +
      • +
      + + + +
        +
      • +

        setIncludeFontPadding

        +
        public TextLayoutBuilder setIncludeFontPadding(boolean shouldInclude)
        +
        Set whether the text Layout includes extra top and bottom padding to make + room for accents that go above the normal ascent and descent. +

        + The default is true.

        +
        +
        Parameters:
        +
        shouldInclude - Whether to include font padding or not
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        getAlignment

        +
        public Layout.Alignment getAlignment()
        +
        Returns the text alignment for this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The text alignment used by this TextLayoutBuilder
        +
        +
      • +
      + + + + + + + +
        +
      • +

        getTextDirection

        +
        public TextDirectionHeuristicCompat getTextDirection()
        +
        Returns the text direction for this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The text direction used by this TextLayoutBuilder
        +
        +
      • +
      + + + +
        +
      • +

        setTextDirection

        +
        public TextLayoutBuilder setTextDirection(TextDirectionHeuristicCompat textDirection)
        +
        Sets the text direction heuristic for the layout. +

        + TextDirectionHeuristicCompat describes how to evaluate the text + of this Layout to know whether to use RTL or LTR text direction

        +
        +
        Parameters:
        +
        textDirection - The text direction heuristic for the layout
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        setShadowLayer

        +
        public TextLayoutBuilder setShadowLayer(float radius,
        +                                        float dx,
        +                                        float dy,
        +                                        int color)
        +
        Sets the shadow layer for the layout.
        +
        +
        Parameters:
        +
        radius - The radius of the blur for shadow
        +
        dx - The horizontal translation of the origin
        +
        dy - The vertical translation of the origin
        +
        color - The shadow color
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        setTextStyle

        +
        public TextLayoutBuilder setTextStyle(int style)
        +
        Sets a text style for the layout.
        +
        +
        Parameters:
        +
        style - The text style for the layout
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        getTypeface

        +
        public Typeface getTypeface()
        +
        Returns the typeface for this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The typeface used by this TextLayoutBuilder
        +
        +
      • +
      + + + +
        +
      • +

        setTypeface

        +
        public TextLayoutBuilder setTypeface(Typeface typeface)
        +
        Sets the typeface used by this TextLayoutBuilder.
        +
        +
        Parameters:
        +
        typeface - The typeface for this TextLayoutBuilder
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        getDrawableState

        +
        public int[] getDrawableState()
        +
        Returns the drawable state for this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The drawable state used by this TextLayoutBuilder
        +
        +
      • +
      + + + +
        +
      • +

        setDrawableState

        +
        public TextLayoutBuilder setDrawableState(int[] drawableState)
        +
        Updates the text colors based on the drawable state.
        +
        +
        Parameters:
        +
        drawableState - The current drawable state of the View holding this layout
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        getEllipsize

        +
        public TextUtils.TruncateAt getEllipsize()
        +
        Returns the text ellipsize for this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The text ellipsize used by this TextLayoutBuilder
        +
        +
      • +
      + + + + + + + +
        +
      • +

        getSingleLine

        +
        public boolean getSingleLine()
        +
        Returns whether the TextLayoutBuilder should show a single line.
        +
        +
        Returns:
        +
        Whether the TextLayoutBuilder should show a single line or not
        +
        +
      • +
      + + + +
        +
      • +

        setSingleLine

        +
        public TextLayoutBuilder setSingleLine(boolean singleLine)
        +
        Sets whether the text should be in a single line or not.
        +
        +
        Parameters:
        +
        singleLine - Whether the text should be in a single line or not
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        See Also:
        +
        setMaxLines(int)
        +
        +
      • +
      + + + +
        +
      • +

        getMaxLines

        +
        public int getMaxLines()
        +
        Returns the number of max lines used by this TextLayoutBuilder.
        +
        +
        Returns:
        +
        The number of max lines for this TextLayoutBuilder
        +
        +
      • +
      + + + +
        +
      • +

        setMaxLines

        +
        public TextLayoutBuilder setMaxLines(int maxLines)
        +
        Sets a maximum number of lines to be shown by the Layout. +

        + Note: Gingerbread always default to two lines max when ellipsized. This cannot be changed. + Use a TextView if you want more control over the number of lines.

        +
        +
        Parameters:
        +
        maxLines - The number of maxLines to show in this Layout
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        See Also:
        +
        setSingleLine(boolean)
        +
        +
      • +
      + + + +
        +
      • +

        getShouldCacheLayout

        +
        public boolean getShouldCacheLayout()
        +
        Returns whether the TextLayoutBuilder should cache the layout.
        +
        +
        Returns:
        +
        Whether the TextLayoutBuilder should cache the layout
        +
        +
      • +
      + + + +
        +
      • +

        setShouldCacheLayout

        +
        public TextLayoutBuilder setShouldCacheLayout(boolean shouldCacheLayout)
        +
        Sets whether the text layout should be cached or not. +

        + Note: If the Layout contains ClickableSpans, the layout will not be cached.

        +
        +
        Parameters:
        +
        shouldCacheLayout - True to cache the text layout, false otherwise
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        +
      • +
      + + + +
        +
      • +

        getShouldWarmText

        +
        public boolean getShouldWarmText()
        +
        Returns whether the TextLayoutBuilder should warm the layout.
        +
        +
        Returns:
        +
        Whether the TextLayoutBuilder should warm the layout
        +
        +
      • +
      + + + +
        +
      • +

        setShouldWarmText

        +
        public TextLayoutBuilder setShouldWarmText(boolean shouldWarmText)
        +
        Sets whether the text should be warmed or not. +

        + Note: Setting this true is highly effective for large blurbs of text. + This method has to be called before the draw pass.

        +
        +
        Parameters:
        +
        shouldWarmText - True to warm the text layout, false otherwise
        +
        Returns:
        +
        This TextLayoutBuilder instance
        +
        See Also:
        +
        setGlyphWarmer(GlyphWarmer)
        +
        +
      • +
      + + + +
        +
      • +

        getGlyphWarmer

        +
        public GlyphWarmer getGlyphWarmer()
        +
        Returns the GlyphWarmer used by the TextLayoutBuilder.
        +
        +
        Returns:
        +
        The GlyphWarmer for this TextLayoutBuilder
        +
        +
      • +
      + + + + + + + +
        +
      • +

        build

        +
        public Layout build()
        +
        Builds and returns a Layout.
        +
        +
        Returns:
        +
        A Layout based on the parameters set
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/GlyphWarmerImpl.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/GlyphWarmerImpl.html new file mode 100644 index 0000000..988f1d9 --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/GlyphWarmerImpl.html @@ -0,0 +1,287 @@ + + + + + +GlyphWarmerImpl (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
com.facebook.fbui.textlayoutbuilder.glyphwarmer
+

Class GlyphWarmerImpl

+
+
+ +
+ +
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        GlyphWarmerImpl

        +
        public GlyphWarmerImpl()
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        warmLayout

        +
        public void warmLayout(Layout layout)
        +
        Description copied from interface: GlyphWarmer
        +
        Warms the text layout.
        +
        +
        Specified by:
        +
        warmLayout in interface GlyphWarmer
        +
        Parameters:
        +
        layout - The layout
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-frame.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-frame.html new file mode 100644 index 0000000..837118d --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-frame.html @@ -0,0 +1,20 @@ + + + + + +com.facebook.fbui.textlayoutbuilder.glyphwarmer (TextLayoutBuilder API) + + + + + +

com.facebook.fbui.textlayoutbuilder.glyphwarmer

+
+

Classes

+ +
+ + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-summary.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-summary.html new file mode 100644 index 0000000..7e26587 --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-summary.html @@ -0,0 +1,156 @@ + + + + + +com.facebook.fbui.textlayoutbuilder.glyphwarmer (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
+

Package com.facebook.fbui.textlayoutbuilder.glyphwarmer

+
+
Provides a default implementation of GlyphWarmer.
+
+

See: Description

+
+
+ + + + +

Package com.facebook.fbui.textlayoutbuilder.glyphwarmer Description

+
Provides a default implementation of GlyphWarmer.
+
+
See Also:
+
GlyphWarmer
+
+
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-tree.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-tree.html new file mode 100644 index 0000000..503a17c --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-tree.html @@ -0,0 +1,135 @@ + + + + + +com.facebook.fbui.textlayoutbuilder.glyphwarmer Class Hierarchy (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
+

Hierarchy For Package com.facebook.fbui.textlayoutbuilder.glyphwarmer

+Package Hierarchies: + +
+
+

Class Hierarchy

+ +
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/package-frame.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/package-frame.html new file mode 100644 index 0000000..02289de --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/package-frame.html @@ -0,0 +1,29 @@ + + + + + +com.facebook.fbui.textlayoutbuilder (TextLayoutBuilder API) + + + + + +

com.facebook.fbui.textlayoutbuilder

+
+

Interfaces

+ +

Classes

+ +

Annotation Types

+ +
+ + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/package-summary.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/package-summary.html new file mode 100644 index 0000000..a5bdebc --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/package-summary.html @@ -0,0 +1,196 @@ + + + + + +com.facebook.fbui.textlayoutbuilder (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
+

Package com.facebook.fbui.textlayoutbuilder

+
+
Provides the classes to build text Layouts easily.
+
+

See: Description

+
+
+ + + + +

Package com.facebook.fbui.textlayoutbuilder Description

+
Provides the classes to build text Layouts easily. +

+ TextLayoutBuilder is an utility class that creates + text Layouts easily. This package contains classes that interact with + TextLayoutBuilder.

+
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/package-tree.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/package-tree.html new file mode 100644 index 0000000..f99554e --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/package-tree.html @@ -0,0 +1,144 @@ + + + + + +com.facebook.fbui.textlayoutbuilder Class Hierarchy (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
+

Hierarchy For Package com.facebook.fbui.textlayoutbuilder

+Package Hierarchies: + +
+
+

Class Hierarchy

+ +

Interface Hierarchy

+ +

Annotation Type Hierarchy

+ +
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/LayoutMeasureUtil.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/LayoutMeasureUtil.html new file mode 100644 index 0000000..6a81478 --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/LayoutMeasureUtil.html @@ -0,0 +1,305 @@ + + + + + +LayoutMeasureUtil (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
com.facebook.fbui.textlayoutbuilder.util
+

Class LayoutMeasureUtil

+
+
+ +
+
    +
  • +
    +
    +
    public class LayoutMeasureUtil
    +extends Object
    +
    Utility Class for measuring text Layouts.
    +
  • +
+
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        LayoutMeasureUtil

        +
        public LayoutMeasureUtil()
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        getWidth

        +
        public static int getWidth(Layout layout)
        +
        Returns the width of the layout.
        +
        +
        Parameters:
        +
        layout - The layout
        +
        Returns:
        +
        The width of the layout
        +
        +
      • +
      + + + +
        +
      • +

        getHeight

        +
        public static int getHeight(Layout layout)
        +
        Prior to version 20, If the Layout specifies extra space between lines (either by spacingmult + or spacingadd) the StaticLayout would erroneously add this space after the last line as well. + This bug was fixed in version 20. This method calculates the extra space and reduces the height + by that amount.
        +
        +
        Parameters:
        +
        layout - The layout
        +
        Returns:
        +
        The height of the layout
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/package-frame.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/package-frame.html new file mode 100644 index 0000000..ee07f21 --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/package-frame.html @@ -0,0 +1,20 @@ + + + + + +com.facebook.fbui.textlayoutbuilder.util (TextLayoutBuilder API) + + + + + +

com.facebook.fbui.textlayoutbuilder.util

+
+

Classes

+ +
+ + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/package-summary.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/package-summary.html new file mode 100644 index 0000000..7cd0ce7 --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/package-summary.html @@ -0,0 +1,151 @@ + + + + + +com.facebook.fbui.textlayoutbuilder.util (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
+

Package com.facebook.fbui.textlayoutbuilder.util

+
+
Provides an utility class to measure text Layouts.
+
+

See: Description

+
+
+ + + + +

Package com.facebook.fbui.textlayoutbuilder.util Description

+
Provides an utility class to measure text Layouts.
+
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/package-tree.html b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/package-tree.html new file mode 100644 index 0000000..6944cfa --- /dev/null +++ b/docs/javadoc/com/facebook/fbui/textlayoutbuilder/util/package-tree.html @@ -0,0 +1,135 @@ + + + + + +com.facebook.fbui.textlayoutbuilder.util Class Hierarchy (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
+

Hierarchy For Package com.facebook.fbui.textlayoutbuilder.util

+Package Hierarchies: + +
+
+

Class Hierarchy

+ +
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/constant-values.html b/docs/javadoc/constant-values.html new file mode 100644 index 0000000..9816a0e --- /dev/null +++ b/docs/javadoc/constant-values.html @@ -0,0 +1,172 @@ + + + + + +Constant Field Values (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
+

Constant Field Values

+

Contents

+ +
+
+ + +

com.facebook.*

+ +
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/deprecated-list.html b/docs/javadoc/deprecated-list.html new file mode 100644 index 0000000..7b7420e --- /dev/null +++ b/docs/javadoc/deprecated-list.html @@ -0,0 +1,122 @@ + + + + + +Deprecated List (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
+

Deprecated API

+

Contents

+
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/help-doc.html b/docs/javadoc/help-doc.html new file mode 100644 index 0000000..62832d8 --- /dev/null +++ b/docs/javadoc/help-doc.html @@ -0,0 +1,223 @@ + + + + + +API Help (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
+

How This API Document Is Organized

+
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+ +This help file applies to API documentation generated using the standard doclet.
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/index-all.html b/docs/javadoc/index-all.html new file mode 100644 index 0000000..f949dc1 --- /dev/null +++ b/docs/javadoc/index-all.html @@ -0,0 +1,431 @@ + + + + + +Index (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
B C D G L M R S T U W  + + +

B

+
+
build() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Builds and returns a Layout.
+
+
+ + + +

C

+
+
com.facebook.fbui.textlayoutbuilder - package com.facebook.fbui.textlayoutbuilder
+
+
Provides the classes to build text Layouts easily.
+
+
com.facebook.fbui.textlayoutbuilder.glyphwarmer - package com.facebook.fbui.textlayoutbuilder.glyphwarmer
+
+
Provides a default implementation of GlyphWarmer.
+
+
com.facebook.fbui.textlayoutbuilder.util - package com.facebook.fbui.textlayoutbuilder.util
+
+
Provides an utility class to measure text Layouts.
+
+
+ + + +

D

+
+
DEFAULT_MAX_LINES - Static variable in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
 
+
+ + + +

G

+
+
getAlignment() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the text alignment for this TextLayoutBuilder.
+
+
getDrawableState() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the drawable state for this TextLayoutBuilder.
+
+
getEllipsize() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the text ellipsize for this TextLayoutBuilder.
+
+
getGlyphWarmer() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the GlyphWarmer used by the TextLayoutBuilder.
+
+
getHeight(Layout) - Static method in class com.facebook.fbui.textlayoutbuilder.util.LayoutMeasureUtil
+
+
Prior to version 20, If the Layout specifies extra space between lines (either by spacingmult + or spacingadd) the StaticLayout would erroneously add this space after the last line as well.
+
+
getIncludeFontPadding() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns whether this TextLayoutBuilder should include font padding.
+
+
getLinkColor() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the link color for this TextLayoutBuilder.
+
+
getMaxLines() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the number of max lines used by this TextLayoutBuilder.
+
+
getShouldCacheLayout() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns whether the TextLayoutBuilder should cache the layout.
+
+
getShouldWarmText() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns whether the TextLayoutBuilder should warm the layout.
+
+
getSingleLine() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns whether the TextLayoutBuilder should show a single line.
+
+
getText() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the text that would be packed in a layout by this TextLayoutBuilder.
+
+
getTextColor() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the text color for this TextLayoutBuilder.
+
+
getTextDirection() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the text direction for this TextLayoutBuilder.
+
+
getTextSize() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the text size for this TextLayoutBuilder.
+
+
getTextSpacingExtra() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the text spacing extra for this TextLayoutBuilder.
+
+
getTextSpacingMultiplier() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the text spacing multiplier for this TextLayoutBuilder.
+
+
getTypeface() - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Returns the typeface for this TextLayoutBuilder.
+
+
getWidth(Layout) - Static method in class com.facebook.fbui.textlayoutbuilder.util.LayoutMeasureUtil
+
+
Returns the width of the layout.
+
+
GlyphWarmer - Interface in com.facebook.fbui.textlayoutbuilder
+
+
Specifies an interface that a class has to implement + to warm the text Layout in the background.
+
+
GlyphWarmerImpl - Class in com.facebook.fbui.textlayoutbuilder.glyphwarmer
+
+
Default GlyphWarmer that runs a HandlerThread + to draw a text Layout on a Picture.
+
+
GlyphWarmerImpl() - Constructor for class com.facebook.fbui.textlayoutbuilder.glyphwarmer.GlyphWarmerImpl
+
 
+
+ + + +

L

+
+
LayoutMeasureUtil - Class in com.facebook.fbui.textlayoutbuilder.util
+
+
Utility Class for measuring text Layouts.
+
+
LayoutMeasureUtil() - Constructor for class com.facebook.fbui.textlayoutbuilder.util.LayoutMeasureUtil
+
 
+
+ + + +

M

+
+
MEASURE_MODE_AT_MOST - Static variable in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
 
+
MEASURE_MODE_EXACTLY - Static variable in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
 
+
MEASURE_MODE_UNSPECIFIED - Static variable in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
 
+
+ + + +

R

+
+
ResourceTextLayoutHelper - Class in com.facebook.fbui.textlayoutbuilder
+
+
An utility class to update a TextLayoutBuilder from an Android resource.
+
+
ResourceTextLayoutHelper() - Constructor for class com.facebook.fbui.textlayoutbuilder.ResourceTextLayoutHelper
+
 
+
+ + + +

S

+
+
setAlignment(Layout.Alignment) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets text alignment for the layout.
+
+
setDrawableState(int[]) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Updates the text colors based on the drawable state.
+
+
setEllipsize(TextUtils.TruncateAt) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the ellipsis location for the layout.
+
+
setGlyphWarmer(GlyphWarmer) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the glyph warmer to use.
+
+
setIncludeFontPadding(boolean) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Set whether the text Layout includes extra top and bottom padding to make + room for accents that go above the normal ascent and descent.
+
+
setLinkColor(int) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the link color for the text in the layout.
+
+
setMaxLines(int) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets a maximum number of lines to be shown by the Layout.
+
+
setShadowLayer(float, float, float, int) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the shadow layer for the layout.
+
+
setShouldCacheLayout(boolean) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets whether the text layout should be cached or not.
+
+
setShouldWarmText(boolean) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets whether the text should be warmed or not.
+
+
setSingleLine(boolean) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets whether the text should be in a single line or not.
+
+
setText(CharSequence) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the text for the layout.
+
+
setTextAppearance(TextLayoutBuilder, Context, int) - Static method in class com.facebook.fbui.textlayoutbuilder.ResourceTextLayoutHelper
+
+
Sets a text appearance for the layout.
+
+
setTextColor(int) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the text color for the layout.
+
+
setTextColor(ColorStateList) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the text color for the layout.
+
+
setTextDirection(TextDirectionHeuristicCompat) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the text direction heuristic for the layout.
+
+
setTextSize(int) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the text size for the layout.
+
+
setTextSpacingExtra(float) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the text extra spacing for the layout.
+
+
setTextSpacingMultiplier(float) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the line spacing multiplier for the layout.
+
+
setTextStyle(int) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets a text style for the layout.
+
+
setTypeface(Typeface) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the typeface used by this TextLayoutBuilder.
+
+
setWidth(int) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the intended width of the text layout.
+
+
setWidth(int, int) - Method in class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
+
Sets the intended width of the text layout while respecting the measure mode.
+
+
+ + + +

T

+
+
TextLayoutBuilder - Class in com.facebook.fbui.textlayoutbuilder
+
+
An utility class to create text Layouts easily.
+
+
TextLayoutBuilder() - Constructor for class com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder
+
 
+
TextLayoutBuilder.MeasureMode - Annotation Type in com.facebook.fbui.textlayoutbuilder
+
+
Measure mode constants similar to View.MeasureSpec
+
+
+ + + +

U

+
+
updateFromStyleResource(TextLayoutBuilder, Context, int) - Static method in class com.facebook.fbui.textlayoutbuilder.ResourceTextLayoutHelper
+
+
Sets the values for a TextLayoutBuilder from a style resource.
+
+
updateFromStyleResource(TextLayoutBuilder, Context, int, int) - Static method in class com.facebook.fbui.textlayoutbuilder.ResourceTextLayoutHelper
+
+
Sets the values for a TextLayoutBuilder from a style resource or a themed attribute.
+
+
updateFromStyleResource(TextLayoutBuilder, Context, AttributeSet, int, int) - Static method in class com.facebook.fbui.textlayoutbuilder.ResourceTextLayoutHelper
+
+
Sets the values for a TextLayoutBuilder from a style resource or a themed attribute.
+
+
+ + + +

W

+
+
warmLayout(Layout) - Method in class com.facebook.fbui.textlayoutbuilder.glyphwarmer.GlyphWarmerImpl
+
 
+
warmLayout(Layout) - Method in interface com.facebook.fbui.textlayoutbuilder.GlyphWarmer
+
+
Warms the text layout.
+
+
+B C D G L M R S T U W 
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/index.html b/docs/javadoc/index.html new file mode 100644 index 0000000..65cf246 --- /dev/null +++ b/docs/javadoc/index.html @@ -0,0 +1,74 @@ + + + + + +TextLayoutBuilder API + + + + + + + + + +<noscript> +<div>JavaScript is disabled on your browser.</div> +</noscript> +<h2>Frame Alert</h2> +<p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to <a href="overview-summary.html">Non-frame version</a>.</p> + + + diff --git a/docs/javadoc/overview-frame.html b/docs/javadoc/overview-frame.html new file mode 100644 index 0000000..00830c1 --- /dev/null +++ b/docs/javadoc/overview-frame.html @@ -0,0 +1,23 @@ + + + + + +Overview List (TextLayoutBuilder API) + + + + + +
All Classes
+
+

Packages

+ +
+

 

+ + diff --git a/docs/javadoc/overview-summary.html b/docs/javadoc/overview-summary.html new file mode 100644 index 0000000..af4b821 --- /dev/null +++ b/docs/javadoc/overview-summary.html @@ -0,0 +1,150 @@ + + + + + +Overview (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
+

TextLayoutBuilder API

+
+
+ + + + + + + + + + + + + + + + + + + + +
Packages 
PackageDescription
com.facebook.fbui.textlayoutbuilder +
Provides the classes to build text Layouts easily.
+
com.facebook.fbui.textlayoutbuilder.glyphwarmer +
Provides a default implementation of GlyphWarmer.
+
com.facebook.fbui.textlayoutbuilder.util +
Provides an utility class to measure text Layouts.
+
+
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/overview-tree.html b/docs/javadoc/overview-tree.html new file mode 100644 index 0000000..90bdbd5 --- /dev/null +++ b/docs/javadoc/overview-tree.html @@ -0,0 +1,148 @@ + + + + + +Class Hierarchy (TextLayoutBuilder API) + + + + + + + + +
+ + + + + + + +
+ + +
+

Hierarchy For All Packages

+Package Hierarchies: + +
+
+

Class Hierarchy

+ +

Interface Hierarchy

+ +

Annotation Type Hierarchy

+ +
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/package-list b/docs/javadoc/package-list new file mode 100644 index 0000000..56e7f61 --- /dev/null +++ b/docs/javadoc/package-list @@ -0,0 +1,3 @@ +com.facebook.fbui.textlayoutbuilder +com.facebook.fbui.textlayoutbuilder.glyphwarmer +com.facebook.fbui.textlayoutbuilder.util diff --git a/docs/javadoc/script.js b/docs/javadoc/script.js new file mode 100644 index 0000000..b346356 --- /dev/null +++ b/docs/javadoc/script.js @@ -0,0 +1,30 @@ +function show(type) +{ + count = 0; + for (var key in methods) { + var row = document.getElementById(key); + if ((methods[key] & type) != 0) { + row.style.display = ''; + row.className = (count++ % 2) ? rowColor : altColor; + } + else + row.style.display = 'none'; + } + updateTabs(type); +} + +function updateTabs(type) +{ + for (var value in tabs) { + var sNode = document.getElementById(tabs[value][0]); + var spanNode = sNode.firstChild; + if (value == type) { + sNode.className = activeTableTab; + spanNode.innerHTML = tabs[value][1]; + } + else { + sNode.className = tableTab; + spanNode.innerHTML = "" + tabs[value][1] + ""; + } + } +} diff --git a/docs/javadoc/stylesheet.css b/docs/javadoc/stylesheet.css new file mode 100644 index 0000000..96e35e7 --- /dev/null +++ b/docs/javadoc/stylesheet.css @@ -0,0 +1,571 @@ +/* Javadoc style sheet */ +/* +Overall document style +*/ + +body { + background-color: #ffffff; + color: #4B4F56; + font-family: -apple-system, Arial, Helvetica, sans-serif; + font-size: 14px; + margin: 0; +} +a:link, a:visited { + text-decoration: none; + color: #34255D; +} +a:hover, a:focus { + text-decoration: none; + color: #6A51B2; +} +a:active { + text-decoration: none; + color: #34255D; +} +a[name] { + color: #1D2129; +} +a[name]:hover { + text-decoration: none; + color: #1D2129; +} +pre { + font-family: Consolas, Menlo, Monaco, monospace; + font-size: 14px; +} +h1 { + font-size: 20px; +} +h2 { + font-size: 18px; +} +h3 { + font-size: 16px; + font-style: italic; +} +h4 { + font-size: 13px; +} +h5 { + font-size: 12px; +} +h6 { + font-size: 11px; +} +ul { + list-style-type: disc; +} +code, tt { + font-family: Consolas, Menlo, Monaco, monospace; + font-size: 14px; + padding-top: 4px; + margin-top: 8px; + line-height: 1.4em; +} +dt code { + font-family: Consolas, Menlo, Monaco, monospace; + font-size: 14px; + padding-top: 4px; +} +table tr td dt code { + font-family: Consolas, Menlo, Monaco, monospace; + font-size: 14px; + vertical-align: top; + padding-top: 4px; +} +sup { + font-size: 8px; +} +/* +Document title and Copyright styles +*/ +.clear { + clear: both; + height: 0px; + overflow: hidden; +} +.aboutLanguage { + float: right; + padding: 0px 21px; + font-size: 11px; + z-index: 200; + margin-top: -9px; +} +.legalCopy { + margin-left: .5em; +} +.bar a, .bar a:link, .bar a:visited, .bar a:active { + color: #FFFFFF; + text-decoration: none; +} +.bar a:hover, .bar a:focus { + color: #9D87D2; +} +.tab { + background-color: #0066FF; + color: #ffffff; + padding: 8px; + width: 5em; + font-weight: bold; +} +/* +Navigation bar styles +*/ +.bar { + background-color: #6A51B2; + color: #FFFFFF; + padding: .8em .5em .4em .8em; + height: auto;/*height: 1.8em;*/ + font-size: 11px; + margin: 0; +} +.topNav { + background-color: #6A51B2; + color: #FFFFFF; + float: left; + padding: 0; + width: 100%; + clear: right; + height: 2.8em; + padding-top: 10px; + overflow: hidden; + font-size: 12px; +} +.bottomNav { + margin-top: 10px; + background-color: #6A51B2; + color: #FFFFFF; + float: left; + padding: 0; + width: 100%; + clear: right; + height: 2.8em; + padding-top: 10px; + overflow: hidden; + font-size: 12px; +} +.subNav { + background-color: #E9EBEE; + float: left; + width: 100%; + overflow: hidden; + font-size: 12px; +} +.subNav div { + clear: left; + float: left; + padding: 0 0 5px 6px; + text-transform: uppercase; +} +ul.navList, ul.subNavList { + float: left; + margin: 0 25px 0 0; + padding: 0; +} +ul.navList li{ + list-style: none; + float: left; + padding: 5px 6px; + text-transform: uppercase; +} +ul.subNavList li{ + list-style: none; + float: left; +} +.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { + color: #FFFFFF; + text-decoration: none; + text-transform: uppercase; +} +.topNav a:hover, .bottomNav a:hover { + text-decoration: none; + color: #9D87D2; + text-transform: uppercase; +} +.navBarCell1Rev { + background-color: #DDD5F0; + color: #253441; + margin: auto 5px; +} +.skipNav { + position: absolute; + top: auto; + left: -9999px; + overflow: hidden; +} +/* +Page header and footer styles +*/ +.header, .footer { + clear: both; + margin: 0 20px; + padding: 5px 0 0 0; +} +.indexHeader { + margin: 10px; + position: relative; +} +.indexHeader span{ + margin-right: 15px; +} +.indexHeader h1 { + font-size: 13px; +} +.title { + color: #1D2129; + margin: 10px 0; +} +.subTitle { + margin: 5px 0 0 0; +} +.header ul { + margin: 0 0 15px 0; + padding: 0; +} +.footer ul { + margin: 20px 0 5px 0; +} +.header ul li, .footer ul li { + list-style: none; + font-size: 13px; +} +/* +Heading styles +*/ +div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { + background-color: #E9EBEE; + border: 1px solid #E9EBEE; + margin: 0 0 6px -8px; + padding: 7px 5px; +} +ul.blockList ul.blockList ul.blockList li.blockList h3 { + background-color: #E9EBEE; + border: 1px solid #E9EBEE; + margin: 0 0 6px -8px; + padding: 7px 5px; +} +ul.blockList ul.blockList li.blockList h3 { + padding: 0; + margin: 15px 0; +} +ul.blockList li.blockList h2 { + padding: 0px 0 20px 0; +} +/* +Page layout container styles +*/ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { + clear: both; + padding: 10px 20px; + position: relative; +} +.indexContainer { + margin: 10px; + position: relative; + font-size: 12px; +} +.indexContainer h2 { + font-size: 13px; + padding: 0 0 3px 0; +} +.indexContainer ul { + margin: 0; + padding: 0; +} +.indexContainer ul li { + list-style: none; + padding-top: 2px; +} +.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { + font-size: 12px; + font-weight: bold; + margin: 10px 0 0 0; + color: #1D2129; +} +.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { + margin: 5px 0 10px 0px; + font-size: 14px; + font-family: Consolas, Menlo, Monaco, monospace; +} +.serializedFormContainer dl.nameValue dt { + margin-left: 1px; + font-size: 1.1em; + display: inline; + font-weight: bold; +} +.serializedFormContainer dl.nameValue dd { + margin: 0 0 0 1px; + font-size: 1.1em; + display: inline; +} +/* +List styles +*/ +ul.horizontal li { + display: inline; + font-size: 0.9em; +} +ul.inheritance { + margin: 0; + padding: 0; +} +ul.inheritance li { + display: inline; + list-style: none; +} +ul.inheritance li ul.inheritance { + margin-left: 15px; + padding-left: 15px; + padding-top: 1px; +} +ul.blockList, ul.blockListLast { + margin: 10px 0 10px 0; + padding: 0; +} +ul.blockList li.blockList, ul.blockListLast li.blockList { + list-style: none; + margin-bottom: 15px; + line-height: 1.4; +} +ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { + padding: 0px 20px 5px 10px; + border: 1px solid #ededed; + background-color: #f8f8f8; +} +ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { + padding: 0 0 5px 8px; + background-color: #ffffff; + border: none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { + margin-left: 0; + padding-left: 0; + padding-bottom: 15px; + border: none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { + list-style: none; + border-bottom: none; + padding-bottom: 0; +} +table tr td dl, table tr td dl dt, table tr td dl dd { + margin-top: 0; + margin-bottom: 1px; +} +/* +Table styles +*/ +.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { + width: 100%; + border-left: 1px solid #E9EBEE; + border-right: 1px solid #E9EBEE; + border-bottom: 1px solid #E9EBEE; +} +.overviewSummary, .memberSummary { + padding: 0px; +} +.overviewSummary caption, .memberSummary caption, .typeSummary caption, +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption { + position: relative; + text-align: left; + background-repeat: no-repeat; + color: #4B4F56; + font-weight: bold; + clear: none; + overflow: hidden; + padding: 0px; + padding-top: 10px; + padding-left: 1px; + margin: 0px; + white-space: pre; +} +.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, +.useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, +.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, +.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, +.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, +.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { + color: #FFFFFF; +} +.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { + white-space: nowrap; + padding-top: 5px; + padding-left: 12px; + padding-right: 12px; + padding-bottom: 7px; + display: inline-block; + float: left; + background-color: #DDD5F0; + border: none; + height: 16px; +} +.memberSummary caption span.activeTableTab span { + white-space: nowrap; + padding-top: 5px; + padding-left: 12px; + padding-right: 12px; + margin-right: 3px; + display: inline-block; + float: left; + background-color: #DDD5F0; + height: 16px; +} +.memberSummary caption span.tableTab span { + white-space: nowrap; + padding-top: 5px; + padding-left: 12px; + padding-right: 12px; + margin-right: 3px; + display: inline-block; + float: left; + background-color: #6A51B2; + height: 16px; +} +.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { + padding-top: 0px; + padding-left: 0px; + padding-right: 0px; + background-image: none; + float: none; + display: inline; +} +.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { + display: none; + width: 5px; + position: relative; + float: left; + background-color: #DDD5F0; +} +.memberSummary .activeTableTab .tabEnd { + display: none; + width: 5px; + margin-right: 3px; + position: relative; + float: left; + background-color: #DDD5F0; +} +.memberSummary .tableTab .tabEnd { + display: none; + width: 5px; + margin-right: 3px; + position: relative; + background-color: #6A51B2; + float: left; + +} +.overviewSummary td, .memberSummary td, .typeSummary td, +.useSummary td, .constantsSummary td, .deprecatedSummary td { + text-align: left; + padding: 0px 0px 12px 10px; +} +th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, +td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ + vertical-align: top; + padding-right: 0px; + padding-top: 8px; + padding-bottom: 3px; +} +th.colFirst, th.colLast, th.colOne, .constantsSummary th { + background: #E9EBEE; + text-align: left; + padding: 8px 3px 3px 7px; +} +td.colFirst, th.colFirst { + white-space: nowrap; + font-size: 13px; +} +td.colLast, th.colLast { + font-size: 13px; +} +td.colOne, th.colOne { + font-size: 13px; +} +.overviewSummary td.colFirst, .overviewSummary th.colFirst, +.useSummary td.colFirst, .useSummary th.colFirst, +.overviewSummary td.colOne, .overviewSummary th.colOne, +.memberSummary td.colFirst, .memberSummary th.colFirst, +.memberSummary td.colOne, .memberSummary th.colOne, +.typeSummary td.colFirst{ + width: 25%; + vertical-align: top; +} +td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { + font-weight: bold; +} +.tableSubHeadingColor { + background-color: #EEEEFF; +} +.altColor { + background-color: #FFFFFF; +} +.rowColor { + background-color: #F6F7F9; +} +/* +Content styles +*/ +.description pre { + margin-top: 0; +} +.deprecatedContent { + margin: 0; + padding: 10px 0; +} +.docSummary { + padding: 0; +} + +ul.blockList ul.blockList ul.blockList li.blockList h3 { + font-style: normal; +} + +div.block { + font-size: 14px; +} + +td.colLast div { + padding-top: 0px; +} + + +td.colLast a { + padding-bottom: 3px; +} +/* +Formatting effect styles +*/ +.sourceLineNo { + color: green; + padding: 0 30px 0 0; +} +h1.hidden { + visibility: hidden; + overflow: hidden; + font-size: 10px; +} +.block { + display: block; + margin: 3px 10px 2px 0px; + color: #4B4F56; +} +.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, +.overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel, +.seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink { + font-weight: bold; +} +.deprecationComment, .emphasizedPhrase, .interfaceName { + font-style: italic; +} + +div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, +div.block div.block span.interfaceName { + font-style: normal; +} + +div.contentContainer ul.blockList li.blockList h2{ + padding-bottom: 0px; +} diff --git a/docs/logo.png b/docs/logo.png new file mode 100644 index 0000000..524199d Binary files /dev/null and b/docs/logo.png differ diff --git a/docs/main.css b/docs/main.css new file mode 100644 index 0000000..a9059c3 --- /dev/null +++ b/docs/main.css @@ -0,0 +1,335 @@ +html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, +blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, +ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, +dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, +tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, +figcaption, footer, header, hgroup, menu, nav, output, ruby, section, +summary, time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + vertical-align: baseline; +} +article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { + display: block; +} +ol, ul { + list-style: none; +} +body { + color: #4B4F56; + font: normal 16px -apple-system, 'BlinkMacSystemFont', 'Roboto', sans-serif; + text-align: left; + display: flex; + flex-direction: column; +} +a { + text-decoration: none; + -webkit-transition: background 0.3s, color 0.3s; + transition: background 0.3s, color 0.3s; +} +/** + * Common classes. + */ +@media only screen and (min-width: 480px) { + .wrapper { + margin: 0px 20px; + } +} +@media only screen and (min-width: 900px) { + .wrapper { + margin: 0px 40px; + } +} +@media only screen and (min-width: 1024px) { + .wrapper { + margin: 0px 80px; + } +} +@media only screen and (min-width: 1200px) { + .wrapper { + margin: 0px 120px; + } +} +@media only screen and (min-width: 1500px) { + .wrapper { + margin: 0px 120px; + } +} +.spacer { + flex-grow: 1; +} +/** + * Navigation + */ +nav ul { + display: flex; + flex-flow: row nowrap; +} +nav ul li { + padding: 0 6px; + display: block; +} +nav ul li a { + border-radius: 2px; + font-size: 14px; + padding: 0px 16px; + height: 36px; +} +/** + * Header + */ +.headerContainer { + background: #6A51B2; + color: #fff; + height: 64px; + padding: 0px; + width: 100%; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.26); + position: fixed; + z-index: 1; +} +header { + display: flex; + flex-flow: row; + text-align: left; + padding: 8px 0px; + align-items: center; +} +header a { + border: 0; + color: #fff; + display: flex; + flex-flow: row nowrap; + align-items: center; +} +header img { + height: 48px; + width: 48px; + margin-right: 12px; +} +header h2 { + font-size: 18px; +} +/** + * Header Navigation + */ + header nav ul li a { + color: #fff; + } + header nav ul li a:hover { + background-color: #DDD5F0; + color: #4B4F56; + } + /** + * Footer + */ + .footerContainer { + border-top: 1px solid #DDD5F0; + background: #fff; + color: #6A51B2; + text-align: left; + display: flex; + flex-direction: column; + } + .footerContainer a { + border: 0; + display: flex; + flex-flow: row nowrap; + align-items: center; + } + footer { + padding: 10px 0px; + display: flex; + flex-flow: row wrap; + align-items: center; + align-content: center; + } + /** + * Footer Navigation + */ + footer nav ul li a { + color: #6A51B2; + } + footer nav ul li a:hover { + background-color: #DDD5F0; + } + /** + * Footer Open Source + */ + footer .fboss { + align-items: center; + display: flex; + flex-flow: row nowrap; + font-size: 14px; + } + footer .fboss .facebookOSSLogoSvg { + flex: 0 0 31px; + height: 30px; + margin-right: 10px; + width: 31px; + } + footer .fboss .facebookOSSLogoSvg path { + fill: #6A51B2; + } + footer .fboss .facebookOSSLogoSvg .middleRing { + opacity: 0.7; + } + footer .fboss .facebookOSSLogoSvg .innerRing { + opacity: 0.45; + } + footer .fboss h2 { + font-size: 14px; + } +/** + * API Container + */ +.apiContainer { + padding-top: 64px; +} +iframe { + display: block; + width: 100%; + height: 100%; +} +/** + * Contents Container + */ +.contentsContainer { + background-color: #F6F7F9; + padding: 64px 0px 0px; +} +.instructionsContainer { + background-color: #E9EBEE; + border-top: 1px solid #DDD5F0; +} +.instructionsContainer.alternate { + background-color: #F6F7F9; +} +/** + * Sections + */ +section { + margin: 36px; +} +section h2 { + color: #6A51B2; + font-size: 30px; + font-style: normal; + margin-bottom: 24px; + font-weight: 300; + font-style: italic; +} +section h3 { + color: #6A51B2; +} +section ol, section ul { + margin: 20px 0px 0px; +} +section ol { + list-style: decimal none inside; +} +/** + * Intro and Features + */ +.introContainer, .featureContainer { + display: flex; + margin-top: 20px; + flex-direction: column; +} +@media only screen and (min-width: 480px) { + .introContainer, .featureContainer { + flex-direction: column; + } +} +@media only screen and (min-width: 900px) { + .featureContainer { + flex-direction: row; + } +} +@media only screen and (min-width: 1200px) { + .introContainer { + flex-direction: row; + } +} +.intro { + flex: 1; +} +.intro ul { + list-style: circle inside; +} +.intro li { + padding: 6px 0px; +} +.feature { + background-color: #fff; + flex: 1; + border-radius: 4px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.26); + padding: 12px 24px; +} +.feature p { + padding: 24px 0px 12px; + font-style: italic; +} +@media only screen and (min-width: 480px) { + .intro, .feature { + margin: 12px 0px; + } +} +@media only screen and (min-width: 900px) { + .feature { + margin: 12px; + } +} +@media only screen and (min-width: 1200px) { + .intro, .feature { + margin: 24px; + } +} +h4 { + display: block; + margin-top: 6px; + text-align: center; + font-style: italic; +} +/** + * Code + */ +pre { + background-color: #4c4c4c; + padding: 12px; + margin: 24px auto; + color: #fff; + max-width: 800px; + border-radius: 2px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.26); + line-height: 1.5em; + font-family: Consolas, Monaco, Menlo, monospace; + white-space: pre-wrap; /* CSS 3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} +pre.highlight { + background-color: #fff; + color: #4c4c4c; + padding: 12px; + margin: 0px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.26); +} +@media only screen and (min-width: 480px) { + pre.highlight { + padding: 12px; + } +} +@media only screen and (min-width: 900px) { + pre.highlight { + padding: 24px; + } +} +code { + color: #6A51B2; + font-family: Consolas, Monaco, Menlo, monospace; +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..b6440ae --- /dev/null +++ b/gradle.properties @@ -0,0 +1,7 @@ +GROUP=com.facebook.fbui.textlayoutbuilder +VERSION_NAME=1.0.0 + +POM_NAME=TextLayoutBuilder +POM_DESCRIPTION=Better way to create text Layouts on Android +POM_ARTIFACT_ID=textlayoutbuilder +POM_PACKAGING=aar diff --git a/gradle/android-maven-install.gradle b/gradle/android-maven-install.gradle new file mode 100644 index 0000000..e18f078 --- /dev/null +++ b/gradle/android-maven-install.gradle @@ -0,0 +1,37 @@ +// Configure the Android maven publication + +apply plugin: 'com.github.dcendents.android-maven' + +version = VERSION_NAME +group = GROUP +// Set the .aar / .jar base file name to match the artifact ID +// in case the module has a different name +project.archivesBaseName = POM_ARTIFACT_ID + +install { + repositories.mavenInstaller { + // This generates POM.xml with proper parameters + pom.project { + name POM_NAME + artifactId POM_ARTIFACT_ID + packaging POM_PACKAGING + description POM_DESCRIPTION + url projectUrl + + scm { + url scmUrl + connection scmConnection + developerConnection scmDeveloperConnection + } + + licenses projectLicenses + + developers { + developer { + id developerId + name developerName + } + } + } + } +} diff --git a/gradle/android-tasks.gradle b/gradle/android-tasks.gradle new file mode 100644 index 0000000..f55f8f0 --- /dev/null +++ b/gradle/android-tasks.gradle @@ -0,0 +1,47 @@ +// Android tasks for Javadoc and sources.jar generation + +afterEvaluate { project -> + task androidJavadoc(type: Javadoc) { + source = android.sourceSets.main.java.srcDirs + exclude '**/pom.xml' + exclude '**/proguard_annotations.pro' + exclude '**/BUCK' + classpath += files(android.bootClasspath) + classpath += configurations.compile + title = 'TextLayoutBuilder API' + options { + links 'http://docs.oracle.com/javase/7/docs/api/' + linksOffline 'https://developer.android.com/reference/', "file://${android.sdkDirectory}/docs/reference/" + } + } + + androidJavadoc.doLast { + copy { + from new File(projectDir, 'src/javadoc/stylesheet.css') + into new File(buildDir, 'docs/javadoc/') + } + } + + task androidJavadocJar(type: Jar) { + classifier = 'javadoc' + from androidJavadoc.destinationDir + } + + task androidSourcesJar(type: Jar) { + classifier = 'sources' + from android.sourceSets.main.java.srcDirs + exclude '**/BUCK' + } + + android.libraryVariants.all { variant -> + def name = variant.name.capitalize() + task "jar${name}"(type: Jar, dependsOn: variant.javaCompile) { + from variant.javaCompile.destinationDir + } + } + + artifacts { + archives androidSourcesJar + archives androidJavadocJar + } +} diff --git a/gradle/bintray.gradle b/gradle/bintray.gradle new file mode 100644 index 0000000..a6892fe --- /dev/null +++ b/gradle/bintray.gradle @@ -0,0 +1,57 @@ +// Upload to Bintray +apply plugin: 'com.jfrog.bintray' + +def getBintrayUsername() { + return hasProperty('bintrayUsername') ? property('bintrayUsername') : System.getenv('BINTRAY_USERNAME') +} + +def getBintrayApiKey() { + return hasProperty('bintrayApiKey') ? property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') +} + +def getBintrayGpgPassword() { + return hasProperty('bintrayGpgPassword') ? property('bintrayGpgPassword') : System.getenv('BINTRAY_GPG_PASSWORD') +} + +def getMavenCentralUsername() { + return hasProperty('mavenCentralUsername') ? property('mavenCentralUsername') : System.getenv('MAVEN_CENTRAL_USERNAME') +} + +def getMavenCentralPassword() { + return hasProperty('mavenCentralPassword') ? property('mavenCentralPassword') : System.getenv('MAVEN_CENTRAL_PASSWORD') +} + +def shouldSyncWithMavenCentral() { + return hasProperty('syncWithMavenCentral') ? property('syncWithMavenCentral').toBoolean() : false +} + +bintray { + user = getBintrayUsername() + key = getBintrayApiKey() + configurations = ['archives'] + pkg { + repo = bintrayRepo + userOrg = bintrayUserOrg + name = bintrayName + desc = bintrayDescription + websiteUrl = projectUrl + issueTrackerUrl = issuesUrl + vcsUrl = scmUrl + licenses = projectLicenses + publish = true + publicDownloadNumbers = true + version { + desc = bintrayDescription + gpg { + sign = true + passphrase = getBintrayGpgPassword() + } + mavenCentralSync { + sync = shouldSyncWithMavenCentral() + user = getMavenCentralUsername() + password = getMavenCentralPassword() + close = '1' // If set to 0, you have to manually click release + } + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..deedc7f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1d64bef --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Nov 11 18:57:22 PST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..9aa616c --- /dev/null +++ b/gradlew @@ -0,0 +1,169 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..e95643d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@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 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= + +@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/library/.gitignore b/library/.gitignore new file mode 100644 index 0000000..cba8ae6 --- /dev/null +++ b/library/.gitignore @@ -0,0 +1,8 @@ +.gradle +.DS_Store +.idea +build/ +local.properties +localhost/ +obj/ +*.iml diff --git a/library/build.gradle b/library/build.gradle new file mode 100644 index 0000000..1435a90 --- /dev/null +++ b/library/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'com.android.library' + +dependencies { + compile project(':library:libs:proxy') + provided "com.android.support:support-compat:${rootProject.ext.supportLibVersion}" + + testCompile "junit:junit:${rootProject.ext.junitVersion}" + testCompile "org.mockito:mockito-core:${rootProject.ext.mockitoCoreVersion}" + testCompile("org.robolectric:robolectric:${rootProject.ext.robolectricVersion}") { + exclude group: 'commons-logging', module: 'commons-logging' + exclude group: 'org.apache.httpcomponents', module: 'httpclient' + } + + androidTestCompile "junit:junit:${rootProject.ext.junitVersion}" +} + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + + defaultConfig { + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + } +} + +apply from: rootProject.file('release.gradle') diff --git a/library/libs/android-support-v4-text/.gitignore b/library/libs/android-support-v4-text/.gitignore new file mode 100644 index 0000000..cba8ae6 --- /dev/null +++ b/library/libs/android-support-v4-text/.gitignore @@ -0,0 +1,8 @@ +.gradle +.DS_Store +.idea +build/ +local.properties +localhost/ +obj/ +*.iml diff --git a/library/libs/android-support-v4-text/build.gradle b/library/libs/android-support-v4-text/build.gradle new file mode 100644 index 0000000..77c147b --- /dev/null +++ b/library/libs/android-support-v4-text/build.gradle @@ -0,0 +1,10 @@ +apply plugin: 'java' + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 + +jar { + baseName = 'android-support-v4-text' + version = '1.0' +} + diff --git a/library/libs/android-support-v4-text/src/main/java/android/support/v4/text/TextDirectionHeuristicCompat.java b/library/libs/android-support-v4-text/src/main/java/android/support/v4/text/TextDirectionHeuristicCompat.java new file mode 100644 index 0000000..e0a54f7 --- /dev/null +++ b/library/libs/android-support-v4-text/src/main/java/android/support/v4/text/TextDirectionHeuristicCompat.java @@ -0,0 +1,29 @@ +/** + * Portions copyright (c) 2016-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. + */ + +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.support.v4.text; + +public interface TextDirectionHeuristicCompat { +} diff --git a/library/libs/android-support-v4-text/src/main/java/android/support/v4/text/TextDirectionHeuristicsCompat.java b/library/libs/android-support-v4-text/src/main/java/android/support/v4/text/TextDirectionHeuristicsCompat.java new file mode 100644 index 0000000..661d8e2 --- /dev/null +++ b/library/libs/android-support-v4-text/src/main/java/android/support/v4/text/TextDirectionHeuristicsCompat.java @@ -0,0 +1,35 @@ +/** + * Portions copyright (c) 2016-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. + */ + +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.support.v4.text; + +public final class TextDirectionHeuristicsCompat { + public static final TextDirectionHeuristicCompat ANYRTL_LTR = null; + public static final TextDirectionHeuristicCompat FIRSTSTRONG_LTR = null; + public static final TextDirectionHeuristicCompat FIRSTSTRONG_RTL = null; + public static final TextDirectionHeuristicCompat LOCALE = null; + public static final TextDirectionHeuristicCompat LTR = null; + public static final TextDirectionHeuristicCompat RTL = null; +} diff --git a/library/libs/android-text/.gitignore b/library/libs/android-text/.gitignore new file mode 100644 index 0000000..cba8ae6 --- /dev/null +++ b/library/libs/android-text/.gitignore @@ -0,0 +1,8 @@ +.gradle +.DS_Store +.idea +build/ +local.properties +localhost/ +obj/ +*.iml diff --git a/library/libs/android-text/build.gradle b/library/libs/android-text/build.gradle new file mode 100644 index 0000000..1ca9842 --- /dev/null +++ b/library/libs/android-text/build.gradle @@ -0,0 +1,10 @@ +apply plugin: 'java' + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 + +jar { + baseName = 'android-text' + version = '1.0' +} + diff --git a/library/libs/android-text/src/main/java/android/text/Layout.java b/library/libs/android-text/src/main/java/android/text/Layout.java new file mode 100644 index 0000000..5b1f78d --- /dev/null +++ b/library/libs/android-text/src/main/java/android/text/Layout.java @@ -0,0 +1,36 @@ +/** + * Portions copyright (c) 2016-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. + */ + +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.text; + +public class Layout { + public enum Alignment { + ALIGN_NORMAL, + ALIGN_OPPOSITE, + ALIGN_CENTER, + ALIGN_LEFT, + ALIGN_RIGHT, + } +} diff --git a/library/libs/android-text/src/main/java/android/text/StaticLayout.java b/library/libs/android-text/src/main/java/android/text/StaticLayout.java new file mode 100644 index 0000000..426c371 --- /dev/null +++ b/library/libs/android-text/src/main/java/android/text/StaticLayout.java @@ -0,0 +1,37 @@ +/** + * Portions copyright (c) 2016-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. + */ + +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.text; + +public class StaticLayout { + public StaticLayout(CharSequence source, int bufstart, int bufend, + TextPaint paint, int outerwidth, + Layout.Alignment align, TextDirectionHeuristic textDir, + float spacingmult, float spacingadd, + boolean includepad, + TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) { + throw new RuntimeException("Stub!"); + } +} diff --git a/library/libs/android-text/src/main/java/android/text/TextDirectionHeuristic.java b/library/libs/android-text/src/main/java/android/text/TextDirectionHeuristic.java new file mode 100644 index 0000000..8e08636 --- /dev/null +++ b/library/libs/android-text/src/main/java/android/text/TextDirectionHeuristic.java @@ -0,0 +1,29 @@ +/** + * Portions copyright (c) 2016-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. + */ + +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.text; + +public interface TextDirectionHeuristic { +} diff --git a/library/libs/android-text/src/main/java/android/text/TextDirectionHeuristics.java b/library/libs/android-text/src/main/java/android/text/TextDirectionHeuristics.java new file mode 100644 index 0000000..b3de179 --- /dev/null +++ b/library/libs/android-text/src/main/java/android/text/TextDirectionHeuristics.java @@ -0,0 +1,35 @@ +/** + * Portions copyright (c) 2016-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. + */ + +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.text; + +public class TextDirectionHeuristics { + public static final TextDirectionHeuristic LTR = null; + public static final TextDirectionHeuristic FIRSTSTRONG_LTR = null; + public static final TextDirectionHeuristic RTL = null; + public static final TextDirectionHeuristic FIRSTSTRONG_RTL = null; + public static final TextDirectionHeuristic ANYRTL_LTR = null; + public static final TextDirectionHeuristic LOCALE = null; +} diff --git a/library/libs/android-text/src/main/java/android/text/TextPaint.java b/library/libs/android-text/src/main/java/android/text/TextPaint.java new file mode 100644 index 0000000..82a1f05 --- /dev/null +++ b/library/libs/android-text/src/main/java/android/text/TextPaint.java @@ -0,0 +1,29 @@ +/** + * Portions copyright (c) 2016-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. + */ + +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.text; + +public class TextPaint { +} diff --git a/library/libs/android-text/src/main/java/android/text/TextUtils.java b/library/libs/android-text/src/main/java/android/text/TextUtils.java new file mode 100644 index 0000000..a75e7e4 --- /dev/null +++ b/library/libs/android-text/src/main/java/android/text/TextUtils.java @@ -0,0 +1,36 @@ +/** + * Portions copyright (c) 2016-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. + */ + +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.text; + +public class TextUtils { + public enum TruncateAt { + START, + MIDDLE, + END, + MARQUEE, + END_SMALL + } +} diff --git a/library/libs/proxy/.gitignore b/library/libs/proxy/.gitignore new file mode 100644 index 0000000..cba8ae6 --- /dev/null +++ b/library/libs/proxy/.gitignore @@ -0,0 +1,8 @@ +.gradle +.DS_Store +.idea +build/ +local.properties +localhost/ +obj/ +*.iml diff --git a/library/libs/proxy/build.gradle b/library/libs/proxy/build.gradle new file mode 100644 index 0000000..0d6d727 --- /dev/null +++ b/library/libs/proxy/build.gradle @@ -0,0 +1,32 @@ +apply plugin: 'java' + +dependencies { + compileOnly project(':library:libs:android-text') + compileOnly project(':library:libs:android-support-v4-text') +} + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 + +jar { + baseName = 'staticlayout-proxy' + version = '1.0' +} + +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource + exclude '**/BUCK' +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +apply from: rootProject.file('release-sonatype.gradle') diff --git a/library/libs/proxy/gradle.properties b/library/libs/proxy/gradle.properties new file mode 100644 index 0000000..d7390bd --- /dev/null +++ b/library/libs/proxy/gradle.properties @@ -0,0 +1,7 @@ +GROUP=com.facebook.fbui.textlayoutbuilder +VERSION_NAME=1.0 + +POM_NAME=StaticLayoutProxy +POM_DESCRIPTION=Helper to access hidden StaticLayout constructor in Android +POM_ARTIFACT_ID=staticlayout-proxy +POM_PACKAGING=jar diff --git a/library/libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy/StaticLayoutProxy.java b/library/libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy/StaticLayoutProxy.java new file mode 100644 index 0000000..6a5a164 --- /dev/null +++ b/library/libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy/StaticLayoutProxy.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder.proxy; + +import java.lang.CharSequence; + +import android.support.v4.text.TextDirectionHeuristicCompat; +import android.support.v4.text.TextDirectionHeuristicsCompat; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.Layout; +import android.text.TextUtils; +import android.text.TextDirectionHeuristic; +import android.text.TextDirectionHeuristics; + +public class StaticLayoutProxy { + public static StaticLayout create( + CharSequence text, + int start, + int end, + TextPaint paint, + int width, + Layout.Alignment alignment, + float spacingMult, + float spacingAdd, + boolean includePadding, + TextUtils.TruncateAt ellipsize, + int ellipsisWidth, + int maxLines, + TextDirectionHeuristicCompat textDirection) { + return new StaticLayout( + text, + start, + end, + paint, + width, + alignment, + fromTextDirectionHeuristicCompat(textDirection), + spacingMult, + spacingAdd, + includePadding, + ellipsize, + ellipsisWidth, + maxLines); + } + + private static TextDirectionHeuristic fromTextDirectionHeuristicCompat( + TextDirectionHeuristicCompat textDirection) { + if (textDirection == TextDirectionHeuristicsCompat.LTR) { + return TextDirectionHeuristics.LTR; + } else if (textDirection == TextDirectionHeuristicsCompat.RTL) { + return TextDirectionHeuristics.RTL; + } else if (textDirection == TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR) { + return TextDirectionHeuristics.FIRSTSTRONG_LTR; + } else if (textDirection == TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL) { + return TextDirectionHeuristics.FIRSTSTRONG_RTL; + } else if (textDirection == TextDirectionHeuristicsCompat.ANYRTL_LTR) { + return TextDirectionHeuristics.ANYRTL_LTR; + } else if (textDirection == TextDirectionHeuristicsCompat.LOCALE) { + return TextDirectionHeuristics.LOCALE; + } else { + return TextDirectionHeuristics.FIRSTSTRONG_LTR; + } + } +} diff --git a/library/src/javadoc/stylesheet.css b/library/src/javadoc/stylesheet.css new file mode 100644 index 0000000..96e35e7 --- /dev/null +++ b/library/src/javadoc/stylesheet.css @@ -0,0 +1,571 @@ +/* Javadoc style sheet */ +/* +Overall document style +*/ + +body { + background-color: #ffffff; + color: #4B4F56; + font-family: -apple-system, Arial, Helvetica, sans-serif; + font-size: 14px; + margin: 0; +} +a:link, a:visited { + text-decoration: none; + color: #34255D; +} +a:hover, a:focus { + text-decoration: none; + color: #6A51B2; +} +a:active { + text-decoration: none; + color: #34255D; +} +a[name] { + color: #1D2129; +} +a[name]:hover { + text-decoration: none; + color: #1D2129; +} +pre { + font-family: Consolas, Menlo, Monaco, monospace; + font-size: 14px; +} +h1 { + font-size: 20px; +} +h2 { + font-size: 18px; +} +h3 { + font-size: 16px; + font-style: italic; +} +h4 { + font-size: 13px; +} +h5 { + font-size: 12px; +} +h6 { + font-size: 11px; +} +ul { + list-style-type: disc; +} +code, tt { + font-family: Consolas, Menlo, Monaco, monospace; + font-size: 14px; + padding-top: 4px; + margin-top: 8px; + line-height: 1.4em; +} +dt code { + font-family: Consolas, Menlo, Monaco, monospace; + font-size: 14px; + padding-top: 4px; +} +table tr td dt code { + font-family: Consolas, Menlo, Monaco, monospace; + font-size: 14px; + vertical-align: top; + padding-top: 4px; +} +sup { + font-size: 8px; +} +/* +Document title and Copyright styles +*/ +.clear { + clear: both; + height: 0px; + overflow: hidden; +} +.aboutLanguage { + float: right; + padding: 0px 21px; + font-size: 11px; + z-index: 200; + margin-top: -9px; +} +.legalCopy { + margin-left: .5em; +} +.bar a, .bar a:link, .bar a:visited, .bar a:active { + color: #FFFFFF; + text-decoration: none; +} +.bar a:hover, .bar a:focus { + color: #9D87D2; +} +.tab { + background-color: #0066FF; + color: #ffffff; + padding: 8px; + width: 5em; + font-weight: bold; +} +/* +Navigation bar styles +*/ +.bar { + background-color: #6A51B2; + color: #FFFFFF; + padding: .8em .5em .4em .8em; + height: auto;/*height: 1.8em;*/ + font-size: 11px; + margin: 0; +} +.topNav { + background-color: #6A51B2; + color: #FFFFFF; + float: left; + padding: 0; + width: 100%; + clear: right; + height: 2.8em; + padding-top: 10px; + overflow: hidden; + font-size: 12px; +} +.bottomNav { + margin-top: 10px; + background-color: #6A51B2; + color: #FFFFFF; + float: left; + padding: 0; + width: 100%; + clear: right; + height: 2.8em; + padding-top: 10px; + overflow: hidden; + font-size: 12px; +} +.subNav { + background-color: #E9EBEE; + float: left; + width: 100%; + overflow: hidden; + font-size: 12px; +} +.subNav div { + clear: left; + float: left; + padding: 0 0 5px 6px; + text-transform: uppercase; +} +ul.navList, ul.subNavList { + float: left; + margin: 0 25px 0 0; + padding: 0; +} +ul.navList li{ + list-style: none; + float: left; + padding: 5px 6px; + text-transform: uppercase; +} +ul.subNavList li{ + list-style: none; + float: left; +} +.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { + color: #FFFFFF; + text-decoration: none; + text-transform: uppercase; +} +.topNav a:hover, .bottomNav a:hover { + text-decoration: none; + color: #9D87D2; + text-transform: uppercase; +} +.navBarCell1Rev { + background-color: #DDD5F0; + color: #253441; + margin: auto 5px; +} +.skipNav { + position: absolute; + top: auto; + left: -9999px; + overflow: hidden; +} +/* +Page header and footer styles +*/ +.header, .footer { + clear: both; + margin: 0 20px; + padding: 5px 0 0 0; +} +.indexHeader { + margin: 10px; + position: relative; +} +.indexHeader span{ + margin-right: 15px; +} +.indexHeader h1 { + font-size: 13px; +} +.title { + color: #1D2129; + margin: 10px 0; +} +.subTitle { + margin: 5px 0 0 0; +} +.header ul { + margin: 0 0 15px 0; + padding: 0; +} +.footer ul { + margin: 20px 0 5px 0; +} +.header ul li, .footer ul li { + list-style: none; + font-size: 13px; +} +/* +Heading styles +*/ +div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { + background-color: #E9EBEE; + border: 1px solid #E9EBEE; + margin: 0 0 6px -8px; + padding: 7px 5px; +} +ul.blockList ul.blockList ul.blockList li.blockList h3 { + background-color: #E9EBEE; + border: 1px solid #E9EBEE; + margin: 0 0 6px -8px; + padding: 7px 5px; +} +ul.blockList ul.blockList li.blockList h3 { + padding: 0; + margin: 15px 0; +} +ul.blockList li.blockList h2 { + padding: 0px 0 20px 0; +} +/* +Page layout container styles +*/ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { + clear: both; + padding: 10px 20px; + position: relative; +} +.indexContainer { + margin: 10px; + position: relative; + font-size: 12px; +} +.indexContainer h2 { + font-size: 13px; + padding: 0 0 3px 0; +} +.indexContainer ul { + margin: 0; + padding: 0; +} +.indexContainer ul li { + list-style: none; + padding-top: 2px; +} +.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { + font-size: 12px; + font-weight: bold; + margin: 10px 0 0 0; + color: #1D2129; +} +.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { + margin: 5px 0 10px 0px; + font-size: 14px; + font-family: Consolas, Menlo, Monaco, monospace; +} +.serializedFormContainer dl.nameValue dt { + margin-left: 1px; + font-size: 1.1em; + display: inline; + font-weight: bold; +} +.serializedFormContainer dl.nameValue dd { + margin: 0 0 0 1px; + font-size: 1.1em; + display: inline; +} +/* +List styles +*/ +ul.horizontal li { + display: inline; + font-size: 0.9em; +} +ul.inheritance { + margin: 0; + padding: 0; +} +ul.inheritance li { + display: inline; + list-style: none; +} +ul.inheritance li ul.inheritance { + margin-left: 15px; + padding-left: 15px; + padding-top: 1px; +} +ul.blockList, ul.blockListLast { + margin: 10px 0 10px 0; + padding: 0; +} +ul.blockList li.blockList, ul.blockListLast li.blockList { + list-style: none; + margin-bottom: 15px; + line-height: 1.4; +} +ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { + padding: 0px 20px 5px 10px; + border: 1px solid #ededed; + background-color: #f8f8f8; +} +ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { + padding: 0 0 5px 8px; + background-color: #ffffff; + border: none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { + margin-left: 0; + padding-left: 0; + padding-bottom: 15px; + border: none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { + list-style: none; + border-bottom: none; + padding-bottom: 0; +} +table tr td dl, table tr td dl dt, table tr td dl dd { + margin-top: 0; + margin-bottom: 1px; +} +/* +Table styles +*/ +.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { + width: 100%; + border-left: 1px solid #E9EBEE; + border-right: 1px solid #E9EBEE; + border-bottom: 1px solid #E9EBEE; +} +.overviewSummary, .memberSummary { + padding: 0px; +} +.overviewSummary caption, .memberSummary caption, .typeSummary caption, +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption { + position: relative; + text-align: left; + background-repeat: no-repeat; + color: #4B4F56; + font-weight: bold; + clear: none; + overflow: hidden; + padding: 0px; + padding-top: 10px; + padding-left: 1px; + margin: 0px; + white-space: pre; +} +.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, +.useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, +.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, +.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, +.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, +.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { + color: #FFFFFF; +} +.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { + white-space: nowrap; + padding-top: 5px; + padding-left: 12px; + padding-right: 12px; + padding-bottom: 7px; + display: inline-block; + float: left; + background-color: #DDD5F0; + border: none; + height: 16px; +} +.memberSummary caption span.activeTableTab span { + white-space: nowrap; + padding-top: 5px; + padding-left: 12px; + padding-right: 12px; + margin-right: 3px; + display: inline-block; + float: left; + background-color: #DDD5F0; + height: 16px; +} +.memberSummary caption span.tableTab span { + white-space: nowrap; + padding-top: 5px; + padding-left: 12px; + padding-right: 12px; + margin-right: 3px; + display: inline-block; + float: left; + background-color: #6A51B2; + height: 16px; +} +.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { + padding-top: 0px; + padding-left: 0px; + padding-right: 0px; + background-image: none; + float: none; + display: inline; +} +.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { + display: none; + width: 5px; + position: relative; + float: left; + background-color: #DDD5F0; +} +.memberSummary .activeTableTab .tabEnd { + display: none; + width: 5px; + margin-right: 3px; + position: relative; + float: left; + background-color: #DDD5F0; +} +.memberSummary .tableTab .tabEnd { + display: none; + width: 5px; + margin-right: 3px; + position: relative; + background-color: #6A51B2; + float: left; + +} +.overviewSummary td, .memberSummary td, .typeSummary td, +.useSummary td, .constantsSummary td, .deprecatedSummary td { + text-align: left; + padding: 0px 0px 12px 10px; +} +th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, +td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ + vertical-align: top; + padding-right: 0px; + padding-top: 8px; + padding-bottom: 3px; +} +th.colFirst, th.colLast, th.colOne, .constantsSummary th { + background: #E9EBEE; + text-align: left; + padding: 8px 3px 3px 7px; +} +td.colFirst, th.colFirst { + white-space: nowrap; + font-size: 13px; +} +td.colLast, th.colLast { + font-size: 13px; +} +td.colOne, th.colOne { + font-size: 13px; +} +.overviewSummary td.colFirst, .overviewSummary th.colFirst, +.useSummary td.colFirst, .useSummary th.colFirst, +.overviewSummary td.colOne, .overviewSummary th.colOne, +.memberSummary td.colFirst, .memberSummary th.colFirst, +.memberSummary td.colOne, .memberSummary th.colOne, +.typeSummary td.colFirst{ + width: 25%; + vertical-align: top; +} +td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { + font-weight: bold; +} +.tableSubHeadingColor { + background-color: #EEEEFF; +} +.altColor { + background-color: #FFFFFF; +} +.rowColor { + background-color: #F6F7F9; +} +/* +Content styles +*/ +.description pre { + margin-top: 0; +} +.deprecatedContent { + margin: 0; + padding: 10px 0; +} +.docSummary { + padding: 0; +} + +ul.blockList ul.blockList ul.blockList li.blockList h3 { + font-style: normal; +} + +div.block { + font-size: 14px; +} + +td.colLast div { + padding-top: 0px; +} + + +td.colLast a { + padding-bottom: 3px; +} +/* +Formatting effect styles +*/ +.sourceLineNo { + color: green; + padding: 0 30px 0 0; +} +h1.hidden { + visibility: hidden; + overflow: hidden; + font-size: 10px; +} +.block { + display: block; + margin: 3px 10px 2px 0px; + color: #4B4F56; +} +.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, +.overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel, +.seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink { + font-weight: bold; +} +.deprecationComment, .emphasizedPhrase, .interfaceName { + font-style: italic; +} + +div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, +div.block div.block span.interfaceName { + font-style: normal; +} + +div.contentContainer ul.blockList li.blockList h2{ + padding-bottom: 0px; +} diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0f878ae --- /dev/null +++ b/library/src/main/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + diff --git a/library/src/main/java/com/facebook/fbui/textlayoutbuilder/GlyphWarmer.java b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/GlyphWarmer.java new file mode 100644 index 0000000..b6b0d10 --- /dev/null +++ b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/GlyphWarmer.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder; + +import android.text.Layout; + +/** + * Specifies an interface that a class has to implement + * to warm the text {@link Layout} in the background. + * This approach helps in drawing text in post Android 4.0 devices. + */ +public interface GlyphWarmer { + /** + * Warms the text layout. + * + * @param layout The layout + */ + public void warmLayout(Layout layout); +} diff --git a/library/src/main/java/com/facebook/fbui/textlayoutbuilder/ResourceTextLayoutHelper.java b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/ResourceTextLayoutHelper.java new file mode 100644 index 0000000..55cd831 --- /dev/null +++ b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/ResourceTextLayoutHelper.java @@ -0,0 +1,214 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.Typeface; +import android.support.annotation.AttrRes; +import android.support.annotation.StyleRes; +import android.text.TextUtils; +import android.util.AttributeSet; + +/** + * An utility class to update a {@link TextLayoutBuilder} from an Android resource. + */ +public class ResourceTextLayoutHelper { + + // Font size in pixels. + private static final int DEFAULT_TEXT_SIZE_PX = 15; + + /** + * Sets the values for a TextLayoutBuilder from a style resource. + * + * @param builder The TextLayoutBuilder + * @param context The Context to use for resolving the attributes + * @param styleRes The style resource identifier + */ + public static void updateFromStyleResource( + TextLayoutBuilder builder, + Context context, + @StyleRes int styleRes) { + updateFromStyleResource(builder, context, 0, styleRes); + } + + /** + * Sets the values for a TextLayoutBuilder from a style resource or a themed attribute. + * + * @param builder The TextLayoutBuilder + * @param context The Context to use for resolving the attributes + * @param styleAttr The themed style attribute + * @param styleRes The style resource identifier + */ + public static void updateFromStyleResource( + TextLayoutBuilder builder, + Context context, + @AttrRes int styleAttr, + @StyleRes int styleRes) { + updateFromStyleResource(builder, context, null, styleAttr, styleRes); + } + + /** + * Sets the values for a TextLayoutBuilder from a style resource or a themed attribute. + * + * @param builder The TextLayoutBuilder + * @param context The Context to use for resolving the attributes + * @param attrs The {@link AttributeSet} used during inflation + * @param styleAttr The themed style attribute + * @param styleRes The style resource identifier + */ + public static void updateFromStyleResource( + TextLayoutBuilder builder, + Context context, + AttributeSet attrs, + @AttrRes int styleAttr, + @StyleRes int styleRes) { + TypedArray customAttrs = context.obtainStyledAttributes( + attrs, + R.styleable.TextStyle, + styleAttr, + styleRes); + + int textAppearanceId = customAttrs.getResourceId( + R.styleable.TextStyle_android_textAppearance, + -1); + + if (textAppearanceId > 0) { + setTextAppearance(builder, context, textAppearanceId); + } + + ColorStateList textColor = customAttrs.getColorStateList( + R.styleable.TextStyle_android_textColor); + + int textSize = customAttrs.getDimensionPixelSize( + R.styleable.TextStyle_android_textSize, + DEFAULT_TEXT_SIZE_PX); + + int shadowColor = customAttrs.getInt( + R.styleable.TextStyle_android_shadowColor, + Color.TRANSPARENT); + + float dx = customAttrs.getFloat( + R.styleable.TextStyle_android_shadowDx, + 0.0f); + + float dy = customAttrs.getFloat( + R.styleable.TextStyle_android_shadowDy, + 0.0f); + + float radius = customAttrs.getFloat( + R.styleable.TextStyle_android_shadowRadius, + 0.0f); + + int textStyle = customAttrs.getInt( + R.styleable.TextStyle_android_textStyle, + -1); + + int ellipsize = customAttrs.getInt( + R.styleable.TextStyle_android_ellipsize, + 0); + + boolean singleLine = customAttrs.getBoolean( + R.styleable.TextStyle_android_singleLine, + false); + + int maxLines = customAttrs.getInt( + R.styleable.TextStyle_android_maxLines, + TextLayoutBuilder.DEFAULT_MAX_LINES); + + customAttrs.recycle(); + + builder.setTextColor(textColor); + + builder.setTextSize(textSize); + builder.setShadowLayer(radius, dx, dy, shadowColor); + + if (textStyle != -1) { + builder.setTypeface(Typeface.defaultFromStyle(textStyle)); + } else { + builder.setTypeface(null); + } + + if (ellipsize > 0 && ellipsize < 4) { + // TruncateAt doesn't have a value for NONE. + builder.setEllipsize(TextUtils.TruncateAt.values()[ellipsize - 1]); + } else { + builder.setEllipsize(null); + } + + builder.setSingleLine(singleLine); + builder.setMaxLines(maxLines); + } + + /** + * Sets a text appearance for the layout. + * + * @param builder The {@link TextLayoutBuilder} instance + * @param context The {@link Context} to use for resolving attributes + * @param resId The resource identifier of the text appearance + */ + public static void setTextAppearance( + TextLayoutBuilder builder, + Context context, + @StyleRes int resId) { + TypedArray customAttrs = context.obtainStyledAttributes( + resId, + R.styleable.TextAppearance); + + ColorStateList textColor = customAttrs.getColorStateList( + R.styleable.TextAppearance_android_textColor); + + int textSize = customAttrs.getDimensionPixelSize( + R.styleable.TextAppearance_android_textSize, + 0); + + int shadowColor = customAttrs.getInt( + R.styleable.TextAppearance_android_shadowColor, + Color.TRANSPARENT); + + if (shadowColor != Color.TRANSPARENT) { + float dx = customAttrs.getFloat( + R.styleable.TextAppearance_android_shadowDx, + 0.0f); + + float dy = customAttrs.getFloat( + R.styleable.TextAppearance_android_shadowDy, + 0.0f); + + float radius = customAttrs.getFloat( + R.styleable.TextAppearance_android_shadowRadius, + 0.0f); + + builder.setShadowLayer(radius, dx, dy, shadowColor); + } + + int textStyle = customAttrs.getInt( + R.styleable.TextAppearance_android_textStyle, + -1); + + customAttrs.recycle(); + + // Override the color only if available. + if (textColor != null) { + builder.setTextColor(textColor); + } + + if (textSize != 0) { + builder.setTextSize(textSize); + } + + // Override the style only if available. + if (textStyle != -1) { + builder.setTypeface(Typeface.defaultFromStyle(textStyle)); + } + } +} diff --git a/library/src/main/java/com/facebook/fbui/textlayoutbuilder/StaticLayoutHelper.java b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/StaticLayoutHelper.java new file mode 100644 index 0000000..5be7afd --- /dev/null +++ b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/StaticLayoutHelper.java @@ -0,0 +1,301 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder; + +import java.lang.reflect.Field; + +import android.support.v4.text.TextDirectionHeuristicCompat; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; + +import com.facebook.fbui.textlayoutbuilder.proxy.StaticLayoutProxy; + +/** + * Helper class to get around the {@link StaticLayout} constructor limitation in ICS. + */ +/* package */ class StaticLayoutHelper { + + // Space and ellipsis to append at the end of a string to ellipsize it + private static final String SPACE_AND_ELLIPSIS = " \u2026"; + + /** + * Returns a StaticLayout using ICS specific constructor if possible. + * + * @param text The text for the layout + * @param start The start index + * @param end The end index + * @param paint The {@link TextPaint} to be used + * @param width The width of the layout + * @param alignment The {@link Layout.Alignment} + * @param spacingMult The line spacing multiplier + * @param spacingAdd The line spacing extra + * @param includePadding Whether to include font padding + * @param ellipsize The ellipsizing behavior specified by {@link TextUtils.TruncateAt} + * @param ellipsisWidth The width of the ellipsis + * @param maxLines The maximum number of lines for this layout + * @param textDirection The text direction + * @return A {@link StaticLayout} + */ + private static StaticLayout getStaticLayoutMaybeMaxLines( + CharSequence text, + int start, + int end, + TextPaint paint, + int width, + Layout.Alignment alignment, + float spacingMult, + float spacingAdd, + boolean includePadding, + TextUtils.TruncateAt ellipsize, + int ellipsisWidth, + int maxLines, + TextDirectionHeuristicCompat textDirection) { + try { + return StaticLayoutProxy.create( + text, + start, + end, + paint, + width, + alignment, + spacingMult, + spacingAdd, + includePadding, + ellipsize, + ellipsisWidth, + maxLines, + textDirection); + } catch (LinkageError e) { + // Use the publicly available constructor. + } + + return getStaticLayoutNoMaxLines( + text, + start, + end, + paint, + width, + alignment, + spacingMult, + spacingAdd, + includePadding, + ellipsize, + ellipsisWidth); + } + + /** + * Returns a StaticLayout with no maxLines restriction. + * + * @param text The text for the layout + * @param start The start index + * @param end The end index + * @param paint The {@link TextPaint} to be used + * @param width The width of the layout + * @param alignment The {@link Layout.Alignment} + * @param spacingMult The line spacing multiplier + * @param spacingAdd The line spacing extra + * @param includePadding Whether to include font padding + * @param ellipsize The ellipsizing behavior specified by {@link TextUtils.TruncateAt} + * @param ellipsisWidth The width of the ellipsis + * @return A {@link StaticLayout} with no maxLines restriction + */ + private static StaticLayout getStaticLayoutNoMaxLines( + CharSequence text, + int start, + int end, + TextPaint paint, + int width, + Layout.Alignment alignment, + float spacingMult, + float spacingAdd, + boolean includePadding, + TextUtils.TruncateAt ellipsize, + int ellipsisWidth) { + + return new StaticLayout( + text, + start, + end, + paint, + width, + alignment, + spacingMult, + spacingAdd, + includePadding, + ellipsize, + ellipsisWidth); + } + + /** + * Creates a StaticLayout will all the required properties. + * + * @param text The text for the layout + * @param start The start index + * @param end The end index + * @param paint The {@link TextPaint} to be used + * @param width The width of the layout + * @param alignment The {@link Layout.Alignment} + * @param spacingMult The line spacing multiplier + * @param spacingAdd The line spacing extra + * @param includePadding Whether to include font padding + * @param ellipsize The ellipsizing behavior specified by {@link TextUtils.TruncateAt} + * @param ellipsisWidth The width of the ellipsis + * @param maxLines The maximum number of lines for this layout + * @param textDirection The text direction + * @return A {@link StaticLayout} + */ + public static StaticLayout make( + CharSequence text, + int start, + int end, + TextPaint paint, + int width, + Layout.Alignment alignment, + float spacingMult, + float spacingAdd, + boolean includePadding, + TextUtils.TruncateAt ellipsize, + int ellipsisWidth, + int maxLines, + TextDirectionHeuristicCompat textDirection) { + + StaticLayout layout = getStaticLayoutMaybeMaxLines( + text, + start, + end, + paint, + width, + alignment, + spacingMult, + spacingAdd, + includePadding, + ellipsize, + ellipsisWidth, + maxLines, + textDirection); + + // Returned layout may not have correct line count (either because it is not supported + // pre-ICS, or because there is a bug in Android pre-Lollipop that causes the text to span + // over more lines than we asked for). We need to manually check if that happened and + // re-create Layout with a substring that will fit into required number of lines. + if (maxLines > 0) { + while (layout.getLineCount() > maxLines) { + int newEnd = layout.getLineStart(maxLines); + if (newEnd >= end) { + // to break out of a potential infinite loop + break; + } + + // newEnd is where the next line starts, not where the previous line ends + // we need to skip over the whitespace characters to get to the end of the line + while (newEnd > start) { + if (Character.isSpace(text.charAt(newEnd - 1))) { + --newEnd; + } else { + break; + } + } + + end = newEnd; + + layout = getStaticLayoutMaybeMaxLines( + text, + start, + end, + paint, + width, + alignment, + spacingMult, + spacingAdd, + includePadding, + ellipsize, + ellipsisWidth, + maxLines, + textDirection); + + if (layout.getLineCount() >= maxLines && + layout.getEllipsisCount(maxLines - 1) == 0) { + CharSequence ellipsizedText = text.subSequence(start, end) + SPACE_AND_ELLIPSIS; + layout = getStaticLayoutMaybeMaxLines( + ellipsizedText, + 0, + ellipsizedText.length(), + paint, + width, + alignment, + spacingMult, + spacingAdd, + includePadding, + ellipsize, + ellipsisWidth, + maxLines, + textDirection); + } + } + } + + while (!fixLayout(layout)) { + // try again + } + + return layout; + } + + /** + * Attempts to fix a StaticLayout with wrong layout information + * that can result in StringIndexOutOfBoundsException during layout.draw(). + * + * @param layout The {@link StaticLayout} to fix + * @return Whether the layout was fixed or not + */ + public static boolean fixLayout(StaticLayout layout) { + int lineStart = layout.getLineStart(0); + for (int i = 0, lineCount = layout.getLineCount(); i < lineCount; ++i) { + int lineEnd = layout.getLineEnd(i); + if (lineEnd < lineStart) { + // Bug, need to swap lineStart and lineEnd + try { + Field mLinesField = StaticLayout.class.getDeclaredField("mLines"); + mLinesField.setAccessible(true); + + Field mColumnsField = StaticLayout.class.getDeclaredField("mColumns"); + mColumnsField.setAccessible(true); + + int[] mLines = (int[]) mLinesField.get(layout); + int mColumns = mColumnsField.getInt(layout); + + // swap lineStart and lineEnd by swapping all the following data: + // mLines[mColumns * i.. mColumns * i+1] <-> mLines[mColumns * (i+1)..mColumns * (i+2)] + for (int j = 0; j < mColumns; ++j) { + swap(mLines, mColumns * i + j, mColumns * i + j + mColumns); + } + } catch (Exception e) { + // something is wrong, bail out + break; + } + + // start over + return false; + } + + lineStart = lineEnd; + } + + return true; + } + + private static void swap(int[] array, int i, int j) { + int tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } +} diff --git a/library/src/main/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.java b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.java new file mode 100644 index 0000000..8daaca3 --- /dev/null +++ b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.java @@ -0,0 +1,810 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder; + +import java.lang.annotation.Retention; + +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.support.annotation.ColorInt; +import android.support.annotation.IntDef; +import android.support.annotation.Px; +import android.support.annotation.VisibleForTesting; +import android.support.v4.text.TextDirectionHeuristicCompat; +import android.support.v4.text.TextDirectionHeuristicsCompat; +import android.support.v4.util.LruCache; +import android.text.BoringLayout; +import android.text.Layout; +import android.text.Spannable; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.style.ClickableSpan; +import android.util.Log; + +import static android.text.Layout.Alignment; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * An utility class to create text {@link Layout}s easily. + *

+ * This class uses a Builder pattern to allow re-using the same object + * to create text {@link Layout}s with similar properties. + */ +public class TextLayoutBuilder { + + /** + * Measure mode constants similar to {@link android.view.View.MeasureSpec} + * + * @see #MEASURE_MODE_UNSPECIFIED + * @see #MEASURE_MODE_EXACTLY + * @see #MEASURE_MODE_AT_MOST + */ + @Retention(SOURCE) + @IntDef({MEASURE_MODE_UNSPECIFIED, MEASURE_MODE_EXACTLY, MEASURE_MODE_AT_MOST}) + public @interface MeasureMode {} + public static final int MEASURE_MODE_UNSPECIFIED = 0; + public static final int MEASURE_MODE_EXACTLY = 1; + public static final int MEASURE_MODE_AT_MOST = 2; + + // Default maxLines. + public static final int DEFAULT_MAX_LINES = Integer.MAX_VALUE; + + // Cache for text layouts. + @VisibleForTesting + static final LruCache sCache = new LruCache<>(100); + + /** + * Params for creating the layout. + */ + @VisibleForTesting + static class Params { + TextPaint paint = new ComparableTextPaint(Paint.ANTI_ALIAS_FLAG); + int width; + @MeasureMode int measureMode; + + CharSequence text; + ColorStateList color; + + float spacingMult = 1.0f; + float spacingAdd = 0.0f; + boolean includePadding = true; + + TextUtils.TruncateAt ellipsize = null; + boolean singleLine = false; + int maxLines = DEFAULT_MAX_LINES; + Alignment alignment = Alignment.ALIGN_NORMAL; + TextDirectionHeuristicCompat textDirection = + TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR; + + boolean mForceNewPaint = false; + + /** + * Create a new paint after the builder builds for the first time. + */ + void createNewPaintIfNeeded() { + // Once after build() is called, it is not safe to set properties + // on the paint as we cache the text layouts. + // Hence we create a new paint object, + // if we ever change one of the paint's properties. + if (mForceNewPaint) { + paint = new ComparableTextPaint(paint); + mForceNewPaint = false; + } + } + + @Override + public int hashCode() { + int hashCode = 1; + hashCode = 31 * hashCode + (paint != null ? paint.hashCode() : 0); + hashCode = 31 * hashCode + width; + hashCode = 31 * hashCode + measureMode; + hashCode = 31 * hashCode + Float.floatToIntBits(spacingMult); + hashCode = 31 * hashCode + Float.floatToIntBits(spacingAdd); + hashCode = 31 * hashCode + (includePadding ? 1 : 0); + hashCode = 31 * hashCode + (ellipsize != null ? ellipsize.hashCode() : 0); + hashCode = 31 * hashCode + (singleLine ? 1 : 0); + hashCode = 31 * hashCode + maxLines; + hashCode = 31 * hashCode + (alignment != null ? alignment.hashCode() : 0); + hashCode = 31 * hashCode + (textDirection != null ? textDirection.hashCode() : 0); + hashCode = 31 * hashCode + (text != null ? text.hashCode() : 0); + + return hashCode; + } + } + + // Params for the builder. + @VisibleForTesting + final Params mParams = new Params(); + + // Locally cached layout for an instance. + private Layout mSavedLayout = null; + + // Text layout glyph warmer. + private GlyphWarmer mGlyphWarmer; + + // Cache layout or not. + private boolean mShouldCacheLayout = true; + + // Warm layout or not. + private boolean mShouldWarmText = false; + + /** + * Sets the intended width of the text layout. + * + * @param width The width of the text layout + * @return This {@link TextLayoutBuilder} instance + * @see #setWidth(int, int) + */ + public TextLayoutBuilder setWidth(@Px int width) { + return setWidth( + width, + width <= 0 ? MEASURE_MODE_UNSPECIFIED : MEASURE_MODE_EXACTLY); + } + + /** + * Sets the intended width of the text layout while respecting the measure mode. + * + * @param width The width of the text layout + * @param measureMode The mode with which to treat the given width + * @return This {@link TextLayoutBuilder} instance + * @see #setWidth(int) + */ + public TextLayoutBuilder setWidth(@Px int width, @MeasureMode int measureMode) { + if (mParams.width != width || mParams.measureMode != measureMode) { + mParams.width = width; + mParams.measureMode = measureMode; + mSavedLayout = null; + } + return this; + } + + /** + * Returns the text that would be packed in a layout by this TextLayoutBuilder. + * + * @return The text used by this TextLayoutBuilder + */ + public CharSequence getText() { + return mParams.text; + } + + /** + * Sets the text for the layout. + * + * @param text The text for the layout + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setText(CharSequence text) { + if (text == mParams.text || + (text != null && mParams.text != null && text.equals(mParams.text))) { + return this; + } + mParams.text = text; + mSavedLayout = null; + return this; + } + + /** + * Returns the text size for this TextLayoutBuilder. + * + * @return The text size used by this TextLayoutBuilder + */ + public float getTextSize() { + return mParams.paint.getTextSize(); + } + + /** + * Sets the text size for the layout. + * + * @param size The text size in pixels + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setTextSize(int size) { + if (mParams.paint.getTextSize() != size) { + mParams.createNewPaintIfNeeded(); + mParams.paint.setTextSize(size); + mSavedLayout = null; + } + return this; + } + + /** + * Returns the text color for this TextLayoutBuilder. + * + * @return The text color used by this TextLayoutBuilder + */ + @ColorInt + public int getTextColor() { + return mParams.paint.getColor(); + } + + /** + * Sets the text color for the layout. + * + * @param color The text color for the layout + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setTextColor(@ColorInt int color) { + mParams.createNewPaintIfNeeded(); + mParams.color = null; + mParams.paint.setColor(color); + mSavedLayout = null; + return this; + } + + /** + * Sets the text color for the layout. + * + * @param colorStateList The text color state list for the layout + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setTextColor(ColorStateList colorStateList) { + mParams.createNewPaintIfNeeded(); + mParams.color = colorStateList; + mParams.paint.setColor(mParams.color != null ? mParams.color.getDefaultColor() : Color.BLACK); + mSavedLayout = null; + return this; + } + + /** + * Returns the link color for this TextLayoutBuilder. + * + * @return The link color used by this TextLayoutBuilder + */ + @ColorInt + public int getLinkColor() { + return mParams.paint.linkColor; + } + + /** + * Sets the link color for the text in the layout. + * + * @param linkColor The link color + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setLinkColor(@ColorInt int linkColor) { + if (mParams.paint.linkColor != linkColor) { + mParams.createNewPaintIfNeeded(); + mParams.paint.linkColor = linkColor; + mSavedLayout = null; + } + return this; + } + + /** + * Returns the text spacing extra for this TextLayoutBuilder. + * + * @return The text spacing extra used by this TextLayoutBuilder + */ + public float getTextSpacingExtra() { + return mParams.spacingAdd; + } + + /** + * Sets the text extra spacing for the layout. + * + * @param spacingExtra the extra space that is added to the height of each line + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setTextSpacingExtra(float spacingExtra) { + if (mParams.spacingAdd != spacingExtra) { + mParams.spacingAdd = spacingExtra; + mSavedLayout = null; + } + return this; + } + + /** + * Returns the text spacing multiplier for this TextLayoutBuilder. + * + * @return The text spacing multiplier used by this TextLayoutBuilder + */ + public float getTextSpacingMultiplier() { + return mParams.spacingMult; + } + + /** + * Sets the line spacing multiplier for the layout. + * + * @param spacingMultiplier the value by which each line's height is multiplied + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setTextSpacingMultiplier(float spacingMultiplier) { + if (mParams.spacingMult != spacingMultiplier) { + mParams.spacingMult = spacingMultiplier; + mSavedLayout = null; + } + return this; + } + + /** + * Returns whether this TextLayoutBuilder should include font padding. + * + * @return Whether this TextLayoutBuilder should include font padding + */ + public boolean getIncludeFontPadding() { + return mParams.includePadding; + } + + /** + * Set whether the text Layout includes extra top and bottom padding to make + * room for accents that go above the normal ascent and descent. + *

+ * The default is true. + * + * @param shouldInclude Whether to include font padding or not + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setIncludeFontPadding(boolean shouldInclude) { + if (mParams.includePadding != shouldInclude) { + mParams.includePadding = shouldInclude; + mSavedLayout = null; + } + return this; + } + + /** + * Returns the text alignment for this TextLayoutBuilder. + * + * @return The text alignment used by this TextLayoutBuilder + */ + public Alignment getAlignment() { + return mParams.alignment; + } + + /** + * Sets text alignment for the layout. + * + * @param alignment The text alignment for the layout + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setAlignment(Alignment alignment) { + if (mParams.alignment != alignment) { + mParams.alignment = alignment; + mSavedLayout = null; + } + return this; + } + + /** + * Returns the text direction for this TextLayoutBuilder. + * + * @return The text direction used by this TextLayoutBuilder + */ + public TextDirectionHeuristicCompat getTextDirection() { + return mParams.textDirection; + } + + /** + * Sets the text direction heuristic for the layout. + *

+ * TextDirectionHeuristicCompat describes how to evaluate the text + * of this Layout to know whether to use RTL or LTR text direction + * + * @param textDirection The text direction heuristic for the layout + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setTextDirection(TextDirectionHeuristicCompat textDirection) { + if (mParams.textDirection != textDirection) { + mParams.textDirection = textDirection; + mSavedLayout = null; + } + return this; + } + + /** + * Sets the shadow layer for the layout. + * + * @param radius The radius of the blur for shadow + * @param dx The horizontal translation of the origin + * @param dy The vertical translation of the origin + * @param color The shadow color + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setShadowLayer(float radius, float dx, float dy, @ColorInt int color) { + mParams.createNewPaintIfNeeded(); + mParams.paint.setShadowLayer(radius, dx, dy, color); + mSavedLayout = null; + return this; + } + + /** + * Sets a text style for the layout. + * + * @param style The text style for the layout + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setTextStyle(int style) { + return setTypeface(Typeface.defaultFromStyle(style)); + } + + /** + * Returns the typeface for this TextLayoutBuilder. + * + * @return The typeface used by this TextLayoutBuilder + */ + public Typeface getTypeface() { + return mParams.paint.getTypeface(); + } + + /** + * Sets the typeface used by this TextLayoutBuilder. + * + * @param typeface The typeface for this TextLayoutBuilder + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setTypeface(Typeface typeface) { + if (mParams.paint.getTypeface() != typeface) { + mParams.createNewPaintIfNeeded(); + mParams.paint.setTypeface(typeface); + mSavedLayout = null; + } + return this; + } + + /** + * Returns the drawable state for this TextLayoutBuilder. + * + * @return The drawable state used by this TextLayoutBuilder + */ + public int[] getDrawableState() { + return mParams.paint.drawableState; + } + + /** + * Updates the text colors based on the drawable state. + * + * @param drawableState The current drawable state of the View holding this layout + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setDrawableState(int[] drawableState) { + mParams.createNewPaintIfNeeded(); + mParams.paint.drawableState = drawableState; + + if (mParams.color != null && mParams.color.isStateful()) { + int color = mParams.color.getColorForState(drawableState, 0); + mParams.paint.setColor(color); + mSavedLayout = null; + } + return this; + } + + /** + * Returns the text ellipsize for this TextLayoutBuilder. + * + * @return The text ellipsize used by this TextLayoutBuilder + */ + public TextUtils.TruncateAt getEllipsize() { + return mParams.ellipsize; + } + + /** + * Sets the ellipsis location for the layout. + * + * @param ellipsize The ellipsis location in the layout + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setEllipsize(TextUtils.TruncateAt ellipsize) { + if (mParams.ellipsize != ellipsize) { + mParams.ellipsize = ellipsize; + mSavedLayout = null; + } + return this; + } + + /** + * Returns whether the TextLayoutBuilder should show a single line. + * + * @return Whether the TextLayoutBuilder should show a single line or not + */ + public boolean getSingleLine() { + return mParams.singleLine; + } + + /** + * Sets whether the text should be in a single line or not. + * + * @param singleLine Whether the text should be in a single line or not + * @return This {@link TextLayoutBuilder} instance + * @see #setMaxLines(int) + */ + public TextLayoutBuilder setSingleLine(boolean singleLine) { + if (mParams.singleLine != singleLine) { + mParams.singleLine = singleLine; + mSavedLayout = null; + } + return this; + } + + /** + * Returns the number of max lines used by this TextLayoutBuilder. + * + * @return The number of max lines for this TextLayoutBuilder + */ + public int getMaxLines() { + return mParams.maxLines; + } + + /** + * Sets a maximum number of lines to be shown by the Layout. + *

+ * Note: Gingerbread always default to two lines max when ellipsized. This cannot be changed. + * Use a TextView if you want more control over the number of lines. + * + * @param maxLines The number of maxLines to show in this Layout + * @return This {@link TextLayoutBuilder} instance + * @see #setSingleLine(boolean) + */ + public TextLayoutBuilder setMaxLines(int maxLines) { + if (mParams.maxLines != maxLines) { + mParams.maxLines = maxLines; + mSavedLayout = null; + } + return this; + } + + /** + * Returns whether the TextLayoutBuilder should cache the layout. + * + * @return Whether the TextLayoutBuilder should cache the layout + */ + public boolean getShouldCacheLayout() { + return mShouldCacheLayout; + } + + /** + * Sets whether the text layout should be cached or not. + *

+ * Note: If the Layout contains {@link ClickableSpan}s, the layout will not be cached. + * + * @param shouldCacheLayout True to cache the text layout, false otherwise + * @return This {@link TextLayoutBuilder} instance + */ + public TextLayoutBuilder setShouldCacheLayout(boolean shouldCacheLayout) { + mShouldCacheLayout = shouldCacheLayout; + return this; + } + + /** + * Returns whether the TextLayoutBuilder should warm the layout. + * + * @return Whether the TextLayoutBuilder should warm the layout + */ + public boolean getShouldWarmText() { + return mShouldWarmText; + } + + /** + * Sets whether the text should be warmed or not. + *

+ * Note: Setting this true is highly effective for large blurbs of text. + * This method has to be called before the draw pass. + * + * @param shouldWarmText True to warm the text layout, false otherwise + * @return This {@link TextLayoutBuilder} instance + * @see #setGlyphWarmer(GlyphWarmer) + */ + public TextLayoutBuilder setShouldWarmText(boolean shouldWarmText) { + mShouldWarmText = shouldWarmText; + return this; + } + + /** + * Returns the GlyphWarmer used by the TextLayoutBuilder. + * + * @return The GlyphWarmer for this TextLayoutBuilder + */ + public GlyphWarmer getGlyphWarmer() { + return mGlyphWarmer; + } + + /** + * Sets the glyph warmer to use. + * + * @param glyphWarmer GlyphWarmer to use to warm the text layout + * @return This {@link TextLayoutBuilder} instance + * @see #setShouldWarmText(boolean) + */ + public TextLayoutBuilder setGlyphWarmer(GlyphWarmer glyphWarmer) { + mGlyphWarmer = glyphWarmer; + return this; + } + + /** + * Builds and returns a {@link Layout}. + * + * @return A {@link Layout} based on the parameters set + */ + public Layout build() { + // Return the cached layout if no property changed. + if (mShouldCacheLayout && mSavedLayout != null) { + return mSavedLayout; + } + + if (TextUtils.isEmpty(mParams.text)) { + return null; + } + + boolean hasClickableSpans = false; + int hashCode = -1; + + if (mShouldCacheLayout && mParams.text instanceof Spannable) { + ClickableSpan[] spans = ((Spannable) mParams.text).getSpans( + 0, + mParams.text.length() - 1, + ClickableSpan.class); + hasClickableSpans = spans.length > 0; + } + + // If the text has ClickableSpans, it will be bound to different + // click listeners each time. It is unsafe to cache these text Layouts. + // Hence they will not be in cache. + if (mShouldCacheLayout && !hasClickableSpans) { + hashCode = mParams.hashCode(); + Layout cachedLayout = sCache.get(hashCode); + if (cachedLayout != null) { + return cachedLayout; + } + } + + BoringLayout.Metrics metrics = null; + + int numLines = mParams.singleLine ? 1 : mParams.maxLines; + + // Try creating a boring layout only if singleLine is requested. + if (numLines == 1) { + metrics = BoringLayout.isBoring(mParams.text, mParams.paint); + } + + // getDesiredWidth here is used to ensure we layout text at the same size which it is measured. + // If we used a large static value it would break RTL due to drawing text at the very end of the + // large value. + int width; + switch (mParams.measureMode) { + case MEASURE_MODE_UNSPECIFIED: + width = (int) Math.ceil(Layout.getDesiredWidth(mParams.text, mParams.paint)); + break; + case MEASURE_MODE_EXACTLY: + width = mParams.width; + break; + case MEASURE_MODE_AT_MOST: + width = + Math.min( + (int) Math.ceil(Layout.getDesiredWidth(mParams.text, mParams.paint)), + mParams.width); + break; + default: + throw new IllegalStateException("Unexpected measure mode " + mParams.measureMode); + } + + Layout layout; + if (metrics != null) { + layout = BoringLayout.make( + mParams.text, + mParams.paint, + width, + mParams.alignment, + mParams.spacingMult, + mParams.spacingAdd, + metrics, + mParams.includePadding, + mParams.ellipsize, + width); + } else { + while (true) { + try { + layout = StaticLayoutHelper.make( + mParams.text, + 0, + mParams.text.length(), + mParams.paint, + width, + mParams.alignment, + mParams.spacingMult, + mParams.spacingAdd, + mParams.includePadding, + mParams.ellipsize, + width, + numLines, + mParams.textDirection); + } catch (IndexOutOfBoundsException e) { + // Workaround for https://code.google.com/p/android/issues/detail?id=35412 + if (!(mParams.text instanceof String)) { + // remove all Spannables and re-try + Log.e("TextLayoutBuilder", "Hit bug #35412, retrying with Spannables removed", e); + mParams.text = mParams.text.toString(); + continue; + } else { + // If it still happens with all Spannables removed we'll bubble the exception up + throw e; + } + } + + break; + } + } + + // Do not cache if the text has ClickableSpans. + if (mShouldCacheLayout && !hasClickableSpans) { + mSavedLayout = layout; + sCache.put(hashCode, layout); + } + + // Force a new paint. + mParams.mForceNewPaint = true; + + if (mShouldWarmText && mGlyphWarmer != null) { + // Draw the text in a background thread to warm the cache. + mGlyphWarmer.warmLayout(layout); + } + + return layout; + } + + /** + * A comparable version of {@link TextPaint}. + */ + private static class ComparableTextPaint extends TextPaint { + + private float mShadowDx; + private float mShadowDy; + private float mShadowRadius; + private int mShadowColor; + + public ComparableTextPaint() { + super(); + } + + public ComparableTextPaint(int flags) { + super(flags); + } + + public ComparableTextPaint(Paint p) { + super(p); + } + + @Override + public void setShadowLayer(float radius, float dx, float dy, int color) { + mShadowRadius = radius; + mShadowDx = dx; + mShadowDy = dy; + mShadowColor = color; + + super.setShadowLayer(radius, dx, dy, color); + } + + @Override + public int hashCode() { + Typeface tf = getTypeface(); + + int hashCode = 1; + hashCode = 31 * hashCode + getColor(); + hashCode = 31 * hashCode + Float.floatToIntBits(getTextSize()); + hashCode = 31 * hashCode + (tf != null ? tf.hashCode() : 0); + hashCode = 31 * hashCode + Float.floatToIntBits(mShadowDx); + hashCode = 31 * hashCode + Float.floatToIntBits(mShadowDy); + hashCode = 31 * hashCode + Float.floatToIntBits(mShadowRadius); + hashCode = 31 * hashCode + mShadowColor; + hashCode = 31 * hashCode + linkColor; + + // Array. + if (drawableState == null) { + hashCode = 31 * hashCode + 0; + } else { + for (int i = 0; i < drawableState.length; i++) { + hashCode = 31 * hashCode + drawableState[i]; + } + } + + return hashCode; + } + } +} diff --git a/library/src/main/java/com/facebook/fbui/textlayoutbuilder/glyphwarmer/GlyphWarmerImpl.java b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/glyphwarmer/GlyphWarmerImpl.java new file mode 100644 index 0000000..2509746 --- /dev/null +++ b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/glyphwarmer/GlyphWarmerImpl.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder.glyphwarmer; + +import android.annotation.SuppressLint; +import android.graphics.Canvas; +import android.graphics.Picture; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.support.annotation.VisibleForTesting; +import android.text.Layout; + +import com.facebook.fbui.textlayoutbuilder.GlyphWarmer; +import com.facebook.fbui.textlayoutbuilder.util.LayoutMeasureUtil; + +/** + * Default {@link GlyphWarmer} that runs a {@link HandlerThread} + * to draw a text {@link Layout} on a {@link Picture}. + * This helps in warming up the glyph cache in Android 4.0+. + */ +public class GlyphWarmerImpl implements GlyphWarmer { + + // Handler for the HandlerThread. + private static WarmHandler sWarmHandler; + + @Override + public void warmLayout(Layout layout) { + WarmHandler handler = getWarmHandler(); + handler.sendMessage(handler.obtainMessage(WarmHandler.NO_OP, layout)); + } + + @VisibleForTesting + Looper getWarmHandlerLooper() { + return getWarmHandler().getLooper(); + } + + @SuppressLint({ + "BadMethodUse-android.os.HandlerThread._Constructor", + "BadMethodUse-java.lang.Thread.start" + }) + private WarmHandler getWarmHandler() { + if (sWarmHandler == null) { + // A text warmer thread to render the the layout in the background. + // This helps warm the layout cache in Android 4.0+ devices, + // making large blurbs of text to draw in 0ms. + HandlerThread warmerThread = new HandlerThread("GlyphWarmer"); + warmerThread.start(); + + // Note: warmerThread.getLooper() can return null. + sWarmHandler = new WarmHandler(warmerThread.getLooper()); + } + + return sWarmHandler; + } + + /** + * A handler to send messages to the GlyphWarmerImpl thread. + */ + private static class WarmHandler extends Handler { + + private static final int NO_OP = 1; + + private final Picture mPicture = new Picture(); + + public WarmHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + Layout layout = (Layout) msg.obj; + try { + Canvas canvas = mPicture.beginRecording( + LayoutMeasureUtil.getWidth(layout), + LayoutMeasureUtil.getHeight(layout)); + layout.draw(canvas); + mPicture.endRecording(); + } catch (Exception e) { + // Do nothing. + } + } + } +} diff --git a/library/src/main/java/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-info.java b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-info.java new file mode 100644 index 0000000..34175cc --- /dev/null +++ b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/glyphwarmer/package-info.java @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2016-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. + */ + +/** + * Provides a default implementation of {@link com.facebook.fbui.textlayoutbuilder.GlyphWarmer}. + * @see com.facebook.fbui.textlayoutbuilder.GlyphWarmer + */ + +package com.facebook.fbui.textlayoutbuilder.glyphwarmer; diff --git a/library/src/main/java/com/facebook/fbui/textlayoutbuilder/package-info.java b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/package-info.java new file mode 100644 index 0000000..4fc06d8 --- /dev/null +++ b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2016-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. + */ + +/** + * Provides the classes to build text {@link android.text.Layout}s easily. + *

+ * {@link com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder} is an utility class that creates + * text {@link android.text.Layout}s easily. This package contains classes that interact with + * {@link com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder}. + */ + +package com.facebook.fbui.textlayoutbuilder; diff --git a/library/src/main/java/com/facebook/fbui/textlayoutbuilder/util/LayoutMeasureUtil.java b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/util/LayoutMeasureUtil.java new file mode 100644 index 0000000..2b0508c --- /dev/null +++ b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/util/LayoutMeasureUtil.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder.util; + +import android.os.Build; +import android.text.Layout; +import android.text.StaticLayout; + +/** + * Utility Class for measuring text {@link Layout}s. + */ +public class LayoutMeasureUtil { + + /** + * Returns the width of the layout. + * + * @param layout The layout + * @return The width of the layout + */ + public static int getWidth(Layout layout) { + if (layout == null) { + return 0; + } + + // Supplying VERY_WIDE will make layout.getWidth() return a very large value. + int count = layout.getLineCount(); + int maxWidth = 0; + + for (int i = 0; i < count; i++) { + maxWidth = Math.max(maxWidth, (int) layout.getLineRight(i)); + } + + return maxWidth; + } + + /** + * Prior to version 20, If the Layout specifies extra space between lines (either by spacingmult + * or spacingadd) the StaticLayout would erroneously add this space after the last line as well. + * This bug was fixed in version 20. This method calculates the extra space and reduces the height + * by that amount. + * + * @param layout The layout + * @return The height of the layout + */ + public static int getHeight(Layout layout) { + if (layout == null) { + return 0; + } + + int extra = 0; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT_WATCH && + layout instanceof StaticLayout) { + int above = layout.getLineAscent(layout.getLineCount() - 1); + int below = layout.getLineDescent(layout.getLineCount() - 1); + float originalSize = (below - above - layout.getSpacingAdd()) / layout.getSpacingMultiplier(); + float ex = below - above - originalSize; + if (ex >= 0) { + extra = (int) (ex + 0.5); + } else { + extra = -(int) (-ex + 0.5); + } + } + return layout.getHeight() - extra; + } +} diff --git a/library/src/main/java/com/facebook/fbui/textlayoutbuilder/util/package-info.java b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/util/package-info.java new file mode 100644 index 0000000..1805856 --- /dev/null +++ b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/util/package-info.java @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2016-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. + */ + +/** + * Provides an utility class to measure text {@link android.text.Layout}s. + */ + +package com.facebook.fbui.textlayoutbuilder.util; diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml new file mode 100644 index 0000000..8af0b1b --- /dev/null +++ b/library/src/main/res/values/attrs.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/src/test/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilderTest.java b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilderTest.java new file mode 100644 index 0000000..bd4a0c4 --- /dev/null +++ b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilderTest.java @@ -0,0 +1,329 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder; + +import android.content.res.ColorStateList; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.support.v4.text.TextDirectionHeuristicsCompat; +import android.text.Layout; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.ClickableSpan; +import android.text.style.StyleSpan; +import android.view.View; + +import com.facebook.fbui.textlayoutbuilder.shadows.ShadowPicture; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +/** + * Tests {@link TextLayoutBuilder} + */ +@Config(manifest = Config.NONE, shadows = {ShadowPicture.class}) +@RunWith(RobolectricTestRunner.class) +public class TextLayoutBuilderTest { + + private static final String TEST = "TEST"; + private static final String LONG_TEXT = + "Lorem ipsum dolor sit amet test \n" + + "Lorem ipsum dolor sit amet test \n" + + "Lorem ipsum dolor sit amet test \n" + + "Lorem ipsum dolor sit amet test \n"; + + private TextLayoutBuilder mBuilder; + private Layout mLayout; + + @Before + public void setup() { + mBuilder = new TextLayoutBuilder(); + mBuilder.setText(TEST); + + // Clear the cache. + mBuilder.sCache.evictAll(); + } + + // Test setters. + @Test + public void testSetText() { + mLayout = mBuilder.setText("Android").build(); + assertEquals(mBuilder.getText(), "Android"); + assertEquals(mLayout.getText(), "Android"); + } + + @Test + public void testSetTextNull() { + mLayout = mBuilder.setText(null).build(); + assertEquals(mBuilder.getText(), null); + assertEquals(mLayout, null); + } + + @Test + public void testSetTextSize() { + mLayout = mBuilder.setTextSize(10).build(); + assertEquals(mBuilder.getTextSize(), 10.0f, 0.0f); + assertEquals(mLayout.getPaint().getTextSize(), 10.0f, 0.0f); + } + + @Test + public void testSetTextColor() { + mLayout = mBuilder.setTextColor(0xFFFF0000).build(); + assertEquals(mBuilder.getTextColor(), 0xFFFF0000); + assertEquals(mLayout.getPaint().getColor(), 0xFFFF0000); + } + + @Test + public void testSetTextColorStateList() { + mLayout = mBuilder.setTextColor(ColorStateList.valueOf(0xFFFF0000)).build(); + assertEquals(mBuilder.getTextColor(), 0xFFFF0000); + assertEquals(mLayout.getPaint().getColor(), 0xFFFF0000); + } + + @Test + public void testSetLinkColor() { + mLayout = mBuilder.setLinkColor(0xFFFF0000).build(); + assertEquals(mBuilder.getLinkColor(), 0xFFFF0000); + assertEquals(mLayout.getPaint().linkColor, 0xFFFF0000); + } + + @Test + public void testSetTextSpacingExtra() { + mLayout = mBuilder.setTextSpacingExtra(10).build(); + assertEquals(mBuilder.getTextSpacingExtra(), 10.0f, 0.0f); + assertEquals(mLayout.getSpacingAdd(), 10.0f, 0.0f); + } + + @Test + public void testSetTextSpacingMultiplier() { + mLayout = mBuilder.setTextSpacingMultiplier(1.5f).build(); + assertEquals(mBuilder.getTextSpacingMultiplier(), 1.5f, 0.0f); + assertEquals(mLayout.getSpacingMultiplier(), 1.5f, 0.0f); + } + + @Test + public void testSetIncludeFontPadding() { + mLayout = mBuilder.setIncludeFontPadding(false).build(); + assertEquals(mBuilder.getIncludeFontPadding(), false); + } + + @Test + public void testSetAlignment() { + mLayout = mBuilder.setAlignment(Layout.Alignment.ALIGN_CENTER).build(); + assertEquals(mBuilder.getAlignment(), Layout.Alignment.ALIGN_CENTER); + assertEquals(mLayout.getAlignment(), Layout.Alignment.ALIGN_CENTER); + } + + @Test + public void testSetTextDirection() { + mLayout = mBuilder.setTextDirection(TextDirectionHeuristicsCompat.LOCALE).build(); + assertEquals(mBuilder.getTextDirection(), TextDirectionHeuristicsCompat.LOCALE); + } + + @Test + public void testSetTypeface() { + mLayout = mBuilder.setTypeface(Typeface.MONOSPACE).build(); + assertEquals(mBuilder.getTypeface(), Typeface.MONOSPACE); + } + + @Test + public void testSetEllipsize() { + mLayout = mBuilder.setEllipsize(TextUtils.TruncateAt.MARQUEE).build(); + assertEquals(mBuilder.getEllipsize(), TextUtils.TruncateAt.MARQUEE); + } + + @Test + public void testSetSingleLine() { + mLayout = mBuilder.setSingleLine(true).build(); + assertEquals(mBuilder.getSingleLine(), true); + } + + @Test + public void testSetMaxLines() { + mLayout = mBuilder.setMaxLines(10).build(); + assertEquals(mBuilder.getMaxLines(), 10.0f, 0.0f); + } + + @Test + public void testSetShouldCacheLayout() { + mLayout = mBuilder.setShouldCacheLayout(false).build(); + assertEquals(mBuilder.getShouldCacheLayout(), false); + } + + @Test + public void testSetShouldWarmText() { + mLayout = mBuilder.setShouldWarmText(true).build(); + assertEquals(mBuilder.getShouldWarmText(), true); + } + + @Test + public void testSetGlyphWarmer() { + FakeGlyphWarmer glyphWarmer = new FakeGlyphWarmer(); + mLayout = mBuilder.setGlyphWarmer(glyphWarmer).build(); + assertEquals(mBuilder.getGlyphWarmer(), glyphWarmer); + } + + // Test functionality. + @Test + public void testSingleLine() { + mLayout = mBuilder + .setText(LONG_TEXT) + .setSingleLine(true) + .setWidth(1000) + .build(); + assertEquals(mLayout.getLineCount(), 1); + } + + @Test + public void testMaxLines() { + mLayout = mBuilder + .setText(LONG_TEXT) + .setMaxLines(2) + .setWidth(1000) + .build(); + assertEquals(mLayout.getLineCount(), 2); + } + + @Test + public void testDrawableState() { + int[] drawableState = {0, 1}; + mLayout = mBuilder.setDrawableState(drawableState).build(); + assertArrayEquals(mBuilder.getDrawableState(), drawableState); + } + + @Test + public void testNewPaint() { + Paint oldPaint = mBuilder.mParams.paint; + + // Build the current builder. + mBuilder.build(); + + // Change paint properties. + mBuilder.setShadowLayer(10.0f, 1.0f, 1.0f, 0); + Paint newPaint = mBuilder.mParams.paint; + assertNotEquals(oldPaint, newPaint); + } + + @Test + public void testWarmText() { + FakeGlyphWarmer warmer = new FakeGlyphWarmer(); + mLayout = mBuilder + .setShouldWarmText(true) + .setGlyphWarmer(warmer) + .build(); + assertEquals(warmer.getLayout(), mLayout); + } + + @Test + public void testDoNotWarmText() { + FakeGlyphWarmer warmer = new FakeGlyphWarmer(); + mLayout = mBuilder + .setShouldWarmText(false) + .setGlyphWarmer(warmer) + .build(); + assertEquals(warmer.getLayout(), null); + } + + @Test + public void testCaching() { + mLayout = mBuilder.setShouldCacheLayout(true).build(); + Layout newLayout = mBuilder.build(); + assertEquals(mLayout, newLayout); + assertEquals(mBuilder.sCache.size(), 1); + assertEquals(mBuilder.sCache.get(mBuilder.mParams.hashCode()), mLayout); + } + + @Test + public void testNoCaching() { + mLayout = mBuilder.setShouldCacheLayout(false).build(); + Layout newLayout = mBuilder.build(); + assertNotEquals(mLayout, newLayout); + assertEquals(mBuilder.sCache.size(), 0); + assertEquals(mBuilder.sCache.get(mBuilder.mParams.hashCode()), null); + } + + @Test + public void testTwoBuildersWithSameParamsAndCaching() { + mLayout = mBuilder.setShouldCacheLayout(true).build(); + + TextLayoutBuilder newBuilder = new TextLayoutBuilder(); + Layout newLayout = newBuilder.setText(TEST).setShouldCacheLayout(true).build(); + assertEquals(mLayout, newLayout); + } + + @Test + public void testTwoBuildersWithSameParamsAndNoCaching() { + mLayout = mBuilder.setShouldCacheLayout(false).build(); + + TextLayoutBuilder newBuilder = new TextLayoutBuilder(); + Layout newLayout = newBuilder.setText(TEST).setShouldCacheLayout(false).build(); + assertNotEquals(mLayout, newLayout); + } + + @Test + public void testSpannableString() { + SpannableStringBuilder spannable = new SpannableStringBuilder("This is a bold text"); + spannable.setSpan(new StyleSpan(Typeface.BOLD), 10, 13, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + mLayout = mBuilder.setText(spannable).build(); + assertEquals(mLayout.getText(), spannable); + } + + @Test + public void testCachingSpannableString() { + SpannableStringBuilder spannable = new SpannableStringBuilder("This is a bold text"); + spannable.setSpan(new StyleSpan(Typeface.BOLD), 10, 13, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + mLayout = mBuilder + .setText(spannable) + .setShouldCacheLayout(true) + .build(); + assertEquals(mBuilder.sCache.size(), 1); + assertEquals(mBuilder.sCache.get(mBuilder.mParams.hashCode()), mLayout); + } + + @Test + public void testNoCachingSpannableString() { + ClickableSpan clickableSpan = new ClickableSpan() { + @Override + public void onClick(View widget) { + // Do nothing. + } + }; + + SpannableStringBuilder spannable = new SpannableStringBuilder("This is a bold text"); + spannable.setSpan(clickableSpan, 10, 13, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + mLayout = mBuilder + .setText(spannable) + .setShouldCacheLayout(true) + .build(); + assertEquals(mBuilder.sCache.size(), 0); + assertEquals(mBuilder.sCache.get(mBuilder.mParams.hashCode()), null); + } + + private static class FakeGlyphWarmer implements GlyphWarmer { + private Layout mLayout = null; + + @Override + public void warmLayout(Layout layout) { + mLayout = layout; + } + + Layout getLayout() { + return mLayout; + } + } +} diff --git a/library/src/test/java/com/facebook/fbui/textlayoutbuilder/TextMeasureModeTest.java b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/TextMeasureModeTest.java new file mode 100644 index 0000000..826d63c --- /dev/null +++ b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/TextMeasureModeTest.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder; + +import android.graphics.Typeface; +import android.text.Layout; + +import com.facebook.fbui.textlayoutbuilder.shadows.ShadowLayout; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import static com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder.MEASURE_MODE_AT_MOST; +import static com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder.MEASURE_MODE_EXACTLY; +import static com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder.MEASURE_MODE_UNSPECIFIED; +import static org.junit.Assert.assertEquals; + +@Config(manifest = Config.NONE, shadows = {ShadowLayout.class}) +@RunWith(RobolectricTestRunner.class) +public class TextMeasureModeTest { + + @Test + public void testMeasureModeUnspecified() { + final Layout layout = new TextLayoutBuilder() + .setText(ShadowLayout.LONG_TEXT) + .setWidth(20, MEASURE_MODE_UNSPECIFIED) + .setTypeface(Typeface.DEFAULT) + .setTextSize(10) + .build(); + + assertEquals(ShadowLayout.LONG_TEXT_LENGTH, layout.getWidth()); + } + + @Test + public void testMeasureModeExactly() { + final Layout layout = new TextLayoutBuilder() + .setText(ShadowLayout.LONG_TEXT) + .setWidth(20, MEASURE_MODE_EXACTLY) + .setTypeface(Typeface.DEFAULT) + .setTextSize(10) + .build(); + + assertEquals(20, layout.getWidth()); + } + + @Test + public void testMeasureModeAtMostLongText() { + final Layout layout = new TextLayoutBuilder() + .setText(ShadowLayout.LONG_TEXT) + .setWidth(20, MEASURE_MODE_AT_MOST) + .setTypeface(Typeface.DEFAULT) + .setTextSize(10) + .build(); + + assertEquals(20, layout.getWidth()); + } + + @Test + public void testMeasureModeAtMostShortText() { + final Layout layout = new TextLayoutBuilder() + .setText(ShadowLayout.SHORT_TEXT) + .setWidth(20, MEASURE_MODE_AT_MOST) + .setTypeface(Typeface.DEFAULT) + .setTextSize(10) + .build(); + + assertEquals(ShadowLayout.SHORT_TEXT_LENGTH, layout.getWidth()); + } + + @Test + public void testLegacyBehaviour() { + final Layout layout = new TextLayoutBuilder() + .setText(ShadowLayout.LONG_TEXT) + .setWidth(-1) + .setTypeface(Typeface.DEFAULT) + .setTextSize(10) + .build(); + + assertEquals(ShadowLayout.LONG_TEXT_LENGTH, layout.getWidth()); + } +} diff --git a/library/src/test/java/com/facebook/fbui/textlayoutbuilder/glyphwarmer/GlyphWarmerImplTest.java b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/glyphwarmer/GlyphWarmerImplTest.java new file mode 100644 index 0000000..31ebbab --- /dev/null +++ b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/glyphwarmer/GlyphWarmerImplTest.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder.glyphwarmer; + +import android.graphics.Canvas; +import android.text.Layout; + +import com.facebook.fbui.textlayoutbuilder.shadows.ShadowPicture; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.internal.ShadowExtractor; +import org.robolectric.shadows.ShadowLooper; + +import static org.mockito.Mockito.verify; +import static org.mockito.Matchers.any; + +/** + * Tests {@link GlyphWarmerImpl}. + */ +@Config(manifest = Config.NONE, shadows = {ShadowPicture.class}) +@RunWith(RobolectricTestRunner.class) +public class GlyphWarmerImplTest { + + @Mock Layout mLayout; + + private ShadowLooper mShadowLooper; + private GlyphWarmerImpl mGlyphWarmerImpl; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + mGlyphWarmerImpl = new GlyphWarmerImpl(); + mShadowLooper = (ShadowLooper) ShadowExtractor.extract(mGlyphWarmerImpl.getWarmHandlerLooper()); + } + + @Test + public void testWarmGlyph() { + mGlyphWarmerImpl.warmLayout(mLayout); + mShadowLooper.runOneTask(); + verify(mLayout).draw(any(Canvas.class)); + } + + @Test + public void testWarmGlyphForNullLayout() { + mGlyphWarmerImpl.warmLayout(null); + mShadowLooper.runOneTask(); + } +} diff --git a/library/src/test/java/com/facebook/fbui/textlayoutbuilder/shadows/ShadowLayout.java b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/shadows/ShadowLayout.java new file mode 100644 index 0000000..5e513a3 --- /dev/null +++ b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/shadows/ShadowLayout.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder.shadows; + +import android.text.Layout; +import android.text.TextPaint; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@Implements(Layout.class) +public class ShadowLayout { + + public static final String LONG_TEXT = "ZzZzZzZzZzZzZzZzZzZzZz"; + public static final String SHORT_TEXT = "Z"; + public static final int LONG_TEXT_LENGTH = 100; + public static final int SHORT_TEXT_LENGTH = 5; + + @Implementation + public static float getDesiredWidth(CharSequence source, TextPaint paint) { + if (LONG_TEXT.equals(source)) { + return LONG_TEXT_LENGTH; + } else if (SHORT_TEXT.equals(source)) { + return SHORT_TEXT_LENGTH; + } + return 0; + } +} diff --git a/library/src/test/java/com/facebook/fbui/textlayoutbuilder/shadows/ShadowPicture.java b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/shadows/ShadowPicture.java new file mode 100644 index 0000000..ac91426 --- /dev/null +++ b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/shadows/ShadowPicture.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder.shadows; + +import android.graphics.Canvas; +import android.graphics.Picture; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@Implements(Picture.class) +public class ShadowPicture { + + @Implementation + public void __constructor__(int nativePicture, boolean fromStream) { + // Do nothing. + } + + @Implementation + public void __constructor__(int nativePicture) { + // Do nothing. + } + + @Implementation + public void __constructor__() { + // Do nothing. + } + + @Implementation + public Canvas beginRecording(int width, int height) { + return new Canvas(); + } +} diff --git a/library/src/test/java/com/facebook/fbui/textlayoutbuilder/util/LayoutMeasureUtilTest.java b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/util/LayoutMeasureUtilTest.java new file mode 100644 index 0000000..b5627a2 --- /dev/null +++ b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/util/LayoutMeasureUtilTest.java @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2016-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.fbui.textlayoutbuilder.util; + +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import static org.junit.Assert.assertEquals; + +/** + * Tests {@link LayoutMeasureUtil} + */ +@Config(manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class LayoutMeasureUtilTest { + + private static final String ONE_LINE_TEXT = "test"; + private static final String TWO_LINE_TEXT = "test\ntest"; + + private Layout mLayout; + + @Test + public void testOneLineWithAdd() { + mLayout = StaticLayoutHelper.makeStaticLayout(ONE_LINE_TEXT, 1.0f, 5.0f); + assertEquals(LayoutMeasureUtil.getHeight(mLayout), 10); + } + + @Test + public void testTwoLinesWithAdd() { + mLayout = StaticLayoutHelper.makeStaticLayout(TWO_LINE_TEXT, 1.0f, 5.0f); + assertEquals(LayoutMeasureUtil.getHeight(mLayout), 25); + } + + @Test + public void testOneLineWithMulti() { + mLayout = StaticLayoutHelper.makeStaticLayout(ONE_LINE_TEXT, 1.5f, 0.0f); + assertEquals(LayoutMeasureUtil.getHeight(mLayout), 10); + } + + @Test + public void testTwoLinesWithMulti() { + mLayout = StaticLayoutHelper.makeStaticLayout(TWO_LINE_TEXT, 1.5f, 0.0f); + assertEquals(LayoutMeasureUtil.getHeight(mLayout), 25); + } + + @Test + public void testOneLineWithAddAndMulti() { + mLayout = StaticLayoutHelper.makeStaticLayout(ONE_LINE_TEXT, 1.5f, 2.0f); + assertEquals(LayoutMeasureUtil.getHeight(mLayout), 10); + } + + @Test + public void testTwoLinesWithAddAndMulti() { + mLayout = StaticLayoutHelper.makeStaticLayout(TWO_LINE_TEXT, 1.5f, 2.0f); + assertEquals(LayoutMeasureUtil.getHeight(mLayout), 27); + } + + @Test + public void testEmptyTextWithAddAndMulti() { + mLayout = StaticLayoutHelper.makeStaticLayout("", 1.5f, 2.0f); + assertEquals(LayoutMeasureUtil.getHeight(mLayout), 10); + } + + // TextPaint with a line height of 10. + private static class DummyTextPaint extends TextPaint { + @Override + public int getFontMetricsInt(FontMetricsInt fmi) { + if (fmi != null) { + fmi.ascent = 0; + fmi.top = 0; + fmi.descent = 10; + fmi.bottom = 10; + } + return 0; + } + } + + private static class StaticLayoutHelper { + public static Layout makeStaticLayout( + CharSequence text, + float spacingMult, + float spacingAdd) { + return new StaticLayout( + text, + new DummyTextPaint(), + 1000, + Layout.Alignment.ALIGN_NORMAL, + spacingMult, + spacingAdd, + true); + } + } +} diff --git a/proguard-android.txt b/proguard-android.txt new file mode 100644 index 0000000..c10c9fd --- /dev/null +++ b/proguard-android.txt @@ -0,0 +1,7 @@ +# This is a configuration file for ProGuard. +# For more details about adding this proguard file to your app: +# https://developer.android.com/studio/projects/android-library.html + +# This library uses a non-public Android constructor within StaticLayout. +# See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details. +-dontwarn android.text.StaticLayout diff --git a/release-sonatype.gradle b/release-sonatype.gradle new file mode 100644 index 0000000..5dc6b35 --- /dev/null +++ b/release-sonatype.gradle @@ -0,0 +1,92 @@ +// Definitions of release tasks. +// In order to upload jars to maven repository run: +// $ ./gradlew [-P askForPasswords=true] uploadArchives +// "-P askForPasswords" is optional. Including it in the command will force +// gradle to ask you for passwords rather than read them from gradle.properties. +// +// In order to upload jars to local maven repository run: +//$ ./gradlew -P repositoryUrl=file://localhost/ uploadArchives + + +apply plugin: 'maven' +apply plugin: 'signing' + +def isReleaseBuild() { + return VERSION_NAME.contains("SNAPSHOT") == false +} + +def getRepositoryUrl() { + return hasProperty('repositoryUrl') ? property('repositoryUrl') : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" +} + +def getRepositoryUsername() { + return hasProperty('repositoryUsername') ? property('repositoryUsername') : "" +} + +def getRepositoryPassword() { + return hasProperty('repositoryPassword') ? property('repositoryPassword') : "" +} + +def getPassword(String passwordName) { + return new String(System.console().readPassword("\nProvide $passwordName:")) +} + +if (hasProperty('askForPasswords')) { + ext.'repositoryPassword' = getPassword('repository password'); + ext.'signing.password' = getPassword('signing password'); +} + +afterEvaluate { project -> + version = VERSION_NAME + group = GROUP + + signing { + required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives + } + + uploadArchives { + configuration = configurations.archives + repositories.mavenDeployer { + beforeDeployment { + MavenDeployment deployment -> signing.signPom(deployment) + } + + repository(url: getRepositoryUrl()) { + authentication( + userName: getRepositoryUsername(), + password: getRepositoryPassword()) + + } + + pom.project { + name POM_NAME + artifactId POM_ARTIFACT_ID + packaging POM_PACKAGING + description POM_DESCRIPTION + url 'https://github.com/facebookincubator/TextLayoutBuilder' + + scm { + url 'https://github.com/facebookincubator/TextLayoutBuilder.git' + connection 'scm:git:https://github.com/facebookincubator/TextLayoutBuilder.git' + developerConnection 'scm:git:git@github.com:facebookincubator/TextLayoutBuilder.git' + } + + licenses { + license { + name 'BSD License' + url 'https://github.com/facebookincubator/TextLayoutBuilder/blob/master/LICENSE' + distribution 'repo' + } + } + + developers { + developer { + id 'facebook' + name 'Facebook' + } + } + } + } + } +} diff --git a/release.gradle b/release.gradle new file mode 100644 index 0000000..1dab987 --- /dev/null +++ b/release.gradle @@ -0,0 +1,5 @@ +// Common Android tasks for all releases that generate Javadocs, sources, etc. +apply from: rootProject.file('gradle/android-tasks.gradle') + +// Upload to Sonatype +apply from: rootProject.file('release-sonatype.gradle') diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..2b9704b --- /dev/null +++ b/settings.gradle @@ -0,0 +1,4 @@ +include ':library' +include ':library:libs:proxy' +include ':library:libs:android-text' +include ':library:libs:android-support-v4-text'