@AutoValue
public abstract class CoachMarkBuilder implements Parcelable {

    public static Builder create(Point coachMarkViewAnchorTop,
                                 Point coachMarkViewAnchorBottom,
                                 @StringRes int coachMarkMessage) {
        return new AutoValue_CoachMarkBuilder.Builder()
                .setAnchorTop(coachMarkViewAnchorTop)
                .setCoachMarkMessage(coachMarkMessage)
                .setNotchPosition(0.0)
                .setAnchorBottom(coachMarkViewAnchorBottom)
                .setFooterHeight(R.dimen.coach_mark_zero_dp)
                .setActionBarHeight(R.dimen.coach_mark_zero_dp)
                .setCoachMarkTextGravity(CoachMarkTextGravity.LEFT)
                .setInfoForViewToMaskList(new ArrayList<InfoForViewToMask>(0))
                .setImageDrawableRes(R.drawable.coachmark_drawable_no_image)
                .setBackGroundTintForImage(R.color.coach_mark_transparent_color)
                .setCoachMarkLayoutMargin(CoachMarkLayoutMargin.create().build())
                .setUserDesiredPopUpPositionWithRespectToView(PopUpPosition.RIGHT)
                .setImageLayoutInformation(ImageLayoutInformation.create(R.dimen.coach_mark_zero_dp, R.dimen.coach_mark_zero_dp).build())
                .setPopUpCoachMarkDismissButtonPosition(DialogDismissButtonPosition.RIGHT)
                .setAnimationOnImage(R.anim.coach_mark_animation);
    }

    public Builder newBuilder() {
        return new AutoValue_CoachMarkBuilder.Builder(this);
    }

    public abstract Point getAnchorTop();

    public abstract Point getAnchorBottom();

    @StringRes
    public abstract int getCoachMarkMessage();

    @DimenRes
    public abstract int getActionBarHeight();

    @DimenRes
    public abstract int getFooterHeight();

    @Nullable
    public abstract ArrayList<InfoForViewToMask> getInfoForViewToMaskList();

    @CoachMarkTextGravity
    public abstract int getCoachMarkTextGravity();

    @Nullable
    public abstract String getFontStyleForDismissButton();

    @Nullable
    public abstract String getFontStyleForCoachMarkText();

    @DialogDismissButtonPosition
    public abstract int getPopUpCoachMarkDismissButtonPosition();

    public abstract double getNotchPosition();

    @PopUpPosition
    public abstract int getUserDesiredPopUpPositionWithRespectToView();

    @ColorRes
    public abstract int getBackGroundTintForImage();

    @DrawableRes
    public abstract int getImageDrawableRes();

    public abstract CoachMarkLayoutMargin getCoachMarkLayoutMargin();

    public abstract ImageLayoutInformation getImageLayoutInformation();

    @AnimRes
    public abstract int getAnimationOnImage();

    @AutoValue.Builder
    public static abstract class Builder {

        public abstract Builder setAnchorTop(Point anchorTop);

        public abstract Builder setAnchorBottom(Point anchorBottom);

        public abstract Builder setCoachMarkMessage(@StringRes int coachMarkMessage);

        public abstract Builder setCoachMarkTextGravity(@CoachMarkTextGravity int coachMarkTextGravity);

        public abstract Builder setFontStyleForDismissButton(@Nullable String fontStyleForDismissButton);

        public abstract Builder setFontStyleForCoachMarkText(@Nullable String fontStyleForCoachMarkText);

        public abstract Builder setActionBarHeight(@DimenRes int actionBarHeight);

        public abstract Builder setFooterHeight(@DimenRes int footerHeight);

        public abstract Builder setInfoForViewToMaskList(@Nullable ArrayList<InfoForViewToMask> infoForViewToMaskList);

        public abstract Builder setPopUpCoachMarkDismissButtonPosition(@DialogDismissButtonPosition int popUpCoachMarkDismissButtonPosition);

        public abstract Builder setNotchPosition(double notchPosition);

        public abstract Builder setUserDesiredPopUpPositionWithRespectToView(@PopUpPosition int userDesiredPopUpPositionWithRespectToView);

        public abstract Builder setBackGroundTintForImage(@ColorRes int backGroundTintForImage);

        public abstract Builder setImageDrawableRes(@DrawableRes int imageDrawableRes);

        public abstract Builder setCoachMarkLayoutMargin(CoachMarkLayoutMargin coachMarkLayoutMargin);

        public abstract Builder setImageLayoutInformation(ImageLayoutInformation imageLayoutInformation);

        public abstract Builder setAnimationOnImage(@AnimRes int animationOnImage);

        public abstract CoachMarkBuilder build();

    }
} @ParametersAreNonnullByDefault
public class DefaultDimensionResourceProvider implements IDimensionResourceProvider {

    private final Context mContext;

    public DefaultDimensionResourceProvider(final Context context) {
        mContext = context;
    }

    @Override
    public int getDimensionInPixel(@DimenRes final int dimenRes) {
        return mContext.getResources().getDimensionPixelSize(dimenRes);
    }
} @ParametersAreNonnullByDefault
public class DefaultScreenInfoProvider implements IScreenInfoProvider {

    private final Context mContext;

    public DefaultScreenInfoProvider(final Context context) {
        mContext = context;
    }

    @Override
    public int getScreenHeightInPixels() {
        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
        return displayMetrics.heightPixels;
    }

    @Override
    public int getScreenWidthInPixels() {
        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
        return displayMetrics.widthPixels;
    }
} @ParametersAreNonnullByDefault
public class DefaultStringResourceProvider implements IStringResourceProvider {

    private final Context mContext;

    public DefaultStringResourceProvider(final Context context) {
        mContext = context;
    }

    @Override
    public String getStringFromResource(@StringRes final int stringRes) {
        return mContext.getString(stringRes);
    }

    @Override
    public String getStringFromResource(@StringRes final int stringResId, final Object... formatArgs) {
        return mContext.getString(stringResId, formatArgs);
    }
} @ParametersAreNonnullByDefault
public class DefaultTypeFaceProvider implements ITypeFaceProvider {

    private final Context mContext;

    public DefaultTypeFaceProvider(final Context context) {
        mContext = context;
    }

    @Override
    @Nullable
    public Typeface getTypeFaceFromAssets(final String fontFilePath) {
        try {
            return Typeface.createFromAsset(mContext.getAssets(), fontFilePath);
        } catch (RuntimeException e) {
            Log.e(DefaultTypeFaceProvider.class.getName(), e.getMessage());
        }
        return null;
    }
} public interface IDimensionResourceProvider {
    int getDimensionInPixel(@DimenRes final int dimenRes);
} public interface IScreenInfoProvider {
    int getScreenHeightInPixels();

    int getScreenWidthInPixels();
} public interface IStringResourceProvider {

    String getStringFromResource(@StringRes final int stringRes);

    String getStringFromResource(@StringRes final int stringResId,
                                 final Object... formatArgs);

} public interface ITypeFaceProvider {
    @Nullable
    Typeface getTypeFaceFromAssets(final String fontFilePath);
} public abstract class BaseViews {

    @Nullable
    private View mRoot;

    protected BaseViews(@NonNull View root) {
        mRoot = root;
        ButterKnife.bind(this, root);
    }

    @Nullable
    public View getRootView() {
        return mRoot;
    }

    @CallSuper
    public void clear() {
        mRoot = null;
    }

} public interface IPopUpCoachMarkPresentation {
    void createViewToBeMaskedOut(int startX, int startY, int height, int width);

    void setImageInformation(double centerX, double centerY, int imageWidth,
                             int imageHeight, int backGroundTint, int imageDrawableRes);

    void dismissWithError(String message);

    void closeCoachMarkDialog();

    void setDismissButtonPositionLeft();

    void setDismissButtonPositionRight();

    void setTypeFaceForDismissButton(Typeface typeface);

    void setTypeFaceForPopUpText(Typeface typeface);

    void setUpGravityForCoachMarkText(@CoachMarkTextGravity int gravity);

    void setPopUpViewTopLeft(Rect margin, @CoachMarkLayoutOrientation int orientation);

    void setPopUpViewBottomRight(Rect margin, @CoachMarkLayoutOrientation int orientation);

    void setNotchPositionIfPopUpTopLeft(Rect margin, float rotation);

    void setNotchPositionIfPopUpBottomRight(Rect margin, float rotation);

    void uiAdjustmentForNotchIfPopUpRight(Rect margin);

    void uiAdjustmentForNotchIfPopUpBottom(Rect margin);

    void setCoachMarkMessage(String message);

    void setPopUpViewPositionWithRespectToImage(@CoachMarkAlignPosition int alignPosition);

    void startAnimationOnImage(@AnimRes int animationRes);

} @ParametersAreNonnullByDefault
public class PopUpCoachMarkPresenter {

    private static final String TAG = PopUpCoachMarkPresenter.class.getSimpleName();

    private static final double MAX_NOTCH_RANGE = .85;
    private static final double MIN_NOTCH_RANGE = 0.0;

    private static final int NO_MARGIN = 0;

    private static final float ROTATION_0 = 0;
    private static final float ROTATION_90 = 90;
    private static final float ROTATION_180 = 180;
    private static final float ROTATION_270 = 270;

    private static final double MULTIPLICATION_FACTOR_NOTCH_POSITION = 1.8;
    private static final double MULTIPLICATION_FACTOR_FOR_NOTCH_UI_ADJUSTMENT_BOTTOM = 2.9;
    private static final double MULTIPLICATION_FACTOR_FOR_UI_ADJUSTMENT_RIGHT = 0.4;
    private static final double PIXEL_ADJUSTMENT_NOTCH = 0.5;


    private CoachMarkBuilder mCoachMarkBuilder;
    private IPopUpCoachMarkPresentation mPresentation;

    private ITypeFaceProvider mTypeFaceProvider;
    private IScreenInfoProvider mScreenInfoProvider;
    private IStringResourceProvider mStringResourceProvider;
    private IDimensionResourceProvider mDimensionResourceProvider;

    public PopUpCoachMarkPresenter(final IStringResourceProvider stringResourceProvider,
                                   final IDimensionResourceProvider dimensionResourceProvider,
                                   final ITypeFaceProvider typeFaceProvider,
                                   final IScreenInfoProvider screenInfoProvider) {
        mStringResourceProvider = stringResourceProvider;
        mDimensionResourceProvider = dimensionResourceProvider;
        mTypeFaceProvider = typeFaceProvider;
        mScreenInfoProvider = screenInfoProvider;
    }

    public void onCreate(CoachMarkBuilder coachMarkBuilder) {
        mCoachMarkBuilder = coachMarkBuilder;
    }

    public void onCreateView(IPopUpCoachMarkPresentation popUpCoachMarkPresentation) {
        mPresentation = popUpCoachMarkPresentation;
    }

    public void onViewCreated() {
        displayCoachMark();
    }

    public void onDestroyView() {
        mPresentation = null;
    }

    public void onDestroy() {
        mScreenInfoProvider = null;
        mDimensionResourceProvider = null;
        mStringResourceProvider = null;
        mTypeFaceProvider = null;
    }

    public void onOkButtonClicked() {
        mPresentation.closeCoachMarkDialog();
    }

    public void onShimClicked() {
        if (mPresentation != null) {
            mPresentation.closeCoachMarkDialog();
        }
    }

    //region helper methods
    protected void displayCoachMark() {
        CoachMarkPixelInfo coachMarkDimenInPixel = createCoachMarkPixelInfo();
        @PopUpPosition
        int popUpPosition = findCoachMarkTextPopUpDisplayPosition(mCoachMarkBuilder.getAnchorTop(),
                mCoachMarkBuilder.getAnchorBottom(),
                mCoachMarkBuilder.getUserDesiredPopUpPositionWithRespectToView(),
                coachMarkDimenInPixel);

        if (popUpPosition == PopUpPosition.NONE) {
            mPresentation.dismissWithError(
                    mStringResourceProvider.getStringFromResource(R.string.coachmark_no_position_found));
            return;
        }

        setTypeFaceForDismissButton(mCoachMarkBuilder.getFontStyleForDismissButton());
        setTypeFaceForCoachMarkText(mCoachMarkBuilder.getFontStyleForCoachMarkText());
        setGravityForCoachMarkText(mCoachMarkBuilder.getCoachMarkTextGravity());
        setMessageForCoachMarkText(mCoachMarkBuilder.getCoachMarkMessage());

        setNotchDisplayEdge(popUpPosition,
                mCoachMarkBuilder.getAnchorTop().y,
                mCoachMarkBuilder.getAnchorBottom().y,
                mCoachMarkBuilder.getAnchorTop().x,
                coachMarkDimenInPixel);
        detectAndCreateShimOutViews(mCoachMarkBuilder.getInfoForViewToMaskList());
        setImageParamsAndPosition(mCoachMarkBuilder.getAnchorTop(),
                mCoachMarkBuilder.getAnchorBottom(),
                coachMarkDimenInPixel.getImageWidthInPixels(),
                coachMarkDimenInPixel.getImageHeightInPixels(),
                mCoachMarkBuilder.getBackGroundTintForImage(),
                mCoachMarkBuilder.getImageDrawableRes());
        mPresentation.startAnimationOnImage(mCoachMarkBuilder.getAnimationOnImage());
        showCoachMark(mCoachMarkBuilder.getPopUpCoachMarkDismissButtonPosition(),
                popUpPosition);
    }

    protected void setMessageForCoachMarkText(@StringRes int messageForCoachMarkTextRes) {
        mPresentation.setCoachMarkMessage(
                mStringResourceProvider.getStringFromResource(messageForCoachMarkTextRes));
    }

    protected void setTypeFaceForDismissButton(@Nullable String fontFileForDismissButton) {
        if (StringUtils.isNull(fontFileForDismissButton)) {
            return;
        }
        Typeface typeface = mTypeFaceProvider.getTypeFaceFromAssets(fontFileForDismissButton);
        if (typeface != null) {
            mPresentation.setTypeFaceForDismissButton(typeface);
        }

    }

    protected void setTypeFaceForCoachMarkText(@Nullable String fontFileForPopUpText) {
        if (StringUtils.isNotNullOrEmpty(fontFileForPopUpText)) {
            Typeface typeface = mTypeFaceProvider.getTypeFaceFromAssets(fontFileForPopUpText);
            if (typeface != null) {
                mPresentation.setTypeFaceForPopUpText(typeface);
            }
        }
    }

    protected void setGravityForCoachMarkText(int textAlignmentForPopUpText) {
        switch (textAlignmentForPopUpText) {
            case CoachMarkTextGravity.CENTER:
                mPresentation.setUpGravityForCoachMarkText(CoachMarkTextGravity.CENTER);
                break;
            case CoachMarkTextGravity.LEFT:
                mPresentation.setUpGravityForCoachMarkText(CoachMarkTextGravity.LEFT);
                break;
            case CoachMarkTextGravity.RIGHT:
                mPresentation.setUpGravityForCoachMarkText(CoachMarkTextGravity.RIGHT);
                break;
        }
    }

    protected void setNotchDisplayEdge(int position,
                                     int anchorTopY,
                                     int anchorBottomY,
                                     int anchorTopX,
                                     CoachMarkPixelInfo coachMarkDimenInPixel) {
        int notchPosition;
        int actualTopMargin;
        int actualLeftMargin;
        int centerY = (anchorTopY + anchorBottomY) / 2;

        Rect notchMarginRect;
        Rect coachMarkMarginRect;
        Rect notchUiAdjustmentMarginRect;

        switch (position) {
            case PopUpPosition.LEFT:
                actualTopMargin = getActualTopMargin(centerY, coachMarkDimenInPixel);
                coachMarkMarginRect = new Rect(coachMarkDimenInPixel.getMarginRectInPixels().left,
                        actualTopMargin - coachMarkDimenInPixel.getMarginRectInPixels().bottom,
                        coachMarkDimenInPixel.getMarginRectInPixels().right +
                                coachMarkDimenInPixel.getImageWidthInPixels(),
                        NO_MARGIN);
                mPresentation.setPopUpViewTopLeft(coachMarkMarginRect,
                        CoachMarkLayoutOrientation.HORIZONTAL);
                notchPosition = getMarginTopForNotch(mCoachMarkBuilder.getNotchPosition(),
                        coachMarkDimenInPixel.getPopUpHeightInPixels(),
                        coachMarkDimenInPixel.getNotchDimenInPixels());
                notchMarginRect = new Rect(-coachMarkDimenInPixel.getMarginOffsetForNotchInPixels(),
                        notchPosition, NO_MARGIN, NO_MARGIN);
                mPresentation.setNotchPositionIfPopUpTopLeft(notchMarginRect, ROTATION_90);
                break;
            case PopUpPosition.TOP:
                actualLeftMargin = getActualLeftMargin(anchorTopX, coachMarkDimenInPixel);
                coachMarkMarginRect = new Rect(actualLeftMargin -
                        coachMarkDimenInPixel.getMarginRectInPixels().right,
                        coachMarkDimenInPixel.getMarginRectInPixels().top,
                        NO_MARGIN,
                        coachMarkDimenInPixel.getMarginRectInPixels().bottom +
                                coachMarkDimenInPixel.getImageHeightInPixels());
                mPresentation.setPopUpViewTopLeft(coachMarkMarginRect,
                        CoachMarkLayoutOrientation.VERTICAL);
                notchPosition = getMarginLeftForNotch(mCoachMarkBuilder.getNotchPosition(),
                        coachMarkDimenInPixel.getPopUpWidthInPixels(),
                        coachMarkDimenInPixel.getNotchDimenInPixels());
                notchMarginRect = new Rect(notchPosition +
                        coachMarkDimenInPixel.getMarginOffsetForNotchInPixels(),
                        -coachMarkDimenInPixel.getMarginOffsetForNotchInPixels(),
                        NO_MARGIN, NO_MARGIN);
                mPresentation.setNotchPositionIfPopUpTopLeft(notchMarginRect, ROTATION_180);
                break;
            case PopUpPosition.RIGHT:
                actualTopMargin = getActualTopMargin(centerY, coachMarkDimenInPixel);
                coachMarkMarginRect = new Rect(coachMarkDimenInPixel.getMarginRectInPixels().left +
                        coachMarkDimenInPixel.getImageWidthInPixels(),
                        actualTopMargin - coachMarkDimenInPixel.getMarginRectInPixels().bottom,
                        coachMarkDimenInPixel.getMarginRectInPixels().right,
                        NO_MARGIN);
                mPresentation.setPopUpViewBottomRight(coachMarkMarginRect,
                        CoachMarkLayoutOrientation.HORIZONTAL);
                notchPosition = getMarginTopForNotch(mCoachMarkBuilder.getNotchPosition(),
                        coachMarkDimenInPixel.getPopUpHeightInPixels(),
                        coachMarkDimenInPixel.getNotchDimenInPixels());
                notchMarginRect = new Rect(NO_MARGIN,
                        notchPosition - (int) (MULTIPLICATION_FACTOR_NOTCH_POSITION * coachMarkDimenInPixel.getMarginOffsetForNotchInPixels()),
                        NO_MARGIN,
                        NO_MARGIN);
                mPresentation.setNotchPositionIfPopUpBottomRight(notchMarginRect, ROTATION_270);
                notchUiAdjustmentMarginRect = new Rect(NO_MARGIN,
                        notchPosition + (int) (MULTIPLICATION_FACTOR_FOR_UI_ADJUSTMENT_RIGHT * coachMarkDimenInPixel.getMarginOffsetForNotchInPixels() + PIXEL_ADJUSTMENT_NOTCH),
                        NO_MARGIN,
                        NO_MARGIN);
                mPresentation.uiAdjustmentForNotchIfPopUpRight(notchUiAdjustmentMarginRect);
                break;
            case PopUpPosition.BOTTOM:
                actualLeftMargin = getActualLeftMargin(anchorTopX, coachMarkDimenInPixel);
                coachMarkMarginRect = new Rect(actualLeftMargin -
                        coachMarkDimenInPixel.getMarginRectInPixels().right,
                        coachMarkDimenInPixel.getMarginRectInPixels().top +
                                coachMarkDimenInPixel.getImageHeightInPixels(),
                        NO_MARGIN,
                        coachMarkDimenInPixel.getMarginRectInPixels().bottom);
                mPresentation.setPopUpViewBottomRight(coachMarkMarginRect,
                        CoachMarkLayoutOrientation.VERTICAL);
                notchPosition = getMarginLeftForNotch(mCoachMarkBuilder.getNotchPosition(),
                        coachMarkDimenInPixel.getPopUpWidthInPixels(),
                        coachMarkDimenInPixel.getNotchDimenInPixels());
                notchMarginRect = new Rect(notchPosition +
                        coachMarkDimenInPixel.getMarginOffsetForNotchInPixels(),
                        NO_MARGIN,
                        NO_MARGIN,
                        NO_MARGIN);
                mPresentation.setNotchPositionIfPopUpBottomRight(notchMarginRect, ROTATION_0);
                notchUiAdjustmentMarginRect = new Rect(notchPosition +
                        (int) (MULTIPLICATION_FACTOR_FOR_NOTCH_UI_ADJUSTMENT_BOTTOM *
                                coachMarkDimenInPixel.getMarginOffsetForNotchInPixels()),
                        NO_MARGIN,
                        NO_MARGIN,
                        NO_MARGIN);
                mPresentation.uiAdjustmentForNotchIfPopUpBottom(notchUiAdjustmentMarginRect);
                break;
        }
    }

    protected int getMarginLeftForNotch(double notchPosition, int popUpWidth, int notchDimen) {
        if (notchPosition > MAX_NOTCH_RANGE) {
            notchPosition = MAX_NOTCH_RANGE;
        }
        if (notchPosition <= MIN_NOTCH_RANGE) {
            notchPosition = MIN_NOTCH_RANGE;
        }

        int marginLeft = (int) (popUpWidth * notchPosition);
        if (marginLeft > popUpWidth - notchDimen) {
            return (popUpWidth - notchDimen);
        }
        return marginLeft;
    }

    protected int getMarginTopForNotch(double notchPosition, int popUpHeight, int notchDimen) {
        if (notchPosition > MAX_NOTCH_RANGE) {
            notchPosition = MAX_NOTCH_RANGE;
        }
        if (notchPosition <= MIN_NOTCH_RANGE) {
            notchPosition = MIN_NOTCH_RANGE;
        }

        int marginTop = (int) (popUpHeight * notchPosition);
        if (marginTop > popUpHeight - notchDimen) {
            return (popUpHeight - notchDimen);
        }
        return marginTop;
    }

    protected void detectAndCreateShimOutViews(@Nullable List<InfoForViewToMask> infoForViewToMaskList) {
        if (infoForViewToMaskList == null) {
            return;
        }
        for (InfoForViewToMask infoForViewToMask : infoForViewToMaskList) {
            mPresentation.createViewToBeMaskedOut(
                    infoForViewToMask.getViewToMaskStartPosition().x,
                    infoForViewToMask.getViewToMaskStartPosition().y,
                    infoForViewToMask.getViewToMaskHeight(),
                    infoForViewToMask.getViewToMaskWidth());
        }
    }

    @PopUpPosition
    protected int findCoachMarkTextPopUpDisplayPosition(Point anchorTop,
                                                      Point anchorBottom,
                                                      @PopUpPosition int defaultPopUpPosition,
                                                      CoachMarkPixelInfo coachMarkDimenInPixel) {
        int centerX = (anchorTop.x + anchorBottom.x) / 2;
        int centerY = (anchorTop.y + anchorBottom.y) / 2;
        Point centerViewPoint = new Point(centerX, centerY);
        return getDisplayPosition(centerViewPoint,
                defaultPopUpPosition, coachMarkDimenInPixel);
    }


    protected void setImageParamsAndPosition(Point anchorTop, Point anchorBottom,
                                           int imageWidth, int imageHeight,
                                           @ColorRes int backGroundTintForImage,
                                           @DrawableRes int imageDrawableRes) {
        double centerX = (anchorTop.x + anchorBottom.x) / 2;
        double centerY = (anchorTop.y + anchorBottom.y) / 2;
        mPresentation.setImageInformation(centerX, centerY, imageWidth,
                imageHeight, backGroundTintForImage, imageDrawableRes);
    }

    protected void showCoachMark(@DialogDismissButtonPosition int dismissButtonPosition,
                               @PopUpPosition int popUpPosition) {

        switch (popUpPosition) {
            case PopUpPosition.LEFT:
                mPresentation.setPopUpViewPositionWithRespectToImage(
                        CoachMarkAlignPosition.ALIGN_RIGHT);
                break;
            case PopUpPosition.TOP:
                mPresentation.setPopUpViewPositionWithRespectToImage(
                        CoachMarkAlignPosition.ALIGN_BOTTOM);
                break;
            case PopUpPosition.RIGHT:
                mPresentation.setPopUpViewPositionWithRespectToImage(
                        CoachMarkAlignPosition.ALIGN_LEFT);
                break;
            case PopUpPosition.BOTTOM:
                mPresentation.setPopUpViewPositionWithRespectToImage(
                        CoachMarkAlignPosition.ALIGN_TOP);
                break;
            case PopUpPosition.NONE:
                Log.wtf(TAG, "This should not have happened here. No position found case already handled");
                break;
        }

        if(popUpPosition == PopUpPosition.NONE){
            return;
        }

        switch (dismissButtonPosition) {
            case DialogDismissButtonPosition.LEFT:
                mPresentation.setDismissButtonPositionLeft();
                break;
            case DialogDismissButtonPosition.RIGHT:
                mPresentation.setDismissButtonPositionRight();
                break;
        }
    }

    @PopUpPosition
    protected int getDisplayPosition(Point viewCenterPoint,
                                   @PopUpPosition int defaultPopUpPosition,
                                   CoachMarkPixelInfo coachMarkDimenInPixel) {
        @PopUpPosition
        int correctPosition = 0;
        switch (defaultPopUpPosition) {
            case PopUpPosition.LEFT:
                if (checkIfLeftPossible(viewCenterPoint, coachMarkDimenInPixel)) {
                    correctPosition = defaultPopUpPosition;
                } else {
                    correctPosition = getCorrectPositionOfCoachMarkIfDefaultFails(viewCenterPoint,
                            coachMarkDimenInPixel);
                }
                break;
            case PopUpPosition.RIGHT:
                if (checkIfRightPossible(viewCenterPoint, coachMarkDimenInPixel)) {
                    correctPosition = defaultPopUpPosition;
                } else {
                    correctPosition = getCorrectPositionOfCoachMarkIfDefaultFails(viewCenterPoint,
                            coachMarkDimenInPixel);
                }
                break;

            case PopUpPosition.BOTTOM:
                if (checkIfBottomPossible(viewCenterPoint, coachMarkDimenInPixel)) {
                    correctPosition = defaultPopUpPosition;
                } else {
                    correctPosition = getCorrectPositionOfCoachMarkIfDefaultFails(viewCenterPoint,
                            coachMarkDimenInPixel);
                }
                break;

            case PopUpPosition.TOP:
                if (checkIfTopPossible(viewCenterPoint, coachMarkDimenInPixel)) {
                    correctPosition = defaultPopUpPosition;
                } else {
                    correctPosition = getCorrectPositionOfCoachMarkIfDefaultFails(viewCenterPoint,
                            coachMarkDimenInPixel);
                }
                break;
            case PopUpPosition.NONE:
                //if user selects no position by default check clockwise to find the correct position
                correctPosition = getCorrectPositionOfCoachMarkIfDefaultFails(viewCenterPoint,
                        coachMarkDimenInPixel);
                break;
        }
        return correctPosition;
    }

    protected boolean checkIfLeftPossible(Point viewCenterPoint,
                                        CoachMarkPixelInfo coachMarkDimenInPixel) {
        int centerX = viewCenterPoint.x;
        return (coachMarkDimenInPixel.getPopUpWidthInPixelsWithOffset()) < centerX &&
                ((coachMarkDimenInPixel.getPopUpHeightInPixelsWithOffset() +
                        coachMarkDimenInPixel.getMarginRectInPixels().top +
                        coachMarkDimenInPixel.getActionBarHeightPixels() +
                        coachMarkDimenInPixel.getFooterHeightPixels() +
                        coachMarkDimenInPixel.getMarginRectInPixels().bottom)
                        <= coachMarkDimenInPixel.getScreenHeightInPixels());
    }

    protected boolean checkIfRightPossible(Point viewCenterPoint,
                                         CoachMarkPixelInfo coachMarkDimenInPixel) {
        int centerX = viewCenterPoint.x;
        return (coachMarkDimenInPixel.getPopUpWidthInPixelsWithOffset()) <=
                (coachMarkDimenInPixel.getScreenWidthInPixels() - centerX) &&
                ((coachMarkDimenInPixel.getPopUpHeightInPixelsWithOffset() +
                        coachMarkDimenInPixel.getMarginRectInPixels().top +
                        coachMarkDimenInPixel.getActionBarHeightPixels() +
                        coachMarkDimenInPixel.getFooterHeightPixels() +
                        coachMarkDimenInPixel.getMarginRectInPixels().bottom) <=
                        coachMarkDimenInPixel.getScreenHeightInPixels());
    }

    protected boolean checkIfTopPossible(Point viewCenterPoint,
                                       CoachMarkPixelInfo coachMarkDimenInPixel) {
        int centerY = viewCenterPoint.y;
        return (coachMarkDimenInPixel.getPopUpHeightInPixelsWithOffset() +
                coachMarkDimenInPixel.getMarginRectInPixels().top +
                coachMarkDimenInPixel.getMarginRectInPixels().bottom +
                coachMarkDimenInPixel.getActionBarHeightPixels()) <=
                centerY && ((coachMarkDimenInPixel.getWidthHeightOffsetForCoachMarkPopUp()
                + coachMarkDimenInPixel.getMarginRectInPixels().left +
                coachMarkDimenInPixel.getMarginRectInPixels().right) <=
                coachMarkDimenInPixel.getScreenWidthInPixels());
    }

    protected boolean checkIfBottomPossible(Point viewCenterPoint,
                                          CoachMarkPixelInfo coachMarkDimenInPixel) {
        int centerY = viewCenterPoint.y;
        return (coachMarkDimenInPixel.getPopUpHeightInPixelsWithOffset() +
                coachMarkDimenInPixel.getMarginRectInPixels().top +
                coachMarkDimenInPixel.getMarginRectInPixels().bottom +
                coachMarkDimenInPixel.getFooterHeightPixels()) <=
                coachMarkDimenInPixel.getScreenHeightInPixels() - centerY &&
                ((coachMarkDimenInPixel.getPopUpWidthInPixelsWithOffset()
                        + coachMarkDimenInPixel.getMarginRectInPixels().left +
                        coachMarkDimenInPixel.getMarginRectInPixels().right) <=
                        coachMarkDimenInPixel.getScreenWidthInPixels());
    }

    @PopUpPosition
    protected int getCorrectPositionOfCoachMarkIfDefaultFails(Point viewCenterPoint,
                                                            CoachMarkPixelInfo coachMarkDimenInPixel) {

        @PopUpPosition
        int correctPopUpPosition = PopUpPosition.NONE;

        if (checkIfRightPossible(viewCenterPoint, coachMarkDimenInPixel))
            correctPopUpPosition = PopUpPosition.RIGHT;
        else if (checkIfBottomPossible(viewCenterPoint, coachMarkDimenInPixel))
            correctPopUpPosition = PopUpPosition.BOTTOM;
        else if (checkIfLeftPossible(viewCenterPoint, coachMarkDimenInPixel))
            correctPopUpPosition = PopUpPosition.LEFT;
        else if (checkIfTopPossible(viewCenterPoint, coachMarkDimenInPixel))
            correctPopUpPosition = PopUpPosition.TOP;

        return correctPopUpPosition;
    }

    protected CoachMarkPixelInfo createCoachMarkPixelInfo() {
        final CoachMarkLayoutMargin coachMarkLayoutMargin =
                mCoachMarkBuilder.getCoachMarkLayoutMargin();
        int leftMargin = mDimensionResourceProvider.getDimensionInPixel(
                coachMarkLayoutMargin.getLeftMarginForCoachMark());
        int rightMargin = mDimensionResourceProvider.getDimensionInPixel(
                coachMarkLayoutMargin.getRightMarginForCoachMark());
        int topMargin = mDimensionResourceProvider.getDimensionInPixel(
                coachMarkLayoutMargin.getTopMarginForCoachMark());
        int bottomMargin = mDimensionResourceProvider.getDimensionInPixel(
                coachMarkLayoutMargin.getBottomMarginForCoachMark());

        int actionBarHeightPixels = mDimensionResourceProvider.getDimensionInPixel(
                mCoachMarkBuilder.getActionBarHeight());
        int footerHeightPixels = mDimensionResourceProvider.getDimensionInPixel(
                mCoachMarkBuilder.getFooterHeight());

        int imageHeightInPixels = mDimensionResourceProvider.getDimensionInPixel(
                mCoachMarkBuilder.getImageLayoutInformation().getImageHeight());
        int imageWidthInPixels = mDimensionResourceProvider.getDimensionInPixel(
                mCoachMarkBuilder.getImageLayoutInformation().getImageWidth());

        int coachMarkPopUpHeightInPixels = mDimensionResourceProvider.getDimensionInPixel(R.dimen.coach_mark_pop_up_height);
        int coachMarkPopUpWidthInPixels = mDimensionResourceProvider.getDimensionInPixel(R.dimen.coach_mark_pop_up_width);

        int notchDimenInPixels = mDimensionResourceProvider.getDimensionInPixel(R.dimen.coach_mark_notch_width_height);
        int marginOffsetForNotchInPixels = mDimensionResourceProvider.getDimensionInPixel(R.dimen.coach_mark_notch_margin_offset);

        int widthHeightOffsetForCoachMarkPopUp = mDimensionResourceProvider.getDimensionInPixel(R.dimen.coach_mark_pop_up_width_height_offset);
        int coachMarkPopUpWidthInPixelsWithOffset = coachMarkPopUpWidthInPixels +
                widthHeightOffsetForCoachMarkPopUp + imageWidthInPixels;
        int coachMarkPopUpHeightInPixelsWithOffset = coachMarkPopUpHeightInPixels +
                widthHeightOffsetForCoachMarkPopUp + imageHeightInPixels;

        int screenHeightInPixels = mScreenInfoProvider.getScreenHeightInPixels();
        int screenWidthInPixels = mScreenInfoProvider.getScreenWidthInPixels();

        return CoachMarkPixelInfo.create()
                .setMarginRectInPixels(new Rect(leftMargin, topMargin, rightMargin, bottomMargin))
                .setPopUpHeightInPixels(coachMarkPopUpHeightInPixels)
                .setPopUpHeightInPixelsWithOffset(coachMarkPopUpHeightInPixelsWithOffset)
                .setPopUpWidthInPixels(coachMarkPopUpWidthInPixels)
                .setPopUpWidthInPixelsWithOffset(coachMarkPopUpWidthInPixelsWithOffset)
                .setWidthHeightOffsetForCoachMarkPopUp(widthHeightOffsetForCoachMarkPopUp)
                .setActionBarHeightPixels(actionBarHeightPixels)
                .setFooterHeightPixels(footerHeightPixels)
                .setNotchDimenInPixels(notchDimenInPixels)
                .setMarginOffsetForNotchInPixels(marginOffsetForNotchInPixels)
                .setImageHeightInPixels(imageHeightInPixels)
                .setImageWidthInPixels(imageWidthInPixels)
                .setScreenHeightInPixels(screenHeightInPixels)
                .setScreenWidthInPixels(screenWidthInPixels)
                .build();
    }

    protected int getActualTopMargin(int centerTopY, CoachMarkPixelInfo coachMarkDimenInPixel) {
        if (centerTopY + coachMarkDimenInPixel.getPopUpHeightInPixels() +
                coachMarkDimenInPixel.getWidthHeightOffsetForCoachMarkPopUp() +
                coachMarkDimenInPixel.getMarginRectInPixels().top +
                coachMarkDimenInPixel.getMarginRectInPixels().bottom >
                coachMarkDimenInPixel.getScreenHeightInPixels()) {
            return coachMarkDimenInPixel.getScreenHeightInPixels() -
                    coachMarkDimenInPixel.getPopUpHeightInPixels() +
                    coachMarkDimenInPixel.getWidthHeightOffsetForCoachMarkPopUp() +
                    coachMarkDimenInPixel.getMarginRectInPixels().top +
                    coachMarkDimenInPixel.getMarginRectInPixels().bottom;
        }
        return centerTopY + coachMarkDimenInPixel.getMarginRectInPixels().top;
    }

    protected int getActualLeftMargin(int anchorTopX, CoachMarkPixelInfo coachMarkDimenInPixel) {
        if (anchorTopX + coachMarkDimenInPixel.getPopUpWidthInPixels() +
                coachMarkDimenInPixel.getWidthHeightOffsetForCoachMarkPopUp() +
                coachMarkDimenInPixel.getMarginRectInPixels().right +
                coachMarkDimenInPixel.getMarginRectInPixels().left >
                coachMarkDimenInPixel.getScreenWidthInPixels()) {
            return coachMarkDimenInPixel.getScreenWidthInPixels() -
                    coachMarkDimenInPixel.getPopUpWidthInPixels() +
                    coachMarkDimenInPixel.getWidthHeightOffsetForCoachMarkPopUp() +
                    coachMarkDimenInPixel.getMarginRectInPixels().right +
                    coachMarkDimenInPixel.getMarginRectInPixels().left;
        }
        return anchorTopX + coachMarkDimenInPixel.getMarginRectInPixels().left;
    }
    //endregion
}
import android.app.Dialog;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.annotation.AnimRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.AppCompatTextView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.f2prateek.bundler.FragmentBundlerCompat;
import com.myntra.coachmarks.builder.CoachMarkBuilder;
import com.myntra.coachmarks.common.CoachMarkAlignPosition;
import com.myntra.coachmarks.common.CoachMarkLayoutOrientation;
import com.myntra.coachmarks.common.CoachMarkTextGravity;
import com.myntra.coachmarks.providers.DefaultDimensionResourceProvider;
import com.myntra.coachmarks.providers.DefaultScreenInfoProvider;
import com.myntra.coachmarks.providers.DefaultStringResourceProvider;
import com.myntra.coachmarks.providers.DefaultTypeFaceProvider;
import com.myntra.coachmarks.providers.interfaces.IDimensionResourceProvider;
import com.myntra.coachmarks.providers.interfaces.IScreenInfoProvider;
import com.myntra.coachmarks.providers.interfaces.IStringResourceProvider;
import com.myntra.coachmarks.providers.interfaces.ITypeFaceProvider;
import com.myntra.coachmarks.ui.common.BaseViews;
import com.myntra.coachmarks.ui.presentation.IPopUpCoachMarkPresentation;
import com.myntra.coachmarks.ui.presenter.PopUpCoachMarkPresenter;
import butterknife.BindView;
import zeta.android.utils.view.ViewUtils;
package com.myntra.coachmarks;





public class PopUpCoachMark extends DialogFragment implements IPopUpCoachMarkPresentation, View.OnClickListener {

    private static final String TAG = PopUpCoachMark.class.getSimpleName();

    private static final int NO_MARGIN = 0;
    private static final String ARG_POP_UP_COACH_MARK_BUILDER_PARCEL = "ARG_POP_UP_COACH_MARK_BUILDER_PARCEL";

    private Views mViews;
    private PopUpCoachMarkPresenter mPresenter;

    static class Views extends BaseViews {

        @BindView(R2.id.rl_coachmark_parent)
        RelativeLayout rlCoachMarkParent;

        @BindView(R2.id.tv_coachmark_text)
        AppCompatTextView tvPopUpCoachMarkText;

        @BindView(R2.id.tv_ok_button)
        AppCompatTextView tvPopUpDismissButton;

        @BindView(R2.id.v_separator)
        ImageView vSeparator;

        @BindView(R2.id.iv_coachmark_image)
        ImageView ivCoachMarkImage;

        @BindView(R2.id.rl_shim_out_view_parent)
        FrameLayout rlShimOutViewParent;

        @BindView(R2.id.rl_coachmark_text_part)
        RelativeLayout rlCoachMarkTextPart;

        @BindView(R2.id.ll_coach_mark_pop_up_parent)
        LinearLayout llPopUpCoachMarkParent;

        @BindView(R2.id.right_bottom_notch)
        View vRightBottomNotch;

        @BindView(R2.id.left_top_notch)
        View vLeftTopNotch;

        @BindView(R2.id.v_notch_base_white_left)
        View vNotchBaseWhiteLeft;

        @BindView(R2.id.v_notch_base_white_top)
        View vNotchBaseWhiteTop;

        @BindView(R2.id.ll_coachmark_text_wrapper_layout)
        LinearLayout llCoachMarkTextWrapperLayout;

        Views(@NonNull View root) {
            super(root);
        }
    }

    public static PopUpCoachMark newInstance(CoachMarkBuilder builder) {
        return FragmentBundlerCompat.create(new PopUpCoachMark())
                .put(ARG_POP_UP_COACH_MARK_BUILDER_PARCEL, builder)
                .build();
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog = super.onCreateDialog(savedInstanceState);
        Window window = dialog.getWindow();
        if (window == null) {
            Log.wtf(TAG, "getWindow() should not be null ever");
            return dialog;
        }
        window.getAttributes().windowAnimations = R.style.coach_mark_dialog_animation;
        window.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(getContext(),
                R.color.coach_mark_transparent_color)));
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        return dialog;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(STYLE_NO_FRAME, R.style.AppTheme);

        final Context context = getContext();
        final IStringResourceProvider stringResourceProvider = new DefaultStringResourceProvider(context);
        final IDimensionResourceProvider dimensionResourceProvider = new DefaultDimensionResourceProvider(context);
        final ITypeFaceProvider typeFaceProvider = new DefaultTypeFaceProvider(context);