package com.github.espiandev.showcaseview;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.text.DynamicLayout;
import android.text.Layout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.Button;
import android.widget.RelativeLayout;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorSet;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.view.ViewHelper;
import java.lang.reflect.Field;
/**
* A view which allows you to showcase areas of your app with an explanation.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class ShowcaseView extends RelativeLayout implements View.OnClickListener, View.OnTouchListener {
public static final int TYPE_NO_LIMIT = 0;
public static final int TYPE_ONE_SHOT = 1;
public static final int INSERT_TO_DECOR = 0;
public static final int INSERT_TO_VIEW = 1;
public static final int ITEM_ACTION_HOME = 0;
public static final int ITEM_TITLE_OR_SPINNER = 1;
public static final int ITEM_ACTION_ITEM = 2;
public static final int ITEM_ACTION_OVERFLOW = 6;
private float showcaseX = -1, showcaseY = -1, showcaseRadius = -1, metricScale = 1.0f, legacyShowcaseX = -1, legacyShowcaseY = -1;
private boolean isRedundant = false, hasCustomClickListener = false;
private ConfigOptions mOptions;
private Paint mPaintTitle, mEraser;
private TextPaint mPaintDetail;
private final int backColor;
private Drawable showcase;
private View mButton, mHandy;
private final Button mBackupButton;
private OnShowcaseEventListener mEventListener;
private Rect voidedArea;
private String mTitleText, mSubText;
private Context mContext;
private int detailTextColor = -1, titleTextColor = -1;
private DynamicLayout mDynamicDetailLayout;
private float[] mBestTextPosition;
private int buttonBack, buttonTextColor;
private String buttonLabel;
public ShowcaseView(Context context) {
this(context, null, R.styleable.CustomTheme_showcaseViewStyle);
this.mContext = context;
}
public ShowcaseView(Context context, AttributeSet attrs) {
this(context, attrs, R.styleable.CustomTheme_showcaseViewStyle);
this.mContext = context;
}
public ShowcaseView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mContext = context;
// Get the attributes for the ShowcaseView
final TypedArray styled = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ShowcaseView, R.attr.showcaseViewStyle,
R.style.ShowcaseView);
backColor = styled.getInt(R.styleable.ShowcaseView_backgroundColor, Color.argb(128, 80, 80, 80));
detailTextColor = styled.getColor(R.styleable.ShowcaseView_detailTextColor, Color.WHITE);
titleTextColor = styled.getColor(R.styleable.ShowcaseView_titleTextColor, Color.parseColor("#49C0EC"));
// Get the attributes for the Button
// final TypedArray buttonStyled = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ShowcaseButton,
// R.attr.showcaseButtonStyle, R.style.ShowcaseButton);
// buttonBack = buttonStyled.getColor(R.styleable.ShowcaseButton_buttonBackgroundColor, Color.parseColor("#33B5E5"));
// buttonLabel = buttonStyled.getString(R.styleable.ShowcaseButton_text);
// buttonTextColor = buttonStyled.getColor(R.styleable.ShowcaseButton_textColor, Color.WHITE);
styled.recycle();
metricScale = getContext().getResources().getDisplayMetrics().density;
// mBackupButton = new Button(mContext, null, R.style.ShowcaseButton);
// mBackupButton.setId(R.id.showcase_button);
// float densityMod = mContext.getResources().getDisplayMetrics().density;
// mBackupButton.setPadding((int) (35 * densityMod), (int) (10 * densityMod), (int) (35 * densityMod), (int) (15 * densityMod));
mBackupButton = (Button) LayoutInflater.from(context).inflate(R.layout.showcase_button, null);
// mBackupButton.setBackgroundColor(buttonBack);
// mBackupButton.setText(!TextUtils.isEmpty(buttonLabel) ? buttonLabel.toUpperCase() : "OK");
// mBackupButton.setTextColor(buttonTextColor);
setConfigOptions(new ConfigOptions());
}
private void init() {
boolean hasShot = getContext().getSharedPreferences("showcase_internal", Context.MODE_PRIVATE).getBoolean(
"hasShot" + getConfigOptions().showcaseId, false);
if (hasShot && mOptions.shotType == TYPE_ONE_SHOT) {
// The showcase has already been shot once, so we don't need to do anything
setVisibility(View.GONE);
isRedundant = true;
return;
}
showcase = getContext().getResources().getDrawable(R.drawable.cling);
mButton = findViewById(R.id.showcase_button);
if (mButton != null && !hasCustomClickListener) {
mButton.setOnClickListener(this);
}
showcaseRadius = metricScale * 94;
PorterDuffXfermode mBlender = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);
setOnTouchListener(this);
mPaintTitle = new Paint();
mPaintTitle.setColor(titleTextColor);
mPaintTitle.setShadowLayer(2.0f, 0f, 2.0f, Color.DKGRAY);
mPaintTitle.setTextSize(24 * metricScale);
mPaintTitle.setAntiAlias(true);
mPaintDetail = new TextPaint();
mPaintDetail.setColor(detailTextColor);
mPaintDetail.setShadowLayer(2.0f, 0f, 2.0f, Color.DKGRAY);
mPaintDetail.setTextSize(16 * metricScale);
mPaintDetail.setAntiAlias(true);
mEraser = new Paint();
mEraser.setColor(0xFFFFFF);
mEraser.setAlpha(0);
mEraser.setXfermode(mBlender);
if (mButton == null && !mOptions.noButton) {
RelativeLayout.LayoutParams lps = (LayoutParams) generateDefaultLayoutParams();
lps.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
lps.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
int margin = ((Number) (metricScale * 12)).intValue();
lps.setMargins(margin, margin, margin, margin);
lps.height = LayoutParams.WRAP_CONTENT;
lps.width = LayoutParams.WRAP_CONTENT;
mBackupButton.setLayoutParams(lps);
mBackupButton.setText("OK");
if (!hasCustomClickListener)
mBackupButton.setOnClickListener(this);
addView(mBackupButton);
}
}
/**
* Set the view to showcase
*
* @param view
* The {@link View} to showcase.
*/
public void setShowcaseView(final View view) {
if (isRedundant || view == null) {
isRedundant = true;
return;
}
isRedundant = false;
view.post(new Runnable() {
@Override
public void run() {
init();
if (mOptions.insert == INSERT_TO_VIEW) {
showcaseX = view.getLeft() + view.getWidth() / 2;
showcaseY = view.getTop() + view.getHeight() / 2;
}
else {
int[] coordinates = new int[2];
view.getLocationInWindow(coordinates);
showcaseX = coordinates[0] + view.getWidth() / 2;
showcaseY = coordinates[1] + view.getHeight() / 2;
}
invalidate();
}
});
}
/**
* Set a specific position to showcase
*
* @param x
* X co-ordinate
* @param y
* Y co-ordinate
*/
public void setShowcasePosition(float x, float y) {
if (isRedundant) {
return;
}
showcaseX = x;
showcaseY = y;
init();
invalidate();
}
public void setShowcaseItem(final int itemType, final int actionItemId, final Activity activity) {
post(new Runnable() {
@Override
public void run() {
View homeButton = activity.findViewById(android.R.id.home);
if (homeButton == null) {
// Thanks to @hameno for this
int homeId = activity.getResources().getIdentifier("abs__home", "id", activity.getPackageName());
if (homeId != 0) {
homeButton = activity.findViewById(homeId);
}
}
if (homeButton == null)
throw new RuntimeException("insertShowcaseViewWithType cannot be used when the theme " + "has no ActionBar");
ViewParent p = homeButton.getParent().getParent(); //ActionBarView
Class abv = p.getClass(); //ActionBarView class
Class absAbv = abv.getSuperclass(); //AbsActionBarView class
switch (itemType) {
case ITEM_ACTION_HOME:
setShowcaseView(homeButton);
break;
case ITEM_TITLE_OR_SPINNER:
try {
Field mTitleViewField = abv.getDeclaredField("mTitleView");
mTitleViewField.setAccessible(true);
View titleView = (View) mTitleViewField.get(p);
if (titleView != null) {
setShowcaseView(titleView);
break;
}
Field mSpinnerField = abv.getDeclaredField("mSpinner");
mSpinnerField.setAccessible(true);
View mSpinnerView = (View) mSpinnerField.get(p);
if (mSpinnerView != null) {
setShowcaseView(mSpinnerView);
break;
}
}
catch (NoSuchFieldException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
break;
case ITEM_ACTION_ITEM:
case ITEM_ACTION_OVERFLOW:
try {
Field mAmpField = absAbv.getDeclaredField("mActionMenuPresenter");
mAmpField.setAccessible(true);
Object mAmp = mAmpField.get(p);
if (itemType == ITEM_ACTION_OVERFLOW) {
// Finds the overflow button associated with the ActionMenuPresenter
Field mObField = mAmp.getClass().getDeclaredField("mOverflowButton");
mObField.setAccessible(true);
View mOb = (View) mObField.get(mAmp);
if (mOb != null)
setShowcaseView(mOb);
}
else {
// Want an ActionItem, so find it
Field mAmvField = mAmp.getClass().getSuperclass().getDeclaredField("mMenuView");
mAmvField.setAccessible(true);
Object mAmv = mAmvField.get(mAmp);
Field mChField;
if (mAmv.getClass().toString().contains("com.actionbarsherlock")) {
// There are thousands of superclasses to traverse up
// Have to get superclasses because mChildren is private
mChField = mAmv.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass()
.getDeclaredField("mChildren");
}
else
mChField = mAmv.getClass().getSuperclass().getSuperclass().getDeclaredField("mChildren");
mChField.setAccessible(true);
Object[] mChs = (Object[]) mChField.get(mAmv);
for (Object mCh : mChs) {
if (mCh != null) {
View v = (View) mCh;
if (v.getId() == actionItemId)
setShowcaseView(v);
}
}
}
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
catch (NoSuchFieldException e) {
e.printStackTrace();
}
catch (NullPointerException npe) {
throw new RuntimeException("insertShowcaseViewWithType() must be called "
+ "after or during onCreateOptionsMenu() of the host Activity");
}
}
}
});
}
/**
* Set the shot method of the showcase - only once or no limit
*
* @param shotType
* either TYPE_ONE_SHOT or TYPE_NO_LIMIT
* @deprecated Use the option in {@link ConfigOptions} instead.
*/
@Deprecated
public void setShotType(int shotType) {
if (shotType == TYPE_NO_LIMIT || shotType == TYPE_ONE_SHOT) {
mOptions.shotType = shotType;
}
}
/**
* Decide whether touches outside the showcased circle should be ignored or not
*
* @param block
* true to block touches, false otherwise. By default, this is true.
* @deprecated Use the option in {@link ConfigOptions} instead.
*/
@Deprecated
public void blockNonShowcasedTouches(boolean block) {
mOptions.block = block;
}
/**
* Override the standard button click event, if there is a button available
*
* @param listener
* Listener to listen to on click events
*/
public void overrideButtonClick(OnClickListener listener) {
if (isRedundant) {
return;
}
if (mButton != null) {
mButton.setOnClickListener(listener);
}
if (mBackupButton != null) {
mBackupButton.setOnClickListener(listener);
}
hasCustomClickListener = true;
}
public void setOnShowcaseEventListener(OnShowcaseEventListener listener) {
mEventListener = listener;
}
@Override
public void dispatchDraw(Canvas canvas) {
if (showcaseX < 0 || showcaseY < 0 || isRedundant) {
super.dispatchDraw(canvas);
return;
}
int width = getMeasuredWidth();
int height = getMeasuredHeight();
Bitmap b = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
//Draw the semi-transparent background
c.drawColor(backColor);
//Erase the area for the ring
c.drawCircle(showcaseX, showcaseY, showcaseRadius, mEraser);
boolean recalculateText = makeVoidedRect();
showcase.setBounds(voidedArea);
showcase.draw(c);
canvas.drawBitmap(b, 0, 0, null);
// Clean up, as we no longer require these items.
try {
c.setBitmap(null);
}
catch (NullPointerException npe) {
//TODO why does this NPE happen?
npe.printStackTrace();
}
b.recycle();
b = null;
if (!TextUtils.isEmpty(mTitleText) || !TextUtils.isEmpty(mSubText)) {
if (recalculateText)
mBestTextPosition = getBestTextPosition(canvas.getWidth(), canvas.getHeight());
if (!TextUtils.isEmpty(mTitleText))
canvas.drawText(mTitleText, mBestTextPosition[0], mBestTextPosition[1], mPaintTitle);
if (!TextUtils.isEmpty(mSubText)) {
canvas.save();
if (recalculateText)
mDynamicDetailLayout = new DynamicLayout(mSubText, mPaintDetail, ((Number) mBestTextPosition[2]).intValue(),
Layout.Alignment.ALIGN_NORMAL, 1.2f, 1.0f, true);
canvas.translate(mBestTextPosition[0], mBestTextPosition[1] + 12 * metricScale);
mDynamicDetailLayout.draw(canvas);
canvas.restore();
}
}
super.dispatchDraw(canvas);
}
private float[] getBestTextPosition(int canvasW, int canvasH) {
//if the width isn't much bigger than the voided area, just consider top & bottom
float spaceTop = voidedArea.top;
float spaceBottom = canvasH - voidedArea.bottom - 64 * metricScale; //64dip considers the OK button
//float spaceLeft = voidedArea.left;
//float spaceRight = canvasW - voidedArea.right;
//TODO currently only considers above or below showcase, deal with left or right
return new float[] {
24 * metricScale,
spaceTop > spaceBottom ? 128 * metricScale : 24 * metricScale + voidedArea.bottom,
canvasW - 48 * metricScale };
}
/**
* Creates a {@link Rect} which represents the area the showcase covers. Used to calculate
* where best to place the text
*
* @return true if voidedArea has changed, false otherwise.
*/
private boolean makeVoidedRect() {
// This if statement saves resources by not recalculating voidedArea
// if the X & Y coordinates haven't changed
if (voidedArea == null || (showcaseX != legacyShowcaseX || showcaseY != legacyShowcaseY)) {
int cx = (int) showcaseX, cy = (int) showcaseY;
int dw = showcase.getIntrinsicWidth();
int dh = showcase.getIntrinsicHeight();
voidedArea = new Rect(cx - dw / 2, cy - dh / 2, cx + dw / 2, cy + dh / 2);
legacyShowcaseX = showcaseX;
legacyShowcaseY = showcaseY;
return true;
}
return false;
}
public AnimatorSet animateGesture(float offsetStartX, float offsetStartY, float offsetEndX, float offsetEndY) {
mHandy = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.handy, null);
addView(mHandy);
ViewHelper.setAlpha(mHandy, 0f);
ObjectAnimator alphaIn = ObjectAnimator.ofFloat(mHandy, "alpha", 0f, 1f).setDuration(500);
ObjectAnimator setUpX = ObjectAnimator.ofFloat(mHandy, "x", showcaseX + offsetStartX).setDuration(0);
ObjectAnimator setUpY = ObjectAnimator.ofFloat(mHandy, "y", showcaseY + offsetStartY).setDuration(0);
ObjectAnimator moveX = ObjectAnimator.ofFloat(mHandy, "x", showcaseX + offsetEndX).setDuration(1000);
ObjectAnimator moveY = ObjectAnimator.ofFloat(mHandy, "y", showcaseY + offsetEndY).setDuration(1000);
moveX.setStartDelay(1000);
moveY.setStartDelay(1000);
ObjectAnimator alphaOut = ObjectAnimator.ofFloat(mHandy, "alpha", 0f).setDuration(500);
alphaOut.setStartDelay(2500);
AnimatorSet as = new AnimatorSet();
as.play(setUpX).with(setUpY).before(alphaIn).before(moveX).with(moveY).before(alphaOut);
as.start();
Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
removeView(mHandy);
}
};
handler.postDelayed(runnable, 3000);
return as;
}
@Override
public void onClick(View view) {
// If the type is set to one-shot, store that it has shot
if (mOptions.shotType == TYPE_ONE_SHOT) {
SharedPreferences internal = getContext().getSharedPreferences("showcase_internal", Context.MODE_PRIVATE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD)
internal.edit().putBoolean("hasShot" + getConfigOptions().showcaseId, true).apply();
else
internal.edit().putBoolean("hasShot" + getConfigOptions().showcaseId, true).commit();
}
hide();
}
public void hide() {
if (mEventListener != null) {
mEventListener.onShowcaseViewHide(this);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ObjectAnimator oa = ObjectAnimator.ofFloat(this, "alpha", 0f);
oa.setDuration(300).addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
oa.start();
}
else {
setVisibility(View.GONE);
}
}
public void show() {
if (mEventListener != null) {
mEventListener.onShowcaseViewShow(this);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ObjectAnimator oa = ObjectAnimator.ofFloat(this, "alpha", 1f);
oa.setDuration(300).addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
oa.start();
}
else {
setVisibility(View.VISIBLE);
}
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
float xDelta = Math.abs(motionEvent.getRawX() - showcaseX);
float yDelta = Math.abs(motionEvent.getRawY() - showcaseY);
double distanceFromFocus = Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2));
if (mOptions.hideOnClickOutside && distanceFromFocus > showcaseRadius) {
this.hide();
return true;
}
if (!mOptions.block)
return false;
return distanceFromFocus > showcaseRadius;
}
public interface OnShowcaseEventListener {
public void onShowcaseViewHide(ShowcaseView showcaseView);
public void onShowcaseViewShow(ShowcaseView showcaseView);
}
public ShowcaseView setTextColors(int titleTextColor, int detailTextColor) {
this.titleTextColor = titleTextColor;
this.detailTextColor = detailTextColor;
if (mPaintTitle != null)
mPaintTitle.setColor(titleTextColor);
if (mPaintDetail != null)
mPaintDetail.setColor(detailTextColor);
invalidate();
return this;
}
public void setText(String titleText, String subText) {
mTitleText = titleText;
mSubText = subText;
invalidate();
}
public void setText(int titleText, int subText) {
mTitleText = mContext.getResources().getString(titleText);
mSubText = mContext.getResources().getString(subText);
invalidate();
}
/**
* Get the ghostly gesture hand for custom gestures
*
* @return a View representing the ghostly hand
*/
public View getHand() {
final View mHandy = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.handy, null);
addView(mHandy);
ViewHelper.setAlpha(mHandy, 0f);
return mHandy;
}
/**
* Point to a specific view
*
* @param view
* The {@link View} to Showcase
*/
public void pointTo(View view) {
ObjectAnimator alphaIn = ObjectAnimator.ofFloat(mHandy, "alpha", 0f, 1f).setDuration(500);
ObjectAnimator setUpX = ObjectAnimator.ofFloat(mHandy, "x", view.getX() + view.getWidth() / 2).setDuration(0);
ObjectAnimator setUpY = ObjectAnimator.ofFloat(mHandy, "y", view.getY() + view.getHeight() / 2).setDuration(0);
AnimatorSet as = new AnimatorSet();
as.play(setUpX).with(setUpY).before(alphaIn);
as.start();
}
/**
* Point to a specific point on the screen
*
* @param x
* X-coordinate to point to
* @param y
* Y-coordinate to point to
*/
public void pointTo(float x, float y) {
ObjectAnimator alphaIn = ObjectAnimator.ofFloat(mHandy, "alpha", 0f, 1f).setDuration(500);
ObjectAnimator setUpX = ObjectAnimator.ofFloat(mHandy, "x", x).setDuration(0);
ObjectAnimator setUpY = ObjectAnimator.ofFloat(mHandy, "y", y).setDuration(0);
AnimatorSet as = new AnimatorSet();
as.play(setUpX).with(setUpY).before(alphaIn);
as.start();
}
private void setConfigOptions(ConfigOptions options) {
mOptions = options;
}
private ConfigOptions getConfigOptions() {
// Make sure that this method never returns null
if (mOptions == null)
return mOptions = new ConfigOptions();
return mOptions;
}
/**
* Quick method to insert a ShowcaseView into an Activity
*
* @param viewToShowcase
* View to showcase
* @param activity
* Activity to insert into
* @param title
* Text to show as a title. Can be null.
* @param detailText
* More detailed text. Can be null.
* @param options
* A set of options to customise the ShowcaseView
* @return the created ShowcaseView instance
*/
public static ShowcaseView insertShowcaseView(View viewToShowcase, Activity activity, String title, String detailText,
ConfigOptions options) {
ShowcaseView sv = new ShowcaseView(activity);
if (options != null)
sv.setConfigOptions(options);
if (sv.getConfigOptions().insert == INSERT_TO_DECOR) {
((ViewGroup) activity.getWindow().getDecorView()).addView(sv);
}
else {
((ViewGroup) activity.findViewById(android.R.id.content)).addView(sv);
}
sv.setShowcaseView(viewToShowcase);
sv.setText(title, detailText);
return sv;
}
/**
* Quick method to insert a ShowcaseView into an Activity
*
* @param viewToShowcase
* View to showcase
* @param activity
* Activity to insert into
* @param title
* Text to show as a title. Can be null.
* @param detailText
* More detailed text. Can be null.
* @param options
* A set of options to customise the ShowcaseView
* @return the created ShowcaseView instance
*/
public static ShowcaseView insertShowcaseView(View viewToShowcase, Activity activity, int title, int detailText, ConfigOptions options) {
ShowcaseView sv = new ShowcaseView(activity);
if (options != null)
sv.setConfigOptions(options);
if (sv.getConfigOptions().insert == INSERT_TO_DECOR) {
((ViewGroup) activity.getWindow().getDecorView()).addView(sv);
}
else {
((ViewGroup) activity.findViewById(android.R.id.content)).addView(sv);
}
sv.setShowcaseView(viewToShowcase);
sv.setText(title, detailText);
return sv;
}
public static ShowcaseView insertShowcaseView(int showcaseViewId, Activity activity, String title, String detailText,
ConfigOptions options) {
View v = activity.findViewById(showcaseViewId);
if (v != null)
return insertShowcaseView(v, activity, title, detailText, options);
return null;
}
public static ShowcaseView insertShowcaseView(int showcaseViewId, Activity activity, int title, int detailText, ConfigOptions options) {
View v = activity.findViewById(showcaseViewId);
if (v != null)
return insertShowcaseView(v, activity, title, detailText, options);
return null;
}
public static ShowcaseView insertShowcaseView(float x, float y, Activity activity, String title, String detailText,
ConfigOptions options) {
ShowcaseView sv = new ShowcaseView(activity);
if (options != null)
sv.setConfigOptions(options);
if (sv.getConfigOptions().insert == INSERT_TO_DECOR) {
((ViewGroup) activity.getWindow().getDecorView()).addView(sv);
}
else {
((ViewGroup) activity.findViewById(android.R.id.content)).addView(sv);
}
sv.setShowcasePosition(x, y);
sv.setText(title, detailText);
return sv;
}
public static ShowcaseView insertShowcaseView(float x, float y, Activity activity, int title, int detailText, ConfigOptions options) {
ShowcaseView sv = new ShowcaseView(activity);
if (options != null)
sv.setConfigOptions(options);
if (sv.getConfigOptions().insert == INSERT_TO_DECOR) {
((ViewGroup) activity.getWindow().getDecorView()).addView(sv);
}
else {
((ViewGroup) activity.findViewById(android.R.id.content)).addView(sv);
}
sv.setShowcasePosition(x, y);
sv.setText(title, detailText);
return sv;
}
public static ShowcaseView insertShowcaseView(View showcase, Activity activity) {
return insertShowcaseView(showcase, activity, null, null, null);
}
/**
* Quickly insert a ShowcaseView into an Activity, highlighting an item.
*
* @param type
* the type of item to showcase (can be ITEM_ACTION_HOME, ITEM_TITLE_OR_SPINNER, ITEM_ACTION_ITEM or ITEM_ACTION_OVERFLOW)
* @param itemId
* the ID of an Action item to showcase (only required for ITEM_ACTION_ITEM
* @param activity
* Activity to insert the ShowcaseView into
* @param title
* Text to show as a title. Can be null.
* @param detailText
* More detailed text. Can be null.
* @param options
* A set of options to customise the ShowcaseView
* @return the created ShowcaseView instance
*/
public static ShowcaseView insertShowcaseViewWithType(int type, int itemId, Activity activity, String title, String detailText,
ConfigOptions options) {
ShowcaseView sv = new ShowcaseView(activity);
if (options != null)
sv.setConfigOptions(options);
if (sv.getConfigOptions().insert == INSERT_TO_DECOR) {
((ViewGroup) activity.getWindow().getDecorView()).addView(sv);
}
else {
((ViewGroup) activity.findViewById(android.R.id.content)).addView(sv);
}
sv.setShowcaseItem(type, itemId, activity);
sv.setText(title, detailText);
return sv;
}
/**
* Quickly insert a ShowcaseView into an Activity, highlighting an item.
*
* @param type
* the type of item to showcase (can be ITEM_ACTION_HOME, ITEM_TITLE_OR_SPINNER, ITEM_ACTION_ITEM or ITEM_ACTION_OVERFLOW)
* @param itemId
* the ID of an Action item to showcase (only required for ITEM_ACTION_ITEM
* @param activity
* Activity to insert the ShowcaseView into
* @param title
* Text to show as a title. Can be null.
* @param detailText
* More detailed text. Can be null.
* @param options
* A set of options to customise the ShowcaseView
* @return the created ShowcaseView instance
*/
public static ShowcaseView insertShowcaseViewWithType(int type, int itemId, Activity activity, int title, int detailText,
ConfigOptions options) {
ShowcaseView sv = new ShowcaseView(activity);
if (options != null)
sv.setConfigOptions(options);
if (sv.getConfigOptions().insert == INSERT_TO_DECOR) {
((ViewGroup) activity.getWindow().getDecorView()).addView(sv);
}
else {
((ViewGroup) activity.findViewById(android.R.id.content)).addView(sv);
}
sv.setShowcaseItem(type, itemId, activity);
sv.setText(title, detailText);
return sv;
}
public static ShowcaseView insertShowcaseView(float x, float y, Activity activity) {
return insertShowcaseView(x, y, activity, null, null, null);
}
public static class ConfigOptions {
public boolean block = true, noButton = false;
public int showcaseId = 0;
public int shotType = TYPE_NO_LIMIT;
public int insert = INSERT_TO_DECOR;
public boolean hideOnClickOutside = false;
}
}