TapTarget en Sketchware

 En el blog de hoy se encuentra el código para crear un tap target, y que ustedes puedan usarlo en sus aplicaciones cuando los usuarios entren por primera vez a sus aplicaciones.


1.- van a crear un moreblock, el cual, su contenido es explicado en el vídeo de hoy


2.- Van a colocar 2 bloques add source directly


3.- Van a colocar los siguientes códigos, los cuales deben ser separados en cada ASD. En el primero irá el código principal, y en el segundo, las clases que construyen el método:

PD: para que el código no tenga errores de carácteres web, al pegar en el ASD, deben tocar la opción de "pegar como texto sin formato". Así no tendrán ningún problema. 




//Código principal

TapTargetView.showFor(MainActivity.this,


TapTarget.forView(_view,_title,_subtitle).outerCircleColorInt(Color.parseColor("#" + _color.replace("#", ""))).outerCircleAlpha(0.99f).targetCircleColor(android.R.color.white).titleTextSize(24).titleTextColor(android.R.color.white).descriptionTextSize(18).descriptionTextColor(android.R.color.white).textColor(android.R.color.white)

.dimColor(android.R.color.black).drawShadow(true).cancelable(true).tintTarget(true).transparentTarget(true).targetRadius(60),new TapTargetView.Listener() {

@Override public void onTargetClick(TapTargetView view) { super.onTargetClick(view);


}

});


//Código de clases constructoras para el método

}static class UiUtil {
    UiUtil() {
    }
    static int dp(Context context, int val) {
        return (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, val, context.getResources().getDisplayMetrics());
    }
    static int sp(Context context, int val) {
        return (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP, val, context.getResources().getDisplayMetrics());
    }
    static int themeIntAttr(Context context, String attr) {
        final android.content.res.Resources.Theme theme = context.getTheme();
        if (theme == null) {
            return -1;
        }
        final TypedValue value = new TypedValue();
        final int id = context.getResources().getIdentifier(attr, "attr", context.getPackageName());

        if (id == 0) {
            // Not found
            return -1;
        }
        theme.resolveAttribute(id, value, true);
        return value.data;
    }
    static int setAlpha(int argb, float alpha) {
        if (alpha > 1.0f) {
            alpha = 1.0f;
        } else if (alpha <= 0.0f) {
            alpha = 0.0f;
        }
        return ((int) ((argb >>> 24) * alpha) << 24) | (argb & 0x00FFFFFF);
    }
}
		static class FloatValueAnimatorBuilder {

    private final ValueAnimator animator;

    private EndListener endListener;

    interface UpdateListener {
        void onUpdate(float lerpTime);
    }
    interface EndListener {
        void onEnd();
    }
    protected FloatValueAnimatorBuilder() {
        this(false);
    }
    FloatValueAnimatorBuilder(boolean reverse) {
        if (reverse) {
            this.animator = ValueAnimator.ofFloat(1.0f, 0.0f);
        } else {
            this.animator = ValueAnimator.ofFloat(0.0f, 1.0f);
        }
    }
    public FloatValueAnimatorBuilder delayBy(long millis) {
        animator.setStartDelay(millis);
        return this;
    }
    public FloatValueAnimatorBuilder duration(long millis) {
        animator.setDuration(millis);
        return this;
    }
    public FloatValueAnimatorBuilder interpolator(TimeInterpolator lerper) {
        animator.setInterpolator(lerper);
        return this;
    }
    public FloatValueAnimatorBuilder repeat(int times) {
        animator.setRepeatCount(times);
        return this;
    }
    public FloatValueAnimatorBuilder onUpdate(final UpdateListener listener) {
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                listener.onUpdate((float) animation.getAnimatedValue());
            }
        });
        return this;
    }
    public FloatValueAnimatorBuilder onEnd(final EndListener listener) {
        this.endListener = listener;
        return this;
    }
    public ValueAnimator build() {
        if (endListener != null) {
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    endListener.onEnd();
                }
            });
        }
        return animator;
    }
}
		static class ReflectUtil {
    ReflectUtil() {
    }
    static Object getPrivateField(Object source, String fieldName)
            throws NoSuchFieldException, IllegalAccessException {
        final java.lang.reflect.Field objectField = source.getClass().getDeclaredField(fieldName);
        objectField.setAccessible(true);
        return objectField.get(source);
    }
}
		static class TapTarget extends Activity {
    final CharSequence title;
    final CharSequence description;
    float outerCircleAlpha = 0.96f;
    int targetRadius = 44;
    Rect bounds;
    android.graphics.drawable.Drawable icon;
    Typeface titleTypeface;
    Typeface descriptionTypeface;


private int outerCircleColorRes = -1;
    private int targetCircleColorRes = -1;
    private int dimColorRes = -1;
    private int titleTextColorRes = -1;
    private int descriptionTextColorRes = -1;

    private Integer outerCircleColor = null;
    private Integer targetCircleColor = null;
    private Integer dimColor = null;
    private Integer titleTextColor = null;
    private Integer descriptionTextColor = null;

    private int titleTextDimen = -1;
    private int descriptionTextDimen = -1;
    private int titleTextSize = 20;
    private int descriptionTextSize = 18;
    int id = -1;
    boolean drawShadow = false;
    boolean cancelable = true;
    boolean tintTarget = true;
    boolean transparentTarget = false;
    float descriptionTextAlpha = 0.54f;

    public static TapTarget forView(View view, CharSequence title) {
        return forView(view, title, null);
    }
    public static TapTarget forView(View view, CharSequence title, CharSequence description) {
        return new ViewTapTarget(view, title, description);
    }
    public static TapTarget forBounds(Rect bounds, CharSequence title) {
        return forBounds(bounds, title, null);
    }
    public static TapTarget forBounds(Rect bounds, CharSequence title,  CharSequence description) {
        return new TapTarget(bounds, title, description);
    }
    protected TapTarget(Rect bounds, CharSequence title,  CharSequence description) {
        this(title, description);
        if (bounds == null) {
            throw new IllegalArgumentException("Cannot pass null bounds or title");
        }
        this.bounds = bounds;
    }
    protected TapTarget(CharSequence title,  CharSequence description) {
        if (title == null) {
            throw new IllegalArgumentException("Cannot pass null title");
        }
        this.title = title;
        this.description = description;
    }
    public TapTarget transparentTarget(boolean transparent) {
        this.transparentTarget = transparent;
        return this;
    }
    public TapTarget outerCircleColor( int color) {
        this.outerCircleColorRes = color;
        return this;
    }
    public TapTarget outerCircleColorInt( int color) {
        this.outerCircleColor = color;
        return this;
    }
    public TapTarget outerCircleAlpha(float alpha) {
        if (alpha < 0.0f || alpha > 1.0f) {
            throw new IllegalArgumentException("Given an invalid alpha value: " + alpha);
        }
        this.outerCircleAlpha = alpha;
        return this;
    }
    public TapTarget targetCircleColor( int color) {
        this.targetCircleColorRes = color;
        return this;
    }
    public TapTarget targetCircleColorInt( int color) {
        this.targetCircleColor = color;
        return this;
    }
    public TapTarget textColor( int color) {
        this.titleTextColorRes = color;
        this.descriptionTextColorRes = color;
        return this;
    }
    public TapTarget textColorInt( int color) {
        this.titleTextColor = color;
        this.descriptionTextColor = color;
        return this;
    }
    public TapTarget titleTextColor( int color) {
        this.titleTextColorRes = color;
        return this;
    }
    public TapTarget titleTextColorInt( int color) {
        this.titleTextColor = color;
        return this;
    }
    public TapTarget descriptionTextColor( int color) {
        this.descriptionTextColorRes = color;
        return this;
    }
    public TapTarget descriptionTextColorInt( int color) {
        this.descriptionTextColor = color;
        return this;
    }
    public TapTarget textTypeface(Typeface typeface) {
        if (typeface == null) throw new IllegalArgumentException("Cannot use a null typeface");
        titleTypeface = typeface;
        descriptionTypeface = typeface;
        return this;
    }
    public TapTarget titleTypeface(Typeface titleTypeface) {
        if (titleTypeface == null) throw new IllegalArgumentException("Cannot use a null typeface");
        this.titleTypeface = titleTypeface;
        return this;
    }
    public TapTarget descriptionTypeface(Typeface descriptionTypeface) {
        if (descriptionTypeface == null) throw new IllegalArgumentException("Cannot use a null typeface");
        this.descriptionTypeface = descriptionTypeface;
        return this;
    }
    public TapTarget titleTextSize(int sp) {
        if (sp < 0) throw new IllegalArgumentException("Given negative text size");
        this.titleTextSize = sp;
        return this;
    }
    public TapTarget descriptionTextSize(int sp) {
        if (sp < 0) throw new IllegalArgumentException("Given negative text size");
        this.descriptionTextSize = sp;
        return this;
    }
    public TapTarget titleTextDimen( int dimen) {
        this.titleTextDimen = dimen;
        return this;
    }
    public TapTarget descriptionTextAlpha(float descriptionTextAlpha) {
        if (descriptionTextAlpha < 0 || descriptionTextAlpha > 1f) {
            throw new IllegalArgumentException("Given an invalid alpha value: " + descriptionTextAlpha);
        }
        this.descriptionTextAlpha = descriptionTextAlpha;
        return this;
    }
    public TapTarget descriptionTextDimen( int dimen) {
        this.descriptionTextDimen = dimen;
        return this;
    }
    public TapTarget dimColor( int color) {
        this.dimColorRes = color;
        return this;
    }
    public TapTarget dimColorInt( int color) {
        this.dimColor = color;
        return this;
    }
    public TapTarget drawShadow(boolean draw) {
        this.drawShadow = draw;
        return this;
    }
    public TapTarget cancelable(boolean status) {
        this.cancelable = status;
        return this;
    }
    public TapTarget tintTarget(boolean tint) {
        this.tintTarget = tint;
        return this;
    }
    public TapTarget icon(android.graphics.drawable.Drawable icon) {
        return icon(icon, false);
    }
    public TapTarget icon(android.graphics.drawable.Drawable icon, boolean hasSetBounds) {
        if (icon == null) throw new IllegalArgumentException("Cannot use null drawable");
        this.icon = icon;
        if (!hasSetBounds) {
            this.icon.setBounds(new Rect(0, 0, this.icon.getIntrinsicWidth(), this.icon.getIntrinsicHeight()));
        }
        return this;
    }
    public TapTarget id(int id) {
        this.id = id;
        return this;
    }
    public TapTarget targetRadius(int targetRadius) {
        this.targetRadius = targetRadius;
        return this;
    }
    public int id() {
        return id;
    }
    public void onReady(Runnable runnable) {
        runnable.run();
    }
public Rect bounds() {
        if (bounds == null) {
            throw new IllegalStateException("Requesting bounds that are not set! Make sure your target is ready");
        }
        return bounds;
    }
    Integer outerCircleColorInt(Context context) {
        return colorResOrInt(context, outerCircleColor, outerCircleColorRes);
    }
    Integer targetCircleColorInt(Context context) {
        return colorResOrInt(context, targetCircleColor, targetCircleColorRes);
    }
    Integer dimColorInt(Context context) {
        return colorResOrInt(context, dimColor, dimColorRes);
    }
    Integer titleTextColorInt(Context context) {
        return colorResOrInt(context, titleTextColor, titleTextColorRes);
    }

    Integer descriptionTextColorInt(Context context) {
        return colorResOrInt(context, descriptionTextColor, descriptionTextColorRes);
    }
    int titleTextSizePx(Context context) {
        return dimenOrSize(context, titleTextSize, titleTextDimen);
    }
    int descriptionTextSizePx(Context context) {
        return dimenOrSize(context, descriptionTextSize, descriptionTextDimen);
    }

    private Integer colorResOrInt(Context context, Integer value,  int resource) {
        if (resource != -1) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                return context.getColor(resource);
            }
        }
        return value;
    }
    private int dimenOrSize(Context context, int size,  int dimen) {
        if (dimen != -1) {
            return context.getResources().getDimensionPixelSize(dimen);
        }
        return UiUtil.sp(context, size);
    }
}
		static class TapTargetView extends View {
    private boolean isDismissed = false;
    private boolean isDismissing = false;
    private boolean isInteractable = true;

    final int TARGET_PADDING;
    final int TARGET_RADIUS;
    final int TARGET_PULSE_RADIUS;
    final int TEXT_PADDING;
    final int TEXT_SPACING;
    final int TEXT_MAX_WIDTH;
    final int TEXT_POSITIONING_BIAS;
    final int CIRCLE_PADDING;
    final int GUTTER_DIM;
    final int SHADOW_DIM;
    final int SHADOW_JITTER_DIM;


    final ViewGroup boundingParent;
    final ViewManager parent;
    final TapTarget target;
    final Rect targetBounds;

    final TextPaint titlePaint;
    final TextPaint descriptionPaint;
    final Paint outerCirclePaint;
    final Paint outerCircleShadowPaint;
    final Paint targetCirclePaint;
    final Paint targetCirclePulsePaint;

    CharSequence title;

    StaticLayout titleLayout;

    CharSequence description;

    StaticLayout descriptionLayout;
    boolean isDark;
    boolean debug;
    boolean shouldTintTarget;
    boolean shouldDrawShadow;
    boolean cancelable;
    boolean visible;

    // Debug related variables

    SpannableStringBuilder debugStringBuilder;

    DynamicLayout debugLayout;

    TextPaint debugTextPaint;

    Paint debugPaint;

    // Drawing properties
    Rect drawingBounds;
    Rect textBounds;

    Path outerCirclePath;
    float outerCircleRadius;
    int calculatedOuterCircleRadius;
    int[] outerCircleCenter;
    int outerCircleAlpha;

    float targetCirclePulseRadius;
    int targetCirclePulseAlpha;

    float targetCircleRadius;
    int targetCircleAlpha;

    int textAlpha;
    int dimColor;

    float lastTouchX;
    float lastTouchY;

    int topBoundary;
    int bottomBoundary;

    Bitmap tintedTarget;

    Listener listener;


    ViewOutlineProvider outlineProvider;

    public static TapTargetView showFor(Activity activity, TapTarget target) {
        return showFor(activity, target, null);
    }

    public static TapTargetView showFor(Activity activity, TapTarget target, Listener listener) {
        if (activity == null) throw new IllegalArgumentException("Activity is null");

        final ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();
        final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        final ViewGroup content = (ViewGroup) decor.findViewById(android.R.id.content);
        final TapTargetView tapTargetView = new TapTargetView(activity, decor, content, target, listener);
        decor.addView(tapTargetView, layoutParams);

        return tapTargetView;
    }

    public static TapTargetView showFor(Dialog dialog, TapTarget target) {
        return showFor(dialog, target, null);
    }

    public static TapTargetView showFor(Dialog dialog, TapTarget target, Listener listener) {
        if (dialog == null) throw new IllegalArgumentException("Dialog is null");

        final Context context = dialog.getContext();
        final WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        params.type = WindowManager.LayoutParams.TYPE_APPLICATION;
        params.format = PixelFormat.RGBA_8888;
        params.flags = 0;
        params.gravity = Gravity.START | Gravity.TOP;
        params.x = 0;
        params.y = 0;
        params.width = WindowManager.LayoutParams.MATCH_PARENT;
        params.height = WindowManager.LayoutParams.MATCH_PARENT;

        final TapTargetView tapTargetView = new TapTargetView(context, windowManager, null, target, listener);
        windowManager.addView(tapTargetView, params);

        return tapTargetView;
    }
public static class Listener {
        /** Signals that the user has clicked inside of the target **/
        public void onTargetClick(TapTargetView view) {
            view.dismiss(true);
        }

        /** Signals that the user has long clicked inside of the target **/
        public void onTargetLongClick(TapTargetView view) {
            onTargetClick(view);
        }

        /** If cancelable, signals that the user has clicked outside of the outer circle **/
        public void onTargetCancel(TapTargetView view) {
            view.dismiss(false);
        }

        /** Signals that the user clicked on the outer circle portion of the tap target **/
        public void onOuterCircleClick(TapTargetView view) {
            // no-op as default
        }

        /**
         * Signals that the tap target has been dismissed
         * @param userInitiated Whether the user caused this action
         *
         *
         */
        public void onTargetDismissed(TapTargetView view, boolean userInitiated) {
        }
    }

    final FloatValueAnimatorBuilder.UpdateListener expandContractUpdateListener = new FloatValueAnimatorBuilder.UpdateListener() {
        @Override
        public void onUpdate(float lerpTime) {
            final float newOuterCircleRadius = calculatedOuterCircleRadius * lerpTime;
            final boolean expanding = newOuterCircleRadius > outerCircleRadius;
            if (!expanding) {
                // When contracting we need to invalidate the old drawing bounds. Otherwise
                // you will see artifacts as the circle gets smaller
                calculateDrawingBounds();
            }

            final float targetAlpha = target.outerCircleAlpha * 255;
            outerCircleRadius = newOuterCircleRadius;
            outerCircleAlpha = (int) Math.min(targetAlpha, (lerpTime * 1.5f * targetAlpha));
            outerCirclePath.reset();
            outerCirclePath.addCircle(outerCircleCenter[0], outerCircleCenter[1], outerCircleRadius, Path.Direction.CW);

            targetCircleAlpha = (int) Math.min(255.0f, (lerpTime * 1.5f * 255.0f));

            if (expanding) {
                targetCircleRadius = TARGET_RADIUS * Math.min(1.0f, lerpTime * 1.5f);
            } else {
                targetCircleRadius = TARGET_RADIUS * lerpTime;
                targetCirclePulseRadius *= lerpTime;
            }

            textAlpha = (int) (delayedLerp(lerpTime, 0.7f) * 255);

            if (expanding) {
                calculateDrawingBounds();
            }

            invalidateViewAndOutline(drawingBounds);
        }
    };

    final ValueAnimator expandAnimation = new FloatValueAnimatorBuilder()
            .duration(250)
            .delayBy(250)
            .interpolator(new AccelerateDecelerateInterpolator())
            .onUpdate(new FloatValueAnimatorBuilder.UpdateListener() {
                @Override
                public void onUpdate(float lerpTime) {
                    expandContractUpdateListener.onUpdate(lerpTime);
                }
            })
            .onEnd(new FloatValueAnimatorBuilder.EndListener() {
                @Override
                public void onEnd() {
                    pulseAnimation.start();
                    isInteractable = true;
                }
            })
            .build();

    final ValueAnimator pulseAnimation = new FloatValueAnimatorBuilder()
            .duration(1000)
            .repeat(ValueAnimator.INFINITE)
            .interpolator(new AccelerateDecelerateInterpolator())
            .onUpdate(new FloatValueAnimatorBuilder.UpdateListener() {
                @Override
                public void onUpdate(float lerpTime) {
                    final float pulseLerp = delayedLerp(lerpTime, 0.5f);
                    targetCirclePulseRadius = (1.0f + pulseLerp) * TARGET_RADIUS;
                    targetCirclePulseAlpha = (int) ((1.0f - pulseLerp) * 255);
                    targetCircleRadius = TARGET_RADIUS + halfwayLerp(lerpTime) * TARGET_PULSE_RADIUS;

                    if (outerCircleRadius != calculatedOuterCircleRadius) {
                        outerCircleRadius = calculatedOuterCircleRadius;
                    }

                    calculateDrawingBounds();
                    invalidateViewAndOutline(drawingBounds);
                }
            })
            .build();

    final ValueAnimator dismissAnimation = new FloatValueAnimatorBuilder(true)
            .duration(250)
            .interpolator(new AccelerateDecelerateInterpolator())
            .onUpdate(new FloatValueAnimatorBuilder.UpdateListener() {
                @Override
                public void onUpdate(float lerpTime) {
                    expandContractUpdateListener.onUpdate(lerpTime);
                }
            })
            .onEnd(new FloatValueAnimatorBuilder.EndListener() {
                @Override
                public void onEnd() {
                    onDismiss(true);
                    ViewUtil.removeView(parent, TapTargetView.this);
                }
            })
            .build();

    private final ValueAnimator dismissConfirmAnimation = new FloatValueAnimatorBuilder()
            .duration(250)
            .interpolator(new AccelerateDecelerateInterpolator())
            .onUpdate(new FloatValueAnimatorBuilder.UpdateListener() {
                @Override
                public void onUpdate(float lerpTime) {
                    final float spedUpLerp = Math.min(1.0f, lerpTime * 2.0f);
                    outerCircleRadius = calculatedOuterCircleRadius * (1.0f + (spedUpLerp * 0.2f));
                    outerCircleAlpha = (int) ((1.0f - spedUpLerp) * target.outerCircleAlpha * 255.0f);
                    outerCirclePath.reset();
                    outerCirclePath.addCircle(outerCircleCenter[0], outerCircleCenter[1], outerCircleRadius, Path.Direction.CW);
                    targetCircleRadius = (1.0f - lerpTime) * TARGET_RADIUS;
                    targetCircleAlpha = (int) ((1.0f - lerpTime) * 255.0f);
                    targetCirclePulseRadius = (1.0f + lerpTime) * TARGET_RADIUS;
                    targetCirclePulseAlpha = (int) ((1.0f - lerpTime) * targetCirclePulseAlpha);
                    textAlpha = (int) ((1.0f - spedUpLerp) * 255.0f);
                    calculateDrawingBounds();
                    invalidateViewAndOutline(drawingBounds);
                }
            })
            .onEnd(new FloatValueAnimatorBuilder.EndListener() {
                @Override
                public void onEnd() {
                    onDismiss(true);
                    ViewUtil.removeView(parent, TapTargetView.this);
                }
            })
            .build();
private ValueAnimator[] animators = new ValueAnimator[]
            {expandAnimation, pulseAnimation, dismissConfirmAnimation, dismissAnimation};

    private final ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener;
    public TapTargetView(final Context context,
                         final ViewManager parent,
                          final ViewGroup boundingParent,
                         final TapTarget target,
                          final Listener userListener) {
        super(context);
        if (target == null) throw new IllegalArgumentException("Target cannot be null");

        this.target = target;
        this.parent = parent;
        this.boundingParent = boundingParent;
        this.listener = userListener != null ? userListener : new Listener();
        this.title = target.title;
        this.description = target.description;

        TARGET_PADDING = UiUtil.dp(context, 20);
        CIRCLE_PADDING = UiUtil.dp(context, 40);
        TARGET_RADIUS = UiUtil.dp(context, target.targetRadius);
        TEXT_PADDING = UiUtil.dp(context, 40);
        TEXT_SPACING = UiUtil.dp(context, 8);
        TEXT_MAX_WIDTH = UiUtil.dp(context, 360);
        TEXT_POSITIONING_BIAS = UiUtil.dp(context, 20);
        GUTTER_DIM = UiUtil.dp(context, 88);
        SHADOW_DIM = UiUtil.dp(context, 8);
        SHADOW_JITTER_DIM = UiUtil.dp(context, 1);
        TARGET_PULSE_RADIUS = (int) (0.1f * TARGET_RADIUS);

        outerCirclePath = new Path();
        targetBounds = new Rect();
        drawingBounds = new Rect();

        titlePaint = new TextPaint();
        titlePaint.setTextSize(target.titleTextSizePx(context));
        titlePaint.setTypeface(Typeface.create("sans-serif-medium", Typeface.NORMAL));
        titlePaint.setAntiAlias(true);

        descriptionPaint = new TextPaint();
        descriptionPaint.setTextSize(target.descriptionTextSizePx(context));
        descriptionPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL));
        descriptionPaint.setAntiAlias(true);
        descriptionPaint.setAlpha((int) (0.54f * 255.0f));

        outerCirclePaint = new Paint();
        outerCirclePaint.setAntiAlias(true);
        outerCirclePaint.setAlpha((int) (target.outerCircleAlpha * 255.0f));

        outerCircleShadowPaint = new Paint();
        outerCircleShadowPaint.setAntiAlias(true);
        outerCircleShadowPaint.setAlpha(50);
        outerCircleShadowPaint.setStyle(Paint.Style.STROKE);
        outerCircleShadowPaint.setStrokeWidth(SHADOW_JITTER_DIM);
        outerCircleShadowPaint.setColor(Color.BLACK);

        targetCirclePaint = new Paint();
        targetCirclePaint.setAntiAlias(true);

        targetCirclePulsePaint = new Paint();
        targetCirclePulsePaint.setAntiAlias(true);

        applyTargetOptions(context);

        globalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (isDismissing) {
                    return;
                }
                updateTextLayouts();
                target.onReady(new Runnable() {
                    @Override
                    public void run() {
                        final int[] offset = new int[2];

                        targetBounds.set(target.bounds());

                        getLocationOnScreen(offset);
                        targetBounds.offset(-offset[0], -offset[1]);

                        if (boundingParent != null) {
                            final WindowManager windowManager
                                    = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
                            final DisplayMetrics displayMetrics = new DisplayMetrics();
                            windowManager.getDefaultDisplay().getMetrics(displayMetrics);

                            final Rect rect = new Rect();
                            boundingParent.getWindowVisibleDisplayFrame(rect);

                            // We bound the boundaries to be within the screen's coordinates to
                            // handle the case where the layout bounds do not match
                            // (like when FLAG_LAYOUT_NO_LIMITS is specified)
                            topBoundary = Math.max(0, rect.top);
                            bottomBoundary = Math.min(rect.bottom, displayMetrics.heightPixels);
                        }

                        drawTintedTarget();
                        requestFocus();
                        calculateDimensions();

                        startExpandAnimation();
                    }
                });
            }
        };

        getViewTreeObserver().addOnGlobalLayoutListener(globalLayoutListener);

        setFocusableInTouchMode(true);
        setClickable(true);
        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (listener == null || outerCircleCenter == null || !isInteractable) return;

                final boolean clickedInTarget =
                        distance(targetBounds.centerX(), targetBounds.centerY(), (int) lastTouchX, (int) lastTouchY) <= targetCircleRadius;
                final double distanceToOuterCircleCenter = distance(outerCircleCenter[0], outerCircleCenter[1],
                        (int) lastTouchX, (int) lastTouchY);
                final boolean clickedInsideOfOuterCircle = distanceToOuterCircleCenter <= outerCircleRadius;

                if (clickedInTarget) {
                    isInteractable = false;
                    listener.onTargetClick(TapTargetView.this);
                } else if (clickedInsideOfOuterCircle) {
                    listener.onOuterCircleClick(TapTargetView.this);
                } else if (cancelable) {
                    isInteractable = false;
                    listener.onTargetCancel(TapTargetView.this);
                }
            }
        });

        setOnLongClickListener(new OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if (listener == null) return false;

                if (targetBounds.contains((int) lastTouchX, (int) lastTouchY)) {
                    listener.onTargetLongClick(TapTargetView.this);
                    return true;
                }

                return false;
            }
        });
    }

    private void startExpandAnimation() {
        if (!visible) {
            isInteractable = false;
            expandAnimation.start();
            visible = true;
        }
    }

    protected void applyTargetOptions(Context context) {
        shouldTintTarget = target.tintTarget;
        shouldDrawShadow = target.drawShadow;
        cancelable = target.cancelable;

        // We can't clip out portions of a view outline, so if the user specified a transparent
        // target, we need to fallback to drawing a jittered shadow approximation
        if (shouldDrawShadow && Build.VERSION.SDK_INT >= 21 && !target.transparentTarget) {
            outlineProvider = new ViewOutlineProvider() {
                @Override
                public void getOutline(View view, Outline outline) {
                    if (outerCircleCenter == null) return;
                    outline.setOval(
                            (int) (outerCircleCenter[0] - outerCircleRadius), (int) (outerCircleCenter[1] - outerCircleRadius),
                            (int) (outerCircleCenter[0] + outerCircleRadius), (int) (outerCircleCenter[1] + outerCircleRadius));
                    outline.setAlpha(outerCircleAlpha / 255.0f);
                    if (Build.VERSION.SDK_INT >= 22) {
                        outline.offset(0, SHADOW_DIM);
                    }
                }
            };

            setOutlineProvider(outlineProvider);
            setElevation(SHADOW_DIM);
        }

        if (shouldDrawShadow && outlineProvider == null && Build.VERSION.SDK_INT < 18) {
            setLayerType(LAYER_TYPE_SOFTWARE, null);
        } else {
            setLayerType(LAYER_TYPE_HARDWARE, null);
        }

        final android.content.res.Resources.Theme theme = context.getTheme();
        isDark = UiUtil.themeIntAttr(context, "isLightTheme") == 0;

        final Integer outerCircleColor = target.outerCircleColorInt(context);
        if (outerCircleColor != null) {
            outerCirclePaint.setColor(outerCircleColor);
        } else if (theme != null) {
            outerCirclePaint.setColor(UiUtil.themeIntAttr(context, "colorPrimary"));
        } else {
            outerCirclePaint.setColor(Color.WHITE);
        }

        final Integer targetCircleColor = target.targetCircleColorInt(context);
        if (targetCircleColor != null) {
            targetCirclePaint.setColor(targetCircleColor);
        } else {
            targetCirclePaint.setColor(isDark ? Color.BLACK : Color.WHITE);
        }

        if (target.transparentTarget) {
            targetCirclePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        }

        targetCirclePulsePaint.setColor(targetCirclePaint.getColor());

        final Integer targetDimColor = target.dimColorInt(context);
        if (targetDimColor != null) {
            dimColor = UiUtil.setAlpha(targetDimColor, 0.3f);
        } else {
            dimColor = -1;
        }

        final Integer titleTextColor = target.titleTextColorInt(context);
        if (titleTextColor != null) {
            titlePaint.setColor(titleTextColor);
        } else {
            titlePaint.setColor(isDark ? Color.BLACK : Color.WHITE);
        }

        final Integer descriptionTextColor = target.descriptionTextColorInt(context);
        if (descriptionTextColor != null) {
            descriptionPaint.setColor(descriptionTextColor);
        } else {
            descriptionPaint.setColor(titlePaint.getColor());
        }

        if (target.titleTypeface != null) {
            titlePaint.setTypeface(target.titleTypeface);
        }

        if (target.descriptionTypeface != null) {
            descriptionPaint.setTypeface(target.descriptionTypeface);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        onDismiss(false);
    }
 void onDismiss(boolean userInitiated) {
        if (isDismissed) return;

        isDismissing = false;
        isDismissed = true;

        for (final ValueAnimator animator : animators) {
            animator.cancel();
            animator.removeAllUpdateListeners();
        }
        ViewUtil.removeOnGlobalLayoutListener(getViewTreeObserver(), globalLayoutListener);
        visible = false;

        if (listener != null) {
            listener.onTargetDismissed(this, userInitiated);
        }
    }

    @Override
    protected void onDraw(Canvas c) {
        if (isDismissed || outerCircleCenter == null) return;

        if (topBoundary > 0 && bottomBoundary > 0) {
            c.clipRect(0, topBoundary, getWidth(), bottomBoundary);
        }

        if (dimColor != -1) {
            c.drawColor(dimColor);
        }

        int saveCount;
        outerCirclePaint.setAlpha(outerCircleAlpha);
        if (shouldDrawShadow && outlineProvider == null) {
            saveCount = c.save();
            {
                c.clipPath(outerCirclePath, Region.Op.DIFFERENCE);
                drawJitteredShadow(c);
            }
            c.restoreToCount(saveCount);
        }
        c.drawCircle(outerCircleCenter[0], outerCircleCenter[1], outerCircleRadius, outerCirclePaint);

        targetCirclePaint.setAlpha(targetCircleAlpha);
        if (targetCirclePulseAlpha > 0) {
            targetCirclePulsePaint.setAlpha(targetCirclePulseAlpha);
            c.drawCircle(targetBounds.centerX(), targetBounds.centerY(),
                    targetCirclePulseRadius, targetCirclePulsePaint);
        }
        c.drawCircle(targetBounds.centerX(), targetBounds.centerY(),
                targetCircleRadius, targetCirclePaint);

        saveCount = c.save();
        {
            c.translate(textBounds.left, textBounds.top);
            titlePaint.setAlpha(textAlpha);
            if (titleLayout != null) {
                titleLayout.draw(c);
            }

            if (descriptionLayout != null && titleLayout != null) {
                c.translate(0, titleLayout.getHeight() + TEXT_SPACING);
                descriptionPaint.setAlpha((int) (target.descriptionTextAlpha * textAlpha));
                descriptionLayout.draw(c);
            }
        }
        c.restoreToCount(saveCount);

        saveCount = c.save();
        {
            if (tintedTarget != null) {
                c.translate(targetBounds.centerX() - tintedTarget.getWidth() / 2,
                        targetBounds.centerY() - tintedTarget.getHeight() / 2);
                c.drawBitmap(tintedTarget, 0, 0, targetCirclePaint);
            } else if (target.icon != null) {
                c.translate(targetBounds.centerX() - target.icon.getBounds().width() / 2,
                        targetBounds.centerY() - target.icon.getBounds().height() / 2);
                target.icon.setAlpha(targetCirclePaint.getAlpha());
                target.icon.draw(c);
            }
        }
        c.restoreToCount(saveCount);

        if (debug) {
            drawDebugInformation(c);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        lastTouchX = e.getX();
        lastTouchY = e.getY();
        return super.onTouchEvent(e);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (isVisible() && cancelable && keyCode == KeyEvent.KEYCODE_BACK) {
            event.startTracking();
            return true;
        }

        return false;
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (isVisible() && isInteractable && cancelable
                && keyCode == KeyEvent.KEYCODE_BACK && event.isTracking() && !event.isCanceled()) {
            isInteractable = false;

            if (listener != null) {
                listener.onTargetCancel(this);
            } else {
                new Listener().onTargetCancel(this);
            }

            return true;
        }

        return false;
    }

    /**
     * Dismiss this view
     * @param tappedTarget If the user tapped the target or not
     *                     (results in different dismiss animations)
     */
    public void dismiss(boolean tappedTarget) {
        isDismissing = true;
        pulseAnimation.cancel();
        expandAnimation.cancel();
        if (tappedTarget) {
            dismissConfirmAnimation.start();
        } else {
            dismissAnimation.start();
        }
    }

    /** Specify whether to draw a wireframe around the view, useful for debugging **/
    public void setDrawDebug(boolean status) {
        if (debug != status) {
            debug = status;
            postInvalidate();
        }
    }

    /** Returns whether this view is visible or not **/
    public boolean isVisible() {
        return !isDismissed && visible;
    }

    void drawJitteredShadow(Canvas c) {
        final float baseAlpha = 0.20f * outerCircleAlpha;
        outerCircleShadowPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        outerCircleShadowPaint.setAlpha((int) baseAlpha);
        c.drawCircle(outerCircleCenter[0], outerCircleCenter[1] + SHADOW_DIM, outerCircleRadius, outerCircleShadowPaint);
        outerCircleShadowPaint.setStyle(Paint.Style.STROKE);
        final int numJitters = 7;
        for (int i = numJitters - 1; i > 0; --i) {
            outerCircleShadowPaint.setAlpha((int) ((i / (float) numJitters) * baseAlpha));
            c.drawCircle(outerCircleCenter[0], outerCircleCenter[1] + SHADOW_DIM ,
                    outerCircleRadius + (numJitters - i) * SHADOW_JITTER_DIM , outerCircleShadowPaint);
        }
    }

    void drawDebugInformation(Canvas c) {
        if (debugPaint == null) {
            debugPaint = new Paint();
            debugPaint.setARGB(255, 255, 0, 0);
            debugPaint.setStyle(Paint.Style.STROKE);
            debugPaint.setStrokeWidth(UiUtil.dp(getContext(), 1));
        }

        if (debugTextPaint == null) {
            debugTextPaint = new TextPaint();
            debugTextPaint.setColor(0xFFFF0000);
            debugTextPaint.setTextSize(UiUtil.sp(getContext(), 16));
        }

        // Draw wireframe
        debugPaint.setStyle(Paint.Style.STROKE);
        c.drawRect(textBounds, debugPaint);
        c.drawRect(targetBounds, debugPaint);
        c.drawCircle(outerCircleCenter[0], outerCircleCenter[1], 10, debugPaint);
        c.drawCircle(outerCircleCenter[0], outerCircleCenter[1], calculatedOuterCircleRadius - CIRCLE_PADDING, debugPaint);
        c.drawCircle(targetBounds.centerX(), targetBounds.centerY(), TARGET_RADIUS + TARGET_PADDING, debugPaint);

        // Draw positions and dimensions
        debugPaint.setStyle(Paint.Style.FILL);
        final String debugText =
                "Text bounds: " + textBounds.toShortString() + "n" +
                        "Target bounds: " + targetBounds.toShortString() + "n" +
                        "Center: " + outerCircleCenter[0] + " " + outerCircleCenter[1] + "n" +
                        "View size: " + getWidth() + " " + getHeight() + "n" +
                        "Target bounds: " + targetBounds.toShortString();

        if (debugStringBuilder == null) {
            debugStringBuilder = new SpannableStringBuilder(debugText);
        } else {
            debugStringBuilder.clear();
            debugStringBuilder.append(debugText);
        }

        if (debugLayout == null) {
            debugLayout = new DynamicLayout(debugText, debugTextPaint, getWidth(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
        }

        final int saveCount = c.save();
        {
            debugPaint.setARGB(220, 0, 0, 0);
            c.translate(0.0f, topBoundary);
            c.drawRect(0.0f, 0.0f, debugLayout.getWidth(), debugLayout.getHeight(), debugPaint);
            debugPaint.setARGB(255, 255, 0, 0);
            debugLayout.draw(c);
        }
        c.restoreToCount(saveCount);
    }

    void drawTintedTarget() {
        final android.graphics.drawable.Drawable icon = target.icon;
        if (!shouldTintTarget || icon == null) {
            tintedTarget = null;
            return;
        }

        if (tintedTarget != null) return;

        tintedTarget = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(),
                Bitmap.Config.ARGB_8888);
        final Canvas canvas = new Canvas(tintedTarget);
        icon.setColorFilter(new PorterDuffColorFilter(
                outerCirclePaint.getColor(), PorterDuff.Mode.SRC_ATOP));
        icon.draw(canvas);
        icon.setColorFilter(null);
    }

    void updateTextLayouts() {
        final int textWidth = Math.min(getWidth(), TEXT_MAX_WIDTH) - TEXT_PADDING * 2;
        if (textWidth <= 0) {
            return;
        }

        titleLayout = new StaticLayout(title, titlePaint, textWidth,
                Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);

        if (description != null) {
            descriptionLayout = new StaticLayout(description, descriptionPaint, textWidth,
                    Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
        } else {
            descriptionLayout = null;
        }
    }

    float halfwayLerp(float lerp) {
        if (lerp < 0.5f) {
            return lerp / 0.5f;
        }

        return (1.0f - lerp) / 0.5f;
    }

    float delayedLerp(float lerp, float threshold) {
        if (lerp < threshold) {
            return 0.0f;
        }

        return (lerp - threshold) / (1.0f - threshold);
    }

    void calculateDimensions() {
        textBounds = getTextBounds();
        outerCircleCenter = getOuterCircleCenterPoint();
        calculatedOuterCircleRadius = getOuterCircleRadius(outerCircleCenter[0], outerCircleCenter[1], textBounds, targetBounds);
    }

    void calculateDrawingBounds() {
        if (outerCircleCenter == null) {
            // Called dismiss before we got a chance to display the tap target
            // So we have no center -> cant determine the drawing bounds
            return;
        }
        drawingBounds.left = (int) Math.max(0, outerCircleCenter[0] - outerCircleRadius);
        drawingBounds.top = (int) Math.min(0, outerCircleCenter[1] - outerCircleRadius);
        drawingBounds.right = (int) Math.min(getWidth(),
                outerCircleCenter[0] + outerCircleRadius + CIRCLE_PADDING);
        drawingBounds.bottom = (int) Math.min(getHeight(),
                outerCircleCenter[1] + outerCircleRadius + CIRCLE_PADDING);
    }

    int getOuterCircleRadius(int centerX, int centerY, Rect textBounds, Rect targetBounds) {
        final int targetCenterX = targetBounds.centerX();
        final int targetCenterY = targetBounds.centerY();
        final int expandedRadius = (int) (1.1f * TARGET_RADIUS);
        final Rect expandedBounds = new Rect(targetCenterX, targetCenterY, targetCenterX, targetCenterY);
        expandedBounds.inset(-expandedRadius, -expandedRadius);

        final int textRadius = maxDistanceToPoints(centerX, centerY, textBounds);
        final int targetRadius = maxDistanceToPoints(centerX, centerY, expandedBounds);
        return Math.max(textRadius, targetRadius) + CIRCLE_PADDING;
    }

    Rect getTextBounds() {
        final int totalTextHeight = getTotalTextHeight();
        final int totalTextWidth = getTotalTextWidth();

        final int possibleTop = targetBounds.centerY() - TARGET_RADIUS - TARGET_PADDING - totalTextHeight;
        final int top;
        if (possibleTop > topBoundary) {
            top = possibleTop;
        } else {
            top = targetBounds.centerY() + TARGET_RADIUS + TARGET_PADDING;
        }

        final int relativeCenterDistance = (getWidth() / 2) - targetBounds.centerX();
        final int bias = relativeCenterDistance < 0 ? -TEXT_POSITIONING_BIAS : TEXT_POSITIONING_BIAS;
        final int left = Math.max(TEXT_PADDING, targetBounds.centerX() - bias - totalTextWidth);
        final int right = Math.min(getWidth() - TEXT_PADDING, left + totalTextWidth);
        return new Rect(left, top, right, top + totalTextHeight);
    }

    int[] getOuterCircleCenterPoint() {
        if (inGutter(targetBounds.centerY())) {
            return new int[]{targetBounds.centerX(), targetBounds.centerY()};
        }

        final int targetRadius = Math.max(targetBounds.width(), targetBounds.height()) / 2 + TARGET_PADDING;
        final int totalTextHeight = getTotalTextHeight();

        final boolean onTop = targetBounds.centerY() - TARGET_RADIUS - TARGET_PADDING - totalTextHeight > 0;

        final int left = Math.min(textBounds.left, targetBounds.left - targetRadius);
        final int right = Math.max(textBounds.right, targetBounds.right + targetRadius);
        final int titleHeight = titleLayout == null ? 0 : titleLayout.getHeight();
        final int centerY = onTop ?
                targetBounds.centerY() - TARGET_RADIUS - TARGET_PADDING - totalTextHeight + titleHeight
                :
                targetBounds.centerY() + TARGET_RADIUS + TARGET_PADDING + titleHeight;

        return new int[] { (left + right) / 2, centerY };
    }

    int getTotalTextHeight() {
        if (titleLayout == null) {
            return 0;
        }

        if (descriptionLayout == null) {
            return titleLayout.getHeight() + TEXT_SPACING;
        }

        return titleLayout.getHeight() + descriptionLayout.getHeight() + TEXT_SPACING;
    }

    int getTotalTextWidth() {
        if (titleLayout == null) {
            return 0;
        }

        if (descriptionLayout == null) {
            return titleLayout.getWidth();
        }

        return Math.max(titleLayout.getWidth(), descriptionLayout.getWidth());
    }
    boolean inGutter(int y) {
        if (bottomBoundary > 0) {
            return y < GUTTER_DIM || y > bottomBoundary - GUTTER_DIM;
        } else {
            return y < GUTTER_DIM || y > getHeight() - GUTTER_DIM;
        }
    }
    int maxDistanceToPoints(int x1, int y1, Rect bounds) {
        final double tl = distance(x1, y1, bounds.left, bounds.top);
        final double tr = distance(x1, y1, bounds.right, bounds.top);
        final double bl = distance(x1, y1, bounds.left, bounds.bottom);
        final double br = distance(x1, y1, bounds.right, bounds.bottom);
        return (int) Math.max(tl, Math.max(tr, Math.max(bl, br)));
    }
    double distance(int x1, int y1, int x2, int y2) {
        return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
    }
    void invalidateViewAndOutline(Rect bounds) {
        invalidate(bounds);
        if (outlineProvider != null && Build.VERSION.SDK_INT >= 21) {
            invalidateOutline();
        }
    }
}
static class ViewUtil {

    ViewUtil() {}

    private static boolean isLaidOut(View view) {
        return true;
    }
    static void onLaidOut(final View view, final Runnable runnable) {
        if (isLaidOut(view)) {
            runnable.run();
            return;
        }
        final ViewTreeObserver observer = view.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                final ViewTreeObserver trueObserver;
                if (observer.isAlive()) {
                    trueObserver = observer;
                } else {
                    trueObserver = view.getViewTreeObserver();
                }
                removeOnGlobalLayoutListener(trueObserver, this);
                runnable.run();
            }
        });
    }
    @SuppressWarnings("deprecation")
    static void removeOnGlobalLayoutListener(ViewTreeObserver observer,
                                             ViewTreeObserver.OnGlobalLayoutListener listener) {
        if (Build.VERSION.SDK_INT >= 16) {
            observer.removeOnGlobalLayoutListener(listener);
        } else {
            observer.removeGlobalOnLayoutListener(listener);
        }
    }
    static void removeView(ViewManager parent, View child) {
        if (parent == null || child == null) {
            return;
        }
        try {
            parent.removeView(child);
        } catch (Exception ignored) {
        }
    }
}
		static class ViewTapTarget extends TapTarget {
    final View view;

    ViewTapTarget(View view, CharSequence title,  CharSequence description) {
        super(title, description);
        if (view == null) {
            throw new IllegalArgumentException("Given null view to target");
        }
        this.view = view;
    }

    @Override
    public void onReady(final Runnable runnable) {
        ViewUtil.onLaidOut(view, new Runnable() {
            @Override
            public void run() {
                // Cache bounds
                final int[] location = new int[2];
                view.getLocationOnScreen(location);
                bounds = new Rect(location[0], location[1],
                        location[0] + view.getWidth(), location[1] + view.getHeight());

                if (icon == null && view.getWidth() > 0 && view.getHeight() > 0) {
                    final Bitmap viewBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
                    final Canvas canvas = new Canvas(viewBitmap);
                    view.draw(canvas);
                    icon = new android.graphics.drawable.BitmapDrawable(view.getContext().getResources(), viewBitmap);
                    icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
                }

                runnable.run();
            }
        });
    }
}
		static class TapTargetSequence {
    private final Activity activity;
    private final Dialog dialog;
    private final Queue<TapTarget> targets;
    private boolean active;
    private TapTargetView currentView;
    Listener listener;
    boolean considerOuterCircleCanceled;
    boolean continueOnCancel;
    public interface Listener {
        void onSequenceFinish();
        void onSequenceStep(TapTarget lastTarget, boolean targetClicked);
        void onSequenceCanceled(TapTarget lastTarget);
    }
    public TapTargetSequence(Activity activity) {
        if (activity == null) throw new IllegalArgumentException("Activity is null");
        this.activity = activity;
        this.dialog = null;
        this.targets = new LinkedList<>();
    }
    public TapTargetSequence(Dialog dialog) {
        if (dialog == null) throw new IllegalArgumentException("Given null Dialog");
        this.dialog = dialog;
        this.activity = null;
        this.targets = new LinkedList<>();
    }
    public TapTargetSequence targets(List<TapTarget> targets) {
        this.targets.addAll(targets);
        return this;
    }
    public TapTargetSequence targets(TapTarget... targets) {
        Collections.addAll(this.targets, targets);
        return this;
    }
    public TapTargetSequence target(TapTarget target) {
        this.targets.add(target);
        return this;
    }
    public TapTargetSequence continueOnCancel(boolean status) {
        this.continueOnCancel = status;
        return this;
    }
    public TapTargetSequence considerOuterCircleCanceled(boolean status) {
        this.considerOuterCircleCanceled = status;
        return this;
    }
    public TapTargetSequence listener(Listener listener) {
        this.listener = listener;
        return this;
    }
    public void start() {
        if (targets.isEmpty() || active) {
            return;
        }
        active = true;
        showNext();
    }
    public void startWith(int targetId) {
        if (active) {
            return;
        }
        while (targets.peek() != null && targets.peek().id() != targetId) {
            targets.poll();
        }
        TapTarget peekedTarget = targets.peek();
        if (peekedTarget == null || peekedTarget.id() != targetId) {
            throw new IllegalStateException("Given target " + targetId + " not in sequence");
        }
        start();
    }
    public void startAt(int index) {
        if (active) {
            return;
        }
        if (index < 0 || index >= targets.size()) {
            throw new IllegalArgumentException("Given invalid index " + index);
        }
        final int expectedSize = targets.size() - index;
        while (targets.peek() != null && targets.size() != expectedSize) {
            targets.poll();
        }
        if (targets.size() != expectedSize) {
            throw new IllegalStateException("Given index " + index + " not in sequence");
        }
        start();
    }
    public boolean cancel() {
        if (targets.isEmpty() || !active) {
            return false;
        }
        if (currentView == null || !currentView.cancelable) {
            return false;
        }
        currentView.dismiss(false);
        active = false;
        targets.clear();
        if (listener != null) {
            listener.onSequenceCanceled(currentView.target);
        }
        return true;
    }
    void showNext() {
        try {
            TapTarget tapTarget = targets.remove();
            if (activity != null) {
                currentView = TapTargetView.showFor(activity, tapTarget, tapTargetListener);
            } else {
                currentView = TapTargetView.showFor(dialog, tapTarget, tapTargetListener);
            }
        } catch (NoSuchElementException e) {
            // No more targets
            if (listener != null) {
                listener.onSequenceFinish();
            }
        }
    }
    private final TapTargetView.Listener tapTargetListener = new TapTargetView.Listener() {
        @Override
        public void onTargetClick(TapTargetView view) {
            super.onTargetClick(view);
            if (listener != null) {
                listener.onSequenceStep(view.target, true);
            }
            showNext();
        }
        @Override
        public void onOuterCircleClick(TapTargetView view) {
            if (considerOuterCircleCanceled) {
                onTargetCancel(view);
            }
        }
        @Override
        public void onTargetCancel(TapTargetView view) {
            super.onTargetCancel(view);
            if (continueOnCancel) {
                if (listener != null) {
                    listener.onSequenceStep(view.target, false);
                }
                showNext();
            } else {
                if (listener != null) {
                    listener.onSequenceCanceled(view.target);
                }
            }
        }
    };

Link al vídeo:

Comentarios

Entradas populares de este blog

Cambio de permisos de usuario

Seekbar personalizado

Multiples imagenes en Firebase Storage