/** * Copyright 2016 Keepsafe Software, Inc. * <p> * 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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 com.getkeepsafe.taptargetview; import android.content.Context; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import androidx.annotation.ColorInt; import androidx.annotation.ColorRes; import androidx.annotation.DimenRes; import androidx.annotation.IdRes; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.appcompat.widget.Toolbar; import android.view.View; /** * Describes the properties and options for a {@link TapTargetView}. * <p> * Each tap target describes a target via a pair of bounds and icon. The bounds dictate the * location and touch area of the target, where the icon is what will be drawn within the center of * the bounds. * <p> * This class can be extended to support various target types. * * @see ViewTapTarget ViewTapTarget for targeting standard Android views */ public class TapTarget { final CharSequence title; @Nullable final CharSequence description; float outerCircleAlpha = 0.96f; int targetRadius = 44; Rect bounds; Drawable icon; Typeface titleTypeface; Typeface descriptionTypeface; @ColorRes private int outerCircleColorRes = -1; @ColorRes private int targetCircleColorRes = -1; @ColorRes private int dimColorRes = -1; @ColorRes private int titleTextColorRes = -1; @ColorRes private int descriptionTextColorRes = -1; private Integer outerCircleColor = null; private Integer targetCircleColor = null; private Integer dimColor = null; private Integer titleTextColor = null; private Integer descriptionTextColor = null; @DimenRes private int titleTextDimen = -1; @DimenRes 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; /** * Return a tap target for the overflow button from the given toolbar * <p> * <b>Note:</b> This is currently experimental, use at your own risk */ public static TapTarget forToolbarOverflow(Toolbar toolbar, CharSequence title) { return forToolbarOverflow(toolbar, title, null); } /** Return a tap target for the overflow button from the given toolbar * <p> * <b>Note:</b> This is currently experimental, use at your own risk */ public static TapTarget forToolbarOverflow(Toolbar toolbar, CharSequence title, @Nullable CharSequence description) { return new ToolbarTapTarget(toolbar, false, title, description); } /** Return a tap target for the overflow button from the given toolbar * <p> * <b>Note:</b> This is currently experimental, use at your own risk */ public static TapTarget forToolbarOverflow(android.widget.Toolbar toolbar, CharSequence title) { return forToolbarOverflow(toolbar, title, null); } /** Return a tap target for the overflow button from the given toolbar * <p> * <b>Note:</b> This is currently experimental, use at your own risk */ public static TapTarget forToolbarOverflow(android.widget.Toolbar toolbar, CharSequence title, @Nullable CharSequence description) { return new ToolbarTapTarget(toolbar, false, title, description); } /** Return a tap target for the navigation button (back, up, etc) from the given toolbar **/ public static TapTarget forToolbarNavigationIcon(Toolbar toolbar, CharSequence title) { return forToolbarNavigationIcon(toolbar, title, null); } /** Return a tap target for the navigation button (back, up, etc) from the given toolbar **/ public static TapTarget forToolbarNavigationIcon(Toolbar toolbar, CharSequence title, @Nullable CharSequence description) { return new ToolbarTapTarget(toolbar, true, title, description); } /** Return a tap target for the navigation button (back, up, etc) from the given toolbar **/ public static TapTarget forToolbarNavigationIcon(android.widget.Toolbar toolbar, CharSequence title) { return forToolbarNavigationIcon(toolbar, title, null); } /** Return a tap target for the navigation button (back, up, etc) from the given toolbar **/ public static TapTarget forToolbarNavigationIcon(android.widget.Toolbar toolbar, CharSequence title, @Nullable CharSequence description) { return new ToolbarTapTarget(toolbar, true, title, description); } /** Return a tap target for the menu item from the given toolbar **/ public static TapTarget forToolbarMenuItem(Toolbar toolbar, @IdRes int menuItemId, CharSequence title) { return forToolbarMenuItem(toolbar, menuItemId, title, null); } /** Return a tap target for the menu item from the given toolbar **/ public static TapTarget forToolbarMenuItem(Toolbar toolbar, @IdRes int menuItemId, CharSequence title, @Nullable CharSequence description) { return new ToolbarTapTarget(toolbar, menuItemId, title, description); } /** Return a tap target for the menu item from the given toolbar **/ public static TapTarget forToolbarMenuItem(android.widget.Toolbar toolbar, @IdRes int menuItemId, CharSequence title) { return forToolbarMenuItem(toolbar, menuItemId, title, null); } /** Return a tap target for the menu item from the given toolbar **/ public static TapTarget forToolbarMenuItem(android.widget.Toolbar toolbar, @IdRes int menuItemId, CharSequence title, @Nullable CharSequence description) { return new ToolbarTapTarget(toolbar, menuItemId, title, description); } /** Return a tap target for the specified view **/ public static TapTarget forView(View view, CharSequence title) { return forView(view, title, null); } /** Return a tap target for the specified view **/ public static TapTarget forView(View view, CharSequence title, @Nullable CharSequence description) { return new ViewTapTarget(view, title, description); } /** Return a tap target for the specified bounds **/ public static TapTarget forBounds(Rect bounds, CharSequence title) { return forBounds(bounds, title, null); } /** Return a tap target for the specified bounds **/ public static TapTarget forBounds(Rect bounds, CharSequence title, @Nullable CharSequence description) { return new TapTarget(bounds, title, description); } protected TapTarget(Rect bounds, CharSequence title, @Nullable CharSequence description) { this(title, description); if (bounds == null) { throw new IllegalArgumentException("Cannot pass null bounds or title"); } this.bounds = bounds; } protected TapTarget(CharSequence title, @Nullable CharSequence description) { if (title == null) { throw new IllegalArgumentException("Cannot pass null title"); } this.title = title; this.description = description; } /** Specify whether the target should be transparent **/ public TapTarget transparentTarget(boolean transparent) { this.transparentTarget = transparent; return this; } /** Specify the color resource for the outer circle **/ public TapTarget outerCircleColor(@ColorRes int color) { this.outerCircleColorRes = color; return this; } /** Specify the color value for the outer circle **/ // TODO(Hilal): In v2, this API should be cleaned up / torched public TapTarget outerCircleColorInt(@ColorInt int color) { this.outerCircleColor = color; return this; } /** Specify the alpha value [0.0, 1.0] of the outer circle **/ 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; } /** Specify the color resource for the target circle **/ public TapTarget targetCircleColor(@ColorRes int color) { this.targetCircleColorRes = color; return this; } /** Specify the color value for the target circle **/ // TODO(Hilal): In v2, this API should be cleaned up / torched public TapTarget targetCircleColorInt(@ColorInt int color) { this.targetCircleColor = color; return this; } /** Specify the color resource for all text **/ public TapTarget textColor(@ColorRes int color) { this.titleTextColorRes = color; this.descriptionTextColorRes = color; return this; } /** Specify the color value for all text **/ // TODO(Hilal): In v2, this API should be cleaned up / torched public TapTarget textColorInt(@ColorInt int color) { this.titleTextColor = color; this.descriptionTextColor = color; return this; } /** Specify the color resource for the title text **/ public TapTarget titleTextColor(@ColorRes int color) { this.titleTextColorRes = color; return this; } /** Specify the color value for the title text **/ // TODO(Hilal): In v2, this API should be cleaned up / torched public TapTarget titleTextColorInt(@ColorInt int color) { this.titleTextColor = color; return this; } /** Specify the color resource for the description text **/ public TapTarget descriptionTextColor(@ColorRes int color) { this.descriptionTextColorRes = color; return this; } /** Specify the color value for the description text **/ // TODO(Hilal): In v2, this API should be cleaned up / torched public TapTarget descriptionTextColorInt(@ColorInt int color) { this.descriptionTextColor = color; return this; } /** Specify the typeface for all text **/ public TapTarget textTypeface(Typeface typeface) { if (typeface == null) throw new IllegalArgumentException("Cannot use a null typeface"); titleTypeface = typeface; descriptionTypeface = typeface; return this; } /** Specify the typeface for title text **/ public TapTarget titleTypeface(Typeface titleTypeface) { if (titleTypeface == null) throw new IllegalArgumentException("Cannot use a null typeface"); this.titleTypeface = titleTypeface; return this; } /** Specify the typeface for description text **/ public TapTarget descriptionTypeface(Typeface descriptionTypeface) { if (descriptionTypeface == null) throw new IllegalArgumentException("Cannot use a null typeface"); this.descriptionTypeface = descriptionTypeface; return this; } /** Specify the text size for the title in SP **/ public TapTarget titleTextSize(int sp) { if (sp < 0) throw new IllegalArgumentException("Given negative text size"); this.titleTextSize = sp; return this; } /** Specify the text size for the description in SP **/ public TapTarget descriptionTextSize(int sp) { if (sp < 0) throw new IllegalArgumentException("Given negative text size"); this.descriptionTextSize = sp; return this; } /** * Specify the text size for the title via a dimen resource * <p> * Note: If set, this value will take precedence over the specified sp size */ public TapTarget titleTextDimen(@DimenRes int dimen) { this.titleTextDimen = dimen; return this; } /** Specify the alpha value [0.0, 1.0] of the description text **/ public TapTarget descriptionTextAlpha(float descriptionTextAlpha) { if (descriptionTextAlpha < 0 || descriptionTextAlpha > 1f) { throw new IllegalArgumentException("Given an invalid alpha value: " + descriptionTextAlpha); } this.descriptionTextAlpha = descriptionTextAlpha; return this; } /** * Specify the text size for the description via a dimen resource * <p> * Note: If set, this value will take precedence over the specified sp size */ public TapTarget descriptionTextDimen(@DimenRes int dimen) { this.descriptionTextDimen = dimen; return this; } /** * Specify the color resource to use as a dim effect * <p> * <b>Note:</b> The given color will have its opacity modified to 30% automatically */ public TapTarget dimColor(@ColorRes int color) { this.dimColorRes = color; return this; } /** * Specify the color value to use as a dim effect * <p> * <b>Note:</b> The given color will have its opacity modified to 30% automatically */ // TODO(Hilal): In v2, this API should be cleaned up / torched public TapTarget dimColorInt(@ColorInt int color) { this.dimColor = color; return this; } /** Specify whether or not to draw a drop shadow around the outer circle **/ public TapTarget drawShadow(boolean draw) { this.drawShadow = draw; return this; } /** Specify whether or not the target should be cancelable **/ public TapTarget cancelable(boolean status) { this.cancelable = status; return this; } /** Specify whether to tint the target's icon with the outer circle's color **/ public TapTarget tintTarget(boolean tint) { this.tintTarget = tint; return this; } /** Specify the icon that will be drawn in the center of the target bounds **/ public TapTarget icon(Drawable icon) { return icon(icon, false); } /** * Specify the icon that will be drawn in the center of the target bounds * @param hasSetBounds Whether the drawable already has its bounds correctly set. If the * drawable does not have its bounds set, then the following bounds will * be applied: <br/> * <code>(0, 0, intrinsic-width, intrinsic-height)</code> */ public TapTarget icon(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; } /** Specify a unique identifier for this target. **/ public TapTarget id(int id) { this.id = id; return this; } /** Specify the target radius in dp. **/ public TapTarget targetRadius(int targetRadius) { this.targetRadius = targetRadius; return this; } /** Return the id associated with this tap target **/ public int id() { return id; } /** * In case your target needs time to be ready (laid out in your view, not created, etc), the * runnable passed here will be invoked when the target is ready. */ public void onReady(Runnable runnable) { runnable.run(); } /** * Returns the target bounds. Throws an exception if they are not set * (target may not be ready) * <p> * This will only be called internally when {@link #onReady(Runnable)} invokes its runnable */ public Rect bounds() { if (bounds == null) { throw new IllegalStateException("Requesting bounds that are not set! Make sure your target is ready"); } return bounds; } @Nullable Integer outerCircleColorInt(Context context) { return colorResOrInt(context, outerCircleColor, outerCircleColorRes); } @Nullable Integer targetCircleColorInt(Context context) { return colorResOrInt(context, targetCircleColor, targetCircleColorRes); } @Nullable Integer dimColorInt(Context context) { return colorResOrInt(context, dimColor, dimColorRes); } @Nullable Integer titleTextColorInt(Context context) { return colorResOrInt(context, titleTextColor, titleTextColorRes); } @Nullable 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); } @Nullable private Integer colorResOrInt(Context context, @Nullable Integer value, @ColorRes int resource) { if (resource != -1) { return ContextCompat.getColor(context, resource); } return value; } private int dimenOrSize(Context context, int size, @DimenRes int dimen) { if (dimen != -1) { return context.getResources().getDimensionPixelSize(dimen); } return UiUtil.sp(context, size); } }