Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,64 +1,73 @@
package com.getkeepsafe.taptargetviewsample;

import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Display;
import android.widget.FrameLayout;
import android.widget.TextView;

import com.getkeepsafe.taptargetview.TapTarget;
import com.getkeepsafe.taptargetview.TapTargetSequence;
import com.getkeepsafe.taptargetview.TapTargetView;

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new TapTargetView.Builder(this)
.title("Hello, world!")
.description("This is the sample app for TapTargetView")
.tintTarget(false)
.listener(new TapTargetView.Listener() {
@Override
public void onTargetClick(TapTargetView view) {
super.onTargetClick(view);
educateBackButton();
}

@Override
public void onTargetCancel(TapTargetView view) {
super.onTargetCancel(view);
educateBackButton();
}
}).showFor(findViewById(R.id.fab));
}
// We load a drawable and create a location to show a tap target here
// We need the display to get the width and height at this point in time
final Display display = getWindowManager().getDefaultDisplay();
// Load our little droid guy
final Drawable droid = getResources().getDrawable(R.drawable.ic_android_black_24dp, getTheme());
// Tell our droid buddy where we want him to appear
final Rect droidTarget = new Rect(0, 0, droid.getIntrinsicWidth() * 2, droid.getIntrinsicHeight() * 2);
// Using deprecated methods makes you look way cool
droidTarget.offset(display.getWidth() / 2, display.getHeight() / 2);

private void educateBackButton() {
new TapTargetView.Builder(MainActivity.this)
.title("This is the back button")
.description("It allows you to go back, sometimes.")
.listener(new TapTargetView.Listener() {
// We have a sequence of targets, so lets build it!
final TapTargetSequence sequence = new TapTargetSequence(this)
.targets(
// This tap target will target the back button, we just need to pass its view
TapTarget.forView(findViewById(R.id.back), "This is the back button", "It allows you to go back, sometimes"),
// Likewise, this tap target will target the search button
TapTarget.forView(findViewById(R.id.search), "This is a search icon", "As you can see, it has gotten pretty dark around here...")
.dimColor(android.R.color.black)
.outerCircleColor(R.color.colorAccent)
.targetCircleColor(android.R.color.black)
.textColor(android.R.color.black),
// This tap target will target our droid buddy at the given target rect
TapTarget.forBounds(droidTarget, "Oh look!", "You can point to any part of the screen. You also can't cancel this one!")
.cancelable(false)
.icon(droid)
)
.listener(new TapTargetSequence.Listener() {
// This listener will tell us when interesting(tm) events happen in regards
// to the sequence
@Override
public void onTargetClick(TapTargetView view) {
super.onTargetClick(view);
educateSearchButton();
public void onSequenceFinish() {
((TextView) findViewById(R.id.educated)).setText("Congratulations! You're educated now!");
}
})
.showFor(findViewById(R.id.back));
}

private void educateSearchButton() {
new TapTargetView.Builder(MainActivity.this)
.title("This is a search icon")
.description("As you can see, it has gotten pretty dark around here...")
.dimColor(android.R.color.black)
.outerCircleColor(R.color.colorAccent)
.targetCircleColor(android.R.color.black)
.textColor(android.R.color.black)
.listener(new TapTargetView.Listener() {
@Override
public void onTargetClick(TapTargetView view) {
super.onTargetClick(view);
((TextView) findViewById(R.id.educated)).setText("Congratulations! You're educated now!");
public void onSequenceCanceled() {
((TextView) findViewById(R.id.educated)).setText("Uh oh! You canceled the sequence :(");
}
})
.showFor(findViewById(R.id.search));
});

// You don't always need a sequence, and for that there's a single time tap target
TapTargetView.showFor(this, TapTarget.forView(findViewById(R.id.fab), "Hello, world!", "This is the sample app for TapTargetView")
.cancelable(false)
.tintTarget(false), new TapTargetView.Listener() {
@Override
public void onTargetClick(TapTargetView view) {
super.onTargetClick(view);
// .. which evidently starts the sequence we defined earlier
sequence.start();
}
});
}
}
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_android_black_24dp.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/**
* Copyright 2016 Keepsafe Software, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.getkeepsafe.taptargetview;

import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorRes;
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 String title;
final String description;

Rect bounds;
Drawable icon;
Typeface typeface;

@ColorRes int outerCircleColor = -1;
@ColorRes int targetCircleColor = -1;
@ColorRes int dimColor = -1;
@ColorRes int textColor = -1;

boolean drawShadow = true;
boolean cancelable = true;
boolean tintTarget = true;

/** Return a tap target for the specified view **/
public static ViewTapTarget forView(View view, String title, String description) {
return new ViewTapTarget(view, title, description);
}

/** Return a tap target for the specified bounds **/
public static TapTarget forBounds(Rect bounds, String title, String description) {
return new TapTarget(bounds, title, description);
}

protected TapTarget(Rect bounds, String title, String description) {
this(title, description);
if (bounds == null) {
throw new IllegalArgumentException("Cannot pass null bounds, title or description");
}

this.bounds = bounds;
}

protected TapTarget(String title, String description) {
if (title == null || description == null) {
throw new IllegalArgumentException("Cannot pass null title or description");
}

this.title = title;
this.description = description;
}

/** Specify the color resource for the outer circle **/
public TapTarget outerCircleColor(@ColorRes int color) {
this.outerCircleColor = color;
return this;
}

/** Specify the color resource for the target circle **/
public TapTarget targetCircleColor(@ColorRes int color) {
this.targetCircleColor = color;
return this;
}

/** Specify the color resource for all text **/
public TapTarget textColor(@ColorRes int color) {
this.textColor = 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");
this.typeface = typeface;
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.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;
}

/**
* 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;
}
}
Loading