sjwall/MaterialTapTargetPrompt

View on GitHub
library/src/main/java/uk/co/samuelwall/materialtaptargetprompt/extras/focals/RectanglePromptFocal.java

Summary

Maintainability
A
0 mins
Test Coverage
/*
 * Copyright (C) 2017 Samuel Wall
 *
 * 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 uk.co.samuelwall.materialtaptargetprompt.extras.focals;

import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import androidx.annotation.ColorInt;
import androidx.annotation.Dimension;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.view.View;

import uk.co.samuelwall.materialtaptargetprompt.extras.PromptFocal;
import uk.co.samuelwall.materialtaptargetprompt.extras.PromptOptions;
import uk.co.samuelwall.materialtaptargetprompt.extras.PromptUtils;

/**
 * Prompt focal implementation to draw the focal as a rectangle.
 */
public class RectanglePromptFocal extends PromptFocal
{
    Paint mPaint;
    // Paint mBoundsPaint;
    int mRippleAlpha;
    RectF mBounds;
    RectF mBaseBounds;
    PointF mBaseBoundsCentre;
    RectF mRippleBounds;
    int mBaseAlpha;
    float mPadding;
    Path mPath;
    private float mRx, mRy;
    @Nullable private PointF mSize;

    /**
     * Constructor.
     */
    public RectanglePromptFocal()
    {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        /*mBoundsPaint = new Paint();
        mBoundsPaint.setColor(Color.BLUE);
        mBoundsPaint.setAlpha(100);*/
        mBounds = new RectF();
        mBaseBounds = new RectF();
        mBaseBoundsCentre = new PointF();
        mRippleBounds = new RectF();
        final float density = Resources.getSystem().getDisplayMetrics().density;
        mRx = mRy = 2 * density;
        mPadding = 8 * density;
    }

    /**
     * Set the radius for the rectangle corners.
     *
     * @param rx The x-radius of the oval used to round the corners
     * @param ry The y-radius of the oval used to round the corners
     * @return This prompt focal
     */
    @NonNull
    public RectanglePromptFocal setCornerRadius(final float rx, final float ry)
    {
        mRx = rx;
        mRy = ry;
        return this;
    }

    /**
     * Set the padding between the target bounds and the rectangle edge.
     *
     * @param padding The distance from the target edge to the rectangle edge.
     * @return This prompt focal.
     */
    @NonNull
    public RectanglePromptFocal setTargetPadding(@Dimension final float padding)
    {
        mPadding = padding;
        return this;
    }

    /**
     * Sets the focal to a fixed size. Set as null to change it back to dynamic.
     *
     * @param size The fixed focal size or null to change back to the default dynamic size.
     * @return This prompt focal
     */
    @NonNull
    public RectanglePromptFocal setSize(@Nullable final PointF size)
    {
        if (size == null)
        {
            mSize = null;
        }
        else
        {
            mSize = new PointF();
            mSize.x = size.x;
            mSize.y = size.y;
        }
        return this;
    }

    @NonNull
    @Override
    public RectF getBounds()
    {
        return mBaseBounds;
    }

    @NonNull
    @Override
    public Path getPath()
    {
        return mPath;
    }

    @Override
    public void setColour(@ColorInt int colour)
    {
        mPaint.setColor(colour);
        mBaseAlpha = Color.alpha(colour);
        mPaint.setAlpha(mBaseAlpha);
    }

    @Override
    public void prepare(@NonNull PromptOptions options, @NonNull View target, final int[] promptViewPosition)
    {
        final int[] targetPosition = new int[2];
        target.getLocationInWindow(targetPosition);
        final float left = targetPosition[0] - promptViewPosition[0];
        final float top = targetPosition[1] - promptViewPosition[1];
        final int width = target.getWidth();
        final int height = target.getHeight();
        if (mSize == null)
        {
            mBaseBounds.left = left - mPadding;
            mBaseBounds.top = top - mPadding;
            mBaseBounds.right = left + width + mPadding;
            mBaseBounds.bottom = top + height + mPadding;
            mBaseBoundsCentre.x = left + (width / 2);
            mBaseBoundsCentre.y = top + (height / 2);
        }
        else
        {
            prepare(options, left + (width / 2), top + (height / 2));
        }
    }

    @Override
    public void prepare(@NonNull PromptOptions options, float targetX, float targetY)
    {
        if (mSize != null)
        {
            final float halfWidth = mSize.x / 2;
            final float halfHeight = mSize.y / 2;
            mBaseBounds.left = targetX - halfWidth - mPadding;
            mBaseBounds.top = targetY - halfHeight - mPadding;
            mBaseBounds.right = targetX + halfWidth + mPadding;
            mBaseBounds.bottom = targetY + halfHeight + mPadding;
            mBaseBoundsCentre.x = targetX;
            mBaseBoundsCentre.y = targetY;
        }
        else
        {
            throw new UnsupportedOperationException("RectanglePromptFocal size must be set using setSize(PointF)");
        }
    }

    @Override
    public void update(@NonNull PromptOptions options, float revealModifier,
                       float alphaModifier)
    {
        PromptUtils.scale(mBaseBoundsCentre, mBaseBounds, mBounds, revealModifier, true);

        mPath = new Path();
        mPath.addRoundRect(mBounds, mRx, mRy, Path.Direction.CW);
    }

    @Override
    public void updateRipple(float revealModifier, final float alphaModifier)
    {
        PromptUtils.scale(mBaseBoundsCentre, mBaseBounds, mRippleBounds, revealModifier, true);
        mRippleAlpha = (int) (mBaseRippleAlpha * alphaModifier);
    }

    @Override
    public void draw(@NonNull Canvas canvas)
    {
        //Draw the ripple
        if (mDrawRipple)
        {
            final int oldAlpha = mPaint.getAlpha();
            final int oldColor = mPaint.getColor();
            if (oldColor == Color.TRANSPARENT)
            {
                mPaint.setColor(Color.WHITE);
            }
            mPaint.setAlpha(mRippleAlpha);
            canvas.drawRoundRect(mRippleBounds, mRx, mRy, mPaint);
            mPaint.setColor(oldColor);
            mPaint.setAlpha(oldAlpha);
        }

        canvas.drawPath(getPath(), mPaint);

        // canvas.drawRoundRect(mBaseBounds, mRx, mRy, mBoundsPaint);
    }

    @Override
    public boolean contains(float x, float y)
    {
        return mBounds.contains(x, y);
    }
}