hunk.code_window:       final FragmentActivity activity = (FragmentActivity) getCurrentActivity();
         mClient = new GoogleApiClient.Builder(getReactApplicationContext())
             .addApi(Fitness.HISTORY_API)
             .addApi(Fitness.SENSORS_API)
             .addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ))
             .addConnectionCallbacks(
                 new GoogleApiClient.ConnectionCallbacks() {
  
hunk.labels: keep keep keep add keep keep keep
hunk.after_edit:           .addApi(Fitness.RECORDING_API)
  
hunk.file_path: android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/PedometerModule.java
hunk.type: add
file: /**
   * Copyright (c) 2015-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.react.flat;
  
  import javax.annotation.Nullable;
  
  import android.support.v4.text.TextDirectionHeuristicsCompat;
  import android.text.Layout;
  import android.text.TextUtils;
  import android.view.Gravity;
  
  import com.facebook.yoga.YogaDirection;
  import com.facebook.yoga.YogaMeasureMode;
  import com.facebook.yoga.YogaMeasureFunction;
  import com.facebook.yoga.YogaNode;
  import com.facebook.yoga.YogaMeasureOutput;
  import com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder;
  import com.facebook.fbui.textlayoutbuilder.glyphwarmer.GlyphWarmerImpl;
  import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
  import com.facebook.react.uimanager.PixelUtil;
  import com.facebook.react.uimanager.Spacing;
  import com.facebook.react.uimanager.ViewDefaults;
  import com.facebook.react.uimanager.ViewProps;
  import com.facebook.react.uimanager.annotations.ReactProp;
  
  /**
   * RCTText is a top-level node for text. It extends {@link RCTVirtualText} because it can contain
   * styling information, but has the following differences:
   *
   * a) RCTText is not a virtual node, and can be measured and laid out.
   * b) when no font size is specified, a font size of ViewDefaults#FONT_SIZE_SP is assumed.
   */
  /* package */ final class RCTText extends RCTVirtualText implements YogaMeasureFunction {
  
    // index of left and right in the Layout.Alignment enum since the base values are @hide
    private static final int ALIGNMENT_LEFT = 3;
    private static final int ALIGNMENT_RIGHT = 4;
  
    // We set every value we use every time we use the layout builder, so we can get away with only
    // using a single instance.
    private static final TextLayoutBuilder sTextLayoutBuilder =
        new TextLayoutBuilder()
            .setShouldCacheLayout(false)
            .setShouldWarmText(true)
            .setGlyphWarmer(new GlyphWarmerImpl());
  
    private @Nullable CharSequence mText;
    private @Nullable DrawTextLayout mDrawCommand;
    private float mSpacingMult = 1.0f;
    private float mSpacingAdd = 0.0f;
    private int mNumberOfLines = Integer.MAX_VALUE;
    private int mAlignment = Gravity.NO_GRAVITY;
  
    public RCTText() {
      setMeasureFunction(this);
      getSpan().setFontSize(getDefaultFontSize());
    }
  
    @Override
    public boolean isVirtual() {
      return false;
    }
  
    @Override
    public boolean isVirtualAnchor() {
      return true;
    }
  
    @Override
    public long measure(
        YogaNode node,
        float width,
        YogaMeasureMode widthMode,
        float height,
        YogaMeasureMode heightMode) {
  
      CharSequence text = getText();
      if (TextUtils.isEmpty(text)) {
        // to indicate that we don't have anything to display
        mText = null;
        return YogaMeasureOutput.make(0, 0);
      } else {
        mText = text;
      }
  
      Layout layout = createTextLayout(
          (int) Math.ceil(width),
          widthMode,
          TextUtils.TruncateAt.END,
          true,
          mNumberOfLines,
          mNumberOfLines == 1,
          text,
          getFontSize(),
          mSpacingAdd,
          mSpacingMult,
          getFontStyle(),
          getAlignment());
  
      if (mDrawCommand != null && !mDrawCommand.isFrozen()) {
        mDrawCommand.setLayout(layout);
      } else {
        mDrawCommand = new DrawTextLayout(layout);
      }
  
      return YogaMeasureOutput.make(mDrawCommand.getLayoutWidth(), mDrawCommand.getLayoutHeight());
    }
  
    @Override
    protected void collectState(
        StateBuilder stateBuilder,
        float left,
        float top,
        float right,
        float bottom,
        float clipLeft,
        float clipTop,
        float clipRight,
        float clipBottom) {
  
      super.collectState(
          stateBuilder,
          left,
          top,
          right,
          bottom,
          clipLeft,
          clipTop,
          clipRight,
          clipBottom);
  
      if (mText == null) {
        // as an optimization, LayoutEngine may not call measure in certain cases, such as when the
        // dimensions are already defined. in these cases, we should still draw the text.
        if (bottom - top > 0 && right - left > 0) {
          CharSequence text = getText();
          if (!TextUtils.isEmpty(text)) {
            mText = text;
          }
        }
  
        if (mText == null) {
          // nothing to draw (empty text).
          return;
        }
      }
  
      boolean updateNodeRegion = false;
      if (mDrawCommand == null) {
        mDrawCommand = new DrawTextLayout(createTextLayout(
            (int) Math.ceil(right - left),
            YogaMeasureMode.EXACTLY,
            TextUtils.TruncateAt.END,
            true,
            mNumberOfLines,
            mNumberOfLines == 1,
            mText,
            getFontSize(),
            mSpacingAdd,
            mSpacingMult,
            getFontStyle(),
            getAlignment()));
        updateNodeRegion = true;
      }
  
      left += getPadding(Spacing.LEFT);
      top += getPadding(Spacing.TOP);
  
      // these are actual right/bottom coordinates where this DrawCommand will draw.
      right = left + mDrawCommand.getLayoutWidth();
      bottom = top + mDrawCommand.getLayoutHeight();
  
      mDrawCommand = (DrawTextLayout) mDrawCommand.updateBoundsAndFreeze(
          left,
          top,
          right,
          bottom,
          clipLeft,
          clipTop,
          clipRight,
          clipBottom);
      stateBuilder.addDrawCommand(mDrawCommand);
  
      if (updateNodeRegion) {
        NodeRegion nodeRegion = getNodeRegion();
        if (nodeRegion instanceof TextNodeRegion) {
          ((TextNodeRegion) nodeRegion).setLayout(mDrawCommand.getLayout());
        }
      }
  
      performCollectAttachDetachListeners(stateBuilder);
    }
  
    @Override
    boolean doesDraw() {
      // assume text always draws - this is a performance optimization to avoid having to
      // getText() when layout was skipped and when collectState wasn't yet called
      return true;
    }
  
    @ReactProp(name = ViewProps.LINE_HEIGHT, defaultDouble = Double.NaN)
    public void setLineHeight(double lineHeight) {
      if (Double.isNaN(lineHeight)) {
        mSpacingMult = 1.0f;
        mSpacingAdd = 0.0f;
      } else {
        mSpacingMult = 0.0f;
        mSpacingAdd = PixelUtil.toPixelFromSP((float) lineHeight);
      }
      notifyChanged(true);
    }
  
    @ReactProp(name = ViewProps.NUMBER_OF_LINES, defaultInt = Integer.MAX_VALUE)
    public void setNumberOfLines(int numberOfLines) {
      mNumberOfLines = numberOfLines;
      notifyChanged(true);
    }
  
    @Override
    /* package */ void updateNodeRegion(
        float left,
        float top,
        float right,
        float bottom,
        boolean isVirtual) {
  
      NodeRegion nodeRegion = getNodeRegion();
      if (mDrawCommand == null) {
        if (!nodeRegion.matches(left, top, right, bottom, isVirtual)) {
          setNodeRegion(new TextNodeRegion(left, top, right, bottom, getReactTag(), isVirtual, null));
        }
        return;
      }
  
      Layout layout = null;
  
      if (nodeRegion instanceof TextNodeRegion) {
        layout = ((TextNodeRegion) nodeRegion).getLayout();
      }
  
      Layout newLayout = mDrawCommand.getLayout();
      if (!nodeRegion.matches(left, top, right, bottom, isVirtual) || layout != newLayout) {
        setNodeRegion(
            new TextNodeRegion(left, top, right, bottom, getReactTag(), isVirtual, newLayout));
      }
    }
  
    @Override
    protected int getDefaultFontSize() {
      // top-level <Text /> should always specify font size.
      return fontSizeFromSp(ViewDefaults.FONT_SIZE_SP);
    }
  
    @Override
    protected void notifyChanged(boolean shouldRemeasure) {
      // Future patch: should only recreate Layout if shouldRemeasure is false
      dirty();
    }
  
    @ReactProp(name = ViewProps.TEXT_ALIGN)
    public void setTextAlign(@Nullable String textAlign) {
      if (textAlign == null || "auto".equals(textAlign)) {
        mAlignment = Gravity.NO_GRAVITY;
      } else if ("left".equals(textAlign)) {
        // left and right may yield potentially different results (relative to non-nodes) in cases
        // when supportsRTL="true" in the manifest.
        mAlignment = Gravity.LEFT;
      } else if ("right".equals(textAlign)) {
        mAlignment = Gravity.RIGHT;
      } else if ("center".equals(textAlign)) {
        mAlignment = Gravity.CENTER;
      } else {
        throw new JSApplicationIllegalArgumentException("Invalid textAlign: " + textAlign);
      }
  
      notifyChanged(false);
    }
  
    public Layout.Alignment getAlignment() {
      boolean isRtl = getLayoutDirection() == YogaDirection.RTL;
      switch (mAlignment) {
        // Layout.Alignment.RIGHT and Layout.Alignment.LEFT are @hide :(
        case Gravity.LEFT:
          int index = isRtl ? ALIGNMENT_RIGHT : ALIGNMENT_LEFT;
          return Layout.Alignment.values()[index];
        case Gravity.RIGHT:
          index = isRtl ? ALIGNMENT_LEFT : ALIGNMENT_RIGHT;
          return Layout.Alignment.values()[index];
        case Gravity.CENTER:
          return Layout.Alignment.ALIGN_CENTER;
        case Gravity.NO_GRAVITY:
        default:
          return Layout.Alignment.ALIGN_NORMAL;
      }
    }
  
    private static Layout createTextLayout(
        int width,
        YogaMeasureMode widthMode,
        TextUtils.TruncateAt ellipsize,
        boolean shouldIncludeFontPadding,
        int maxLines,
        boolean isSingleLine,
        CharSequence text,
        int textSize,
        float extraSpacing,
        float spacingMultiplier,
        int textStyle,
        Layout.Alignment textAlignment) {
      Layout newLayout;
  
      final @TextLayoutBuilder.MeasureMode int textMeasureMode;
      switch (widthMode) {
        case UNDEFINED:
          textMeasureMode = TextLayoutBuilder.MEASURE_MODE_UNSPECIFIED;
          break;
        case EXACTLY:
          textMeasureMode = TextLayoutBuilder.MEASURE_MODE_EXACTLY;
          break;
        case AT_MOST:
          textMeasureMode = TextLayoutBuilder.MEASURE_MODE_AT_MOST;
          break;
        default:
          throw new IllegalStateException("Unexpected size mode: " + widthMode);
      }
  
      sTextLayoutBuilder
          .setEllipsize(ellipsize)
          .setMaxLines(maxLines)
          .setSingleLine(isSingleLine)
          .setText(text)
          .setTextSize(textSize)
          .setWidth(width, textMeasureMode);
  
      sTextLayoutBuilder.setTextStyle(textStyle);
  
      sTextLayoutBuilder.setTextDirection(TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR);
      sTextLayoutBuilder.setIncludeFontPadding(shouldIncludeFontPadding);
      sTextLayoutBuilder.setTextSpacingExtra(extraSpacing);
      sTextLayoutBuilder.setTextSpacingMultiplier(spacingMultiplier);
      sTextLayoutBuilder.setAlignment(textAlignment);
  
      newLayout = sTextLayoutBuilder.build();
  
      sTextLayoutBuilder.setText(null);
  
      return newLayout;
    }
  }
  