aboutsummaryrefslogtreecommitdiffstats
path: root/showcaseview
diff options
context:
space:
mode:
authorBananeweizen <bananeweizen@gmx.de>2014-08-14 22:28:19 +0200
committerBananeweizen <bananeweizen@gmx.de>2014-08-14 22:28:19 +0200
commitcfd75411787da9809a4f7e4e5e1f0be6a9a0fa77 (patch)
tree94687c96ea7cb15507e7310e419e371071d219ae /showcaseview
parent8c468294662cb57a1d7b8ec8b9a9195e277bc339 (diff)
downloadcgeo-cfd75411787da9809a4f7e4e5e1f0be6a9a0fa77.zip
cgeo-cfd75411787da9809a4f7e4e5e1f0be6a9a0fa77.tar.gz
cgeo-cfd75411787da9809a4f7e4e5e1f0be6a9a0fa77.tar.bz2
initial showcase implementation
Diffstat (limited to 'showcaseview')
-rw-r--r--showcaseview/.classpath9
-rw-r--r--showcaseview/.project33
-rw-r--r--showcaseview/.settings/org.eclipse.jdt.core.prefs4
-rw-r--r--showcaseview/AndroidManifest.xml24
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/AnimationFactory.java36
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/AnimatorAnimationFactory.java96
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/ApiUtils.java39
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/Calculator.java34
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/NewShowcaseDrawer.java71
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/OnShowcaseEventListener.java61
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/ShotStateStore.java56
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseAreaCalculator.java60
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseDrawer.java42
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseView.java603
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/StandardShowcaseDrawer.java99
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/TextDrawer.java217
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionBarReflector.java54
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionBarViewWrapper.java164
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionItemTarget.java53
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionViewTarget.java75
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/targets/AppCompatReflector.java59
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/targets/PointTarget.java40
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/targets/Reflector.java32
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/targets/ReflectorFactory.java52
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/targets/SherlockReflector.java59
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/targets/Target.java30
-rw-r--r--showcaseview/java/com/github/amlcurran/showcaseview/targets/ViewTarget.java46
-rw-r--r--showcaseview/project.properties15
-rw-r--r--showcaseview/res/drawable-hdpi/btn_cling_normal.9.pngbin0 -> 333 bytes
-rw-r--r--showcaseview/res/drawable-hdpi/btn_cling_pressed.9.pngbin0 -> 467 bytes
-rw-r--r--showcaseview/res/drawable-hdpi/cling.pngbin0 -> 57394 bytes
-rw-r--r--showcaseview/res/drawable-hdpi/hand.pngbin0 -> 16891 bytes
-rw-r--r--showcaseview/res/drawable-mdpi/btn_cling_normal.9.pngbin0 -> 245 bytes
-rw-r--r--showcaseview/res/drawable-mdpi/btn_cling_pressed.9.pngbin0 -> 370 bytes
-rw-r--r--showcaseview/res/drawable-mdpi/cling.pngbin0 -> 32299 bytes
-rw-r--r--showcaseview/res/drawable-mdpi/hand.pngbin0 -> 9752 bytes
-rw-r--r--showcaseview/res/drawable-xhdpi/btn_cling_normal.9.pngbin0 -> 400 bytes
-rw-r--r--showcaseview/res/drawable-xhdpi/btn_cling_pressed.9.pngbin0 -> 601 bytes
-rw-r--r--showcaseview/res/drawable-xhdpi/cling.pngbin0 -> 94777 bytes
-rw-r--r--showcaseview/res/drawable-xhdpi/cling_bleached.pngbin0 -> 97870 bytes
-rw-r--r--showcaseview/res/drawable-xhdpi/hand.pngbin0 -> 26636 bytes
-rw-r--r--showcaseview/res/drawable/button.xml20
-rw-r--r--showcaseview/res/drawable/button_normal.xml40
-rw-r--r--showcaseview/res/drawable/cling_button_bg.xml21
-rw-r--r--showcaseview/res/layout/handy.xml22
-rw-r--r--showcaseview/res/layout/showcase_button.xml21
-rw-r--r--showcaseview/res/values/attrs.xml35
-rw-r--r--showcaseview/res/values/dimens.xml25
-rw-r--r--showcaseview/res/values/ids.xml21
-rw-r--r--showcaseview/res/values/strings.xml20
-rw-r--r--showcaseview/res/values/styles.xml61
51 files changed, 2449 insertions, 0 deletions
diff --git a/showcaseview/.classpath b/showcaseview/.classpath
new file mode 100644
index 0000000..be2dd15
--- /dev/null
+++ b/showcaseview/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="gen"/>
+ <classpathentry kind="src" path="java"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+ <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/showcaseview/.project b/showcaseview/.project
new file mode 100644
index 0000000..5be774b
--- /dev/null
+++ b/showcaseview/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>showcaseview</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/showcaseview/.settings/org.eclipse.jdt.core.prefs b/showcaseview/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..b080d2d
--- /dev/null
+++ b/showcaseview/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/showcaseview/AndroidManifest.xml b/showcaseview/AndroidManifest.xml
new file mode 100644
index 0000000..28494cf
--- /dev/null
+++ b/showcaseview/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2014 Alex Curran
+ ~
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.github.amlcurran.showcaseview">
+ <uses-sdk
+ android:minSdkVersion="4"
+ android:targetSdkVersion="19" />
+
+</manifest>
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/AnimationFactory.java b/showcaseview/java/com/github/amlcurran/showcaseview/AnimationFactory.java
new file mode 100644
index 0000000..dac6a00
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/AnimationFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+import android.graphics.Point;
+import android.view.View;
+
+interface AnimationFactory {
+ void fadeInView(View target, long duration, AnimationStartListener listener);
+
+ void fadeOutView(View target, long duration, AnimationEndListener listener);
+
+ void animateTargetToPoint(ShowcaseView showcaseView, Point point);
+
+ public interface AnimationStartListener {
+ void onAnimationStart();
+ }
+
+ public interface AnimationEndListener {
+ void onAnimationEnd();
+ }
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/AnimatorAnimationFactory.java b/showcaseview/java/com/github/amlcurran/showcaseview/AnimatorAnimationFactory.java
new file mode 100644
index 0000000..bbc24b7
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/AnimatorAnimationFactory.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.graphics.Point;
+import android.view.View;
+import android.view.animation.AccelerateDecelerateInterpolator;
+
+class AnimatorAnimationFactory implements AnimationFactory {
+
+ private static final String ALPHA = "alpha";
+ private static final float INVISIBLE = 0f;
+ private static final float VISIBLE = 1f;
+
+ private final AccelerateDecelerateInterpolator interpolator;
+
+ public AnimatorAnimationFactory() {
+ interpolator = new AccelerateDecelerateInterpolator();
+ }
+
+ @Override
+ public void fadeInView(View target, long duration, final AnimationStartListener listener) {
+ ObjectAnimator oa = ObjectAnimator.ofFloat(target, ALPHA, INVISIBLE, VISIBLE);
+ oa.setDuration(duration).addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animator) {
+ listener.onAnimationStart();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animator) {
+ }
+ });
+ oa.start();
+ }
+
+ @Override
+ public void fadeOutView(View target, long duration, final AnimationEndListener listener) {
+ ObjectAnimator oa = ObjectAnimator.ofFloat(target, ALPHA, INVISIBLE);
+ oa.setDuration(duration).addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animator) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ listener.onAnimationEnd();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animator) {
+ }
+ });
+ oa.start();
+ }
+
+ @Override
+ public void animateTargetToPoint(ShowcaseView showcaseView, Point point) {
+ AnimatorSet set = new AnimatorSet();
+ ObjectAnimator xAnimator = ObjectAnimator.ofInt(showcaseView, "showcaseX", point.x);
+ ObjectAnimator yAnimator = ObjectAnimator.ofInt(showcaseView, "showcaseY", point.y);
+ set.playTogether(xAnimator, yAnimator);
+ set.setInterpolator(interpolator);
+ set.start();
+ }
+
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/ApiUtils.java b/showcaseview/java/com/github/amlcurran/showcaseview/ApiUtils.java
new file mode 100644
index 0000000..2e94e1d
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/ApiUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.view.View;
+
+public class ApiUtils {
+
+ public boolean isCompatWith(int versionCode) {
+ return Build.VERSION.SDK_INT >= versionCode;
+ }
+
+ public boolean isCompatWithHoneycomb() {
+ return isCompatWith(Build.VERSION_CODES.HONEYCOMB);
+ }
+
+ @TargetApi(14)
+ public void setFitsSystemWindowsCompat(View view) {
+ if (isCompatWith(Build.VERSION_CODES.ICE_CREAM_SANDWICH)) {
+ view.setFitsSystemWindows(true);
+ }
+ }
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/Calculator.java b/showcaseview/java/com/github/amlcurran/showcaseview/Calculator.java
new file mode 100644
index 0000000..78fba6f
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/Calculator.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+import android.graphics.Point;
+import android.view.View;
+
+/**
+ * Calculates various items for use with ShowcaseView
+ */
+class Calculator {
+
+ static Point getShowcasePointFromView(View view) {
+ Point result = new Point();
+ result.x = view.getLeft() + view.getWidth() / 2;
+ result.y = view.getTop() + view.getHeight() / 2;
+ return result;
+ }
+
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/NewShowcaseDrawer.java b/showcaseview/java/com/github/amlcurran/showcaseview/NewShowcaseDrawer.java
new file mode 100644
index 0000000..8d16b86
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/NewShowcaseDrawer.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+/**
+ * Created by curraa01 on 13/10/2013.
+ */
+class NewShowcaseDrawer extends StandardShowcaseDrawer {
+
+ private static final int ALPHA_60_PERCENT = 153;
+ private final float outerRadius;
+ private final float innerRadius;
+
+ public NewShowcaseDrawer(Resources resources) {
+ super(resources);
+ outerRadius = resources.getDimension(R.dimen.showcase_radius_outer);
+ innerRadius = resources.getDimension(R.dimen.showcase_radius_inner);
+ }
+
+ @Override
+ public void setShowcaseColour(int color) {
+ eraserPaint.setColor(color);
+ }
+
+ @Override
+ public void drawShowcase(Bitmap buffer, float x, float y, float scaleMultiplier) {
+ Canvas bufferCanvas = new Canvas(buffer);
+ eraserPaint.setAlpha(ALPHA_60_PERCENT);
+ bufferCanvas.drawCircle(x, y, outerRadius, eraserPaint);
+ eraserPaint.setAlpha(0);
+ bufferCanvas.drawCircle(x, y, innerRadius, eraserPaint);
+ }
+
+ @Override
+ public int getShowcaseWidth() {
+ return (int) (outerRadius * 2);
+ }
+
+ @Override
+ public int getShowcaseHeight() {
+ return (int) (outerRadius * 2);
+ }
+
+ @Override
+ public float getBlockedRadius() {
+ return innerRadius;
+ }
+
+ @Override
+ public void setBackgroundColour(int backgroundColor) {
+ this.backgroundColour = backgroundColor;
+ }
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/OnShowcaseEventListener.java b/showcaseview/java/com/github/amlcurran/showcaseview/OnShowcaseEventListener.java
new file mode 100644
index 0000000..1ff298a
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/OnShowcaseEventListener.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+/**
+* @author Alex
+*/
+public interface OnShowcaseEventListener {
+
+ /**
+ * Called when the ShowcaseView has been told to hide. Use {@link #onShowcaseViewDidHide(ShowcaseView)}
+ * if you want to know when the ShowcaseView has been fully hidden.
+ */
+ public void onShowcaseViewHide(ShowcaseView showcaseView);
+
+ /**
+ * Called when the animation hiding the ShowcaseView has finished, and it is no longer visible on the screen.
+ */
+ public void onShowcaseViewDidHide(ShowcaseView showcaseView);
+
+ /**
+ * Called when the ShowcaseView is shown.
+ */
+ public void onShowcaseViewShow(ShowcaseView showcaseView);
+
+ /**
+ * Empty implementation of OnShowcaseViewEventListener such that null
+ * checks aren't needed
+ */
+ public static final OnShowcaseEventListener NONE = new OnShowcaseEventListener() {
+ @Override
+ public void onShowcaseViewHide(ShowcaseView showcaseView) {
+
+ }
+
+ @Override
+ public void onShowcaseViewDidHide(ShowcaseView showcaseView) {
+
+ }
+
+ @Override
+ public void onShowcaseViewShow(ShowcaseView showcaseView) {
+
+ }
+ };
+
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/ShotStateStore.java b/showcaseview/java/com/github/amlcurran/showcaseview/ShotStateStore.java
new file mode 100644
index 0000000..6ff782a
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/ShotStateStore.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+class ShotStateStore {
+
+ private static final String PREFS_SHOWCASE_INTERNAL = "showcase_internal";
+ private static final int INVALID_SHOT_ID = -1;
+
+ long shotId = INVALID_SHOT_ID;
+
+ private final Context context;
+
+ public ShotStateStore(Context context) {
+ this.context = context;
+ }
+
+ boolean hasShot() {
+ return isSingleShot() && context
+ .getSharedPreferences(PREFS_SHOWCASE_INTERNAL, Context.MODE_PRIVATE)
+ .getBoolean("hasShot" + shotId, false);
+ }
+
+ boolean isSingleShot() {
+ return shotId != INVALID_SHOT_ID;
+ }
+
+ void storeShot() {
+ if (isSingleShot()) {
+ SharedPreferences internal = context.getSharedPreferences(PREFS_SHOWCASE_INTERNAL, Context.MODE_PRIVATE);
+ internal.edit().putBoolean("hasShot" + shotId, true).apply();
+ }
+ }
+
+ void setSingleShot(long shotId) {
+ this.shotId = shotId;
+ }
+
+} \ No newline at end of file
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseAreaCalculator.java b/showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseAreaCalculator.java
new file mode 100644
index 0000000..b50633b
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseAreaCalculator.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+import android.graphics.Rect;
+import android.util.Log;
+
+/**
+ * Class responsible for calculating where the Showcase should position itself
+ */
+class ShowcaseAreaCalculator {
+
+ private final Rect mShowcaseRect = new Rect();
+
+ /**
+ * Creates a {@link android.graphics.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.
+ */
+ public boolean calculateShowcaseRect(float x, float y, ShowcaseDrawer showcaseDrawer) {
+
+ int cx = (int) x, cy = (int) y;
+ int dw = showcaseDrawer.getShowcaseWidth();
+ int dh = showcaseDrawer.getShowcaseHeight();
+
+ if (mShowcaseRect.left == cx - dw / 2 && mShowcaseRect.top == cy - dh / 2) {
+ return false;
+ }
+
+ Log.d("ShowcaseView", "Recalculated");
+
+ mShowcaseRect.left = cx - dw / 2;
+ mShowcaseRect.top = cy - dh / 2;
+ mShowcaseRect.right = cx + dw / 2;
+ mShowcaseRect.bottom = cy + dh / 2;
+
+ return true;
+
+ }
+
+ public Rect getShowcaseRect() {
+ return mShowcaseRect;
+ }
+
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseDrawer.java b/showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseDrawer.java
new file mode 100644
index 0000000..3cd82aa
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseDrawer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+/**
+ * Created by curraa01 on 13/10/2013.
+ */
+interface ShowcaseDrawer {
+
+ void setShowcaseColour(int color);
+
+ void drawShowcase(Bitmap buffer, float x, float y, float scaleMultiplier);
+
+ int getShowcaseWidth();
+
+ int getShowcaseHeight();
+
+ float getBlockedRadius();
+
+ void setBackgroundColour(int backgroundColor);
+
+ void erase(Bitmap bitmapBuffer);
+
+ void drawToCanvas(Canvas canvas, Bitmap bitmapBuffer);
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseView.java b/showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseView.java
new file mode 100644
index 0000000..6c32c8d
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/ShowcaseView.java
@@ -0,0 +1,603 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.PorterDuff;
+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.ViewTreeObserver;
+import android.widget.Button;
+import android.widget.RelativeLayout;
+
+import com.github.amlcurran.showcaseview.targets.Target;
+
+import static com.github.amlcurran.showcaseview.AnimationFactory.AnimationEndListener;
+import static com.github.amlcurran.showcaseview.AnimationFactory.AnimationStartListener;
+
+/**
+ * A view which allows you to showcase areas of your app with an explanation.
+ */
+public class ShowcaseView extends RelativeLayout
+ implements View.OnClickListener, View.OnTouchListener, ViewTreeObserver.OnPreDrawListener, ViewTreeObserver.OnGlobalLayoutListener {
+
+ private static final int HOLO_BLUE = Color.parseColor("#33B5E5");
+
+ private final Button mEndButton;
+ private final TextDrawer textDrawer;
+ private final ShowcaseDrawer showcaseDrawer;
+ private final ShowcaseAreaCalculator showcaseAreaCalculator;
+ private final AnimationFactory animationFactory;
+ private final ShotStateStore shotStateStore;
+
+ // Showcase metrics
+ private int showcaseX = -1;
+ private int showcaseY = -1;
+ private float scaleMultiplier = 1f;
+
+ // Touch items
+ private boolean hasCustomClickListener = false;
+ private boolean blockTouches = true;
+ private boolean hideOnTouch = false;
+ private OnShowcaseEventListener mEventListener = OnShowcaseEventListener.NONE;
+
+ private boolean hasAlteredText = false;
+ private boolean hasNoTarget = false;
+ private boolean shouldCentreText;
+ private Bitmap bitmapBuffer;
+
+ // Animation items
+ private long fadeInMillis;
+ private long fadeOutMillis;
+
+ protected ShowcaseView(Context context, boolean newStyle) {
+ this(context, null, R.styleable.CustomTheme_showcaseViewStyle, newStyle);
+ }
+
+ protected ShowcaseView(Context context, AttributeSet attrs, int defStyle, boolean newStyle) {
+ super(context, attrs, defStyle);
+
+ ApiUtils apiUtils = new ApiUtils();
+ animationFactory = new AnimatorAnimationFactory();
+ showcaseAreaCalculator = new ShowcaseAreaCalculator();
+ shotStateStore = new ShotStateStore(context);
+
+ apiUtils.setFitsSystemWindowsCompat(this);
+ getViewTreeObserver().addOnPreDrawListener(this);
+ getViewTreeObserver().addOnGlobalLayoutListener(this);
+
+ // Get the attributes for the ShowcaseView
+ final TypedArray styled = context.getTheme()
+ .obtainStyledAttributes(attrs, R.styleable.ShowcaseView, R.attr.showcaseViewStyle,
+ R.style.ShowcaseView);
+
+ // Set the default animation times
+ fadeInMillis = getResources().getInteger(android.R.integer.config_mediumAnimTime);
+ fadeOutMillis = getResources().getInteger(android.R.integer.config_mediumAnimTime);
+
+ mEndButton = (Button) LayoutInflater.from(context).inflate(R.layout.showcase_button, null);
+ if (newStyle) {
+ showcaseDrawer = new NewShowcaseDrawer(getResources());
+ } else {
+ showcaseDrawer = new StandardShowcaseDrawer(getResources());
+ }
+ textDrawer = new TextDrawer(getResources(), showcaseAreaCalculator, getContext());
+
+ updateStyle(styled, false);
+
+ init();
+ }
+
+ private void init() {
+
+ setOnTouchListener(this);
+
+ if (mEndButton.getParent() == null) {
+ int margin = (int) getResources().getDimension(R.dimen.button_margin);
+ RelativeLayout.LayoutParams lps = (LayoutParams) generateDefaultLayoutParams();
+ lps.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+ lps.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+ lps.setMargins(margin, margin, margin, margin);
+ mEndButton.setLayoutParams(lps);
+ mEndButton.setText(android.R.string.ok);
+ if (!hasCustomClickListener) {
+ mEndButton.setOnClickListener(this);
+ }
+ addView(mEndButton);
+ }
+
+ }
+
+ private boolean hasShot() {
+ return shotStateStore.hasShot();
+ }
+
+ void setShowcasePosition(Point point) {
+ setShowcasePosition(point.x, point.y);
+ }
+
+ void setShowcasePosition(int x, int y) {
+ if (shotStateStore.hasShot()) {
+ return;
+ }
+ showcaseX = x;
+ showcaseY = y;
+ //init();
+ invalidate();
+ }
+
+ public void setTarget(final Target target) {
+ setShowcase(target, false);
+ }
+
+ public void setShowcase(final Target target, final boolean animate) {
+ postDelayed(new Runnable() {
+ @Override
+ public void run() {
+
+ if (!shotStateStore.hasShot()) {
+
+ updateBitmap();
+ Point targetPoint = target.getPoint();
+ if (targetPoint != null) {
+ hasNoTarget = false;
+ if (animate) {
+ animationFactory.animateTargetToPoint(ShowcaseView.this, targetPoint);
+ } else {
+ setShowcasePosition(targetPoint);
+ }
+ } else {
+ hasNoTarget = true;
+ invalidate();
+ }
+
+ }
+ }
+ }, 100);
+ }
+
+ private void updateBitmap() {
+ if (bitmapBuffer == null || haveBoundsChanged()) {
+ if(bitmapBuffer != null)
+ bitmapBuffer.recycle();
+ bitmapBuffer = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
+
+ }
+ }
+
+ private boolean haveBoundsChanged() {
+ return getMeasuredWidth() != bitmapBuffer.getWidth() ||
+ getMeasuredHeight() != bitmapBuffer.getHeight();
+ }
+
+ public boolean hasShowcaseView() {
+ return (showcaseX != 1000000 && showcaseY != 1000000) && !hasNoTarget;
+ }
+
+ public void setShowcaseX(int x) {
+ setShowcasePosition(x, showcaseY);
+ }
+
+ public void setShowcaseY(int y) {
+ setShowcasePosition(showcaseX, y);
+ }
+
+ public int getShowcaseX() {
+ return showcaseX;
+ }
+
+ public int getShowcaseY() {
+ return showcaseY;
+ }
+
+ /**
+ * Override the standard button click event
+ *
+ * @param listener Listener to listen to on click events
+ */
+ public void overrideButtonClick(OnClickListener listener) {
+ if (shotStateStore.hasShot()) {
+ return;
+ }
+ if (mEndButton != null) {
+ mEndButton.setOnClickListener(listener != null ? listener : this);
+ }
+ hasCustomClickListener = true;
+ }
+
+ public void setOnShowcaseEventListener(OnShowcaseEventListener listener) {
+ if (listener != null) {
+ mEventListener = listener;
+ } else {
+ mEventListener = OnShowcaseEventListener.NONE;
+ }
+ }
+
+ public void setButtonText(CharSequence text) {
+ if (mEndButton != null) {
+ mEndButton.setText(text);
+ }
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ boolean recalculatedCling = showcaseAreaCalculator.calculateShowcaseRect(showcaseX, showcaseY, showcaseDrawer);
+ boolean recalculateText = recalculatedCling || hasAlteredText;
+ if (recalculateText) {
+ textDrawer.calculateTextPosition(getMeasuredWidth(), getMeasuredHeight(), this, shouldCentreText);
+ }
+ hasAlteredText = false;
+ return true;
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (showcaseX < 0 || showcaseY < 0 || shotStateStore.hasShot()) {
+ super.dispatchDraw(canvas);
+ return;
+ }
+
+ //Draw background color
+ showcaseDrawer.erase(bitmapBuffer);
+
+ // Draw the showcase drawable
+ if (!hasNoTarget) {
+ showcaseDrawer.drawShowcase(bitmapBuffer, showcaseX, showcaseY, scaleMultiplier);
+ showcaseDrawer.drawToCanvas(canvas, bitmapBuffer);
+ }
+
+ // Draw the text on the screen, recalculating its position if necessary
+ textDrawer.draw(canvas);
+
+ super.dispatchDraw(canvas);
+
+ }
+
+ @Override
+ public void onClick(View view) {
+ hide();
+ }
+
+ public void hide() {
+ clearBitmap();
+ // If the type is set to one-shot, store that it has shot
+ shotStateStore.storeShot();
+ mEventListener.onShowcaseViewHide(this);
+ fadeOutShowcase();
+ }
+
+ private void clearBitmap() {
+ if (bitmapBuffer != null && !bitmapBuffer.isRecycled()) {
+ bitmapBuffer.recycle();
+ bitmapBuffer = null;
+ }
+ }
+
+ private void fadeOutShowcase() {
+ animationFactory.fadeOutView(this, fadeOutMillis, new AnimationEndListener() {
+ @Override
+ public void onAnimationEnd() {
+ setVisibility(View.GONE);
+ mEventListener.onShowcaseViewDidHide(ShowcaseView.this);
+ }
+ });
+ }
+
+ public void show() {
+ mEventListener.onShowcaseViewShow(this);
+ fadeInShowcase();
+ }
+
+ private void fadeInShowcase() {
+ animationFactory.fadeInView(this, fadeInMillis,
+ new AnimationStartListener() {
+ @Override
+ public void onAnimationStart() {
+ 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 (MotionEvent.ACTION_UP == motionEvent.getAction() &&
+ hideOnTouch && distanceFromFocus > showcaseDrawer.getBlockedRadius()) {
+ this.hide();
+ return true;
+ }
+
+ return blockTouches && distanceFromFocus > showcaseDrawer.getBlockedRadius();
+ }
+
+ private static void insertShowcaseView(ShowcaseView showcaseView, Activity activity) {
+ ((ViewGroup) activity.getWindow().getDecorView()).addView(showcaseView);
+ if (!showcaseView.hasShot()) {
+ showcaseView.show();
+ } else {
+ showcaseView.hideImmediate();
+ }
+ }
+
+ private void hideImmediate() {
+ setVisibility(GONE);
+ }
+
+ public void setContentTitle(CharSequence title) {
+ textDrawer.setContentTitle(title);
+ }
+
+ public void setContentText(CharSequence text) {
+ textDrawer.setContentText(text);
+ }
+
+ private void setScaleMultiplier(float scaleMultiplier) {
+ this.scaleMultiplier = scaleMultiplier;
+ }
+
+ @Override
+ public void onGlobalLayout() {
+ if (!shotStateStore.hasShot()) {
+ updateBitmap();
+ }
+ }
+
+ public void hideButton() {
+ mEndButton.setVisibility(GONE);
+ }
+
+ public void showButton() {
+ mEndButton.setVisibility(VISIBLE);
+ }
+
+ /**
+ * Builder class which allows easier creation of {@link ShowcaseView}s.
+ * It is recommended that you use this Builder class.
+ */
+ public static class Builder {
+
+ final ShowcaseView showcaseView;
+ private final Activity activity;
+
+ public Builder(Activity activity) {
+ this(activity, false);
+ }
+
+ public Builder(Activity activity, boolean useNewStyle) {
+ this.activity = activity;
+ this.showcaseView = new ShowcaseView(activity, useNewStyle);
+ this.showcaseView.setTarget(Target.NONE);
+ }
+
+ /**
+ * Create the {@link com.github.amlcurran.showcaseview.ShowcaseView} and show it.
+ *
+ * @return the created ShowcaseView
+ */
+ public ShowcaseView build() {
+ insertShowcaseView(showcaseView, activity);
+ return showcaseView;
+ }
+
+ /**
+ * Set the title text shown on the ShowcaseView.
+ */
+ public Builder setContentTitle(int resId) {
+ return setContentTitle(activity.getString(resId));
+ }
+
+ /**
+ * Set the title text shown on the ShowcaseView.
+ */
+ public Builder setContentTitle(CharSequence title) {
+ showcaseView.setContentTitle(title);
+ return this;
+ }
+
+ /**
+ * Set the descriptive text shown on the ShowcaseView.
+ */
+ public Builder setContentText(int resId) {
+ return setContentText(activity.getString(resId));
+ }
+
+ /**
+ * Set the descriptive text shown on the ShowcaseView.
+ */
+ public Builder setContentText(CharSequence text) {
+ showcaseView.setContentText(text);
+ return this;
+ }
+
+ /**
+ * Set the target of the showcase.
+ *
+ * @param target a {@link com.github.amlcurran.showcaseview.targets.Target} representing
+ * the item to showcase (e.g., a button, or action item).
+ */
+ public Builder setTarget(Target target) {
+ showcaseView.setTarget(target);
+ return this;
+ }
+
+ /**
+ * Set the style of the ShowcaseView. See the sample app for example styles.
+ */
+ public Builder setStyle(int theme) {
+ showcaseView.setStyle(theme);
+ return this;
+ }
+
+ /**
+ * Set a listener which will override the button clicks.
+ * <p/>
+ * Note that you will have to manually hide the ShowcaseView
+ */
+ public Builder setOnClickListener(OnClickListener onClickListener) {
+ showcaseView.overrideButtonClick(onClickListener);
+ return this;
+ }
+
+ /**
+ * Don't make the ShowcaseView block touches on itself. This doesn't
+ * block touches in the showcased area.
+ * <p/>
+ * By default, the ShowcaseView does block touches
+ */
+ public Builder doNotBlockTouches() {
+ showcaseView.setBlocksTouches(false);
+ return this;
+ }
+
+ /**
+ * Make this ShowcaseView hide when the user touches outside the showcased area.
+ * This enables {@link #doNotBlockTouches()} as well.
+ * <p/>
+ * By default, the ShowcaseView doesn't hide on touch.
+ */
+ public Builder hideOnTouchOutside() {
+ showcaseView.setBlocksTouches(true);
+ showcaseView.setHideOnTouchOutside(true);
+ return this;
+ }
+
+ /**
+ * Set the ShowcaseView to only ever show once.
+ *
+ * @param shotId a unique identifier (<em>across the app</em>) to store
+ * whether this ShowcaseView has been shown.
+ */
+ public Builder singleShot(long shotId) {
+ showcaseView.setSingleShot(shotId);
+ return this;
+ }
+
+ public Builder setShowcaseEventListener(OnShowcaseEventListener showcaseEventListener) {
+ showcaseView.setOnShowcaseEventListener(showcaseEventListener);
+ return this;
+ }
+ }
+
+ /**
+ * Set whether the text should be centred in the screen, or left-aligned (which is the default).
+ */
+ public void setShouldCentreText(boolean shouldCentreText) {
+ this.shouldCentreText = shouldCentreText;
+ hasAlteredText = true;
+ invalidate();
+ }
+
+ /**
+ * @see com.github.amlcurran.showcaseview.ShowcaseView.Builder#setSingleShot(long)
+ */
+ private void setSingleShot(long shotId) {
+ shotStateStore.setSingleShot(shotId);
+ }
+
+ /**
+ * Change the position of the ShowcaseView's button from the default bottom-right position.
+ *
+ * @param layoutParams a {@link android.widget.RelativeLayout.LayoutParams} representing
+ * the new position of the button
+ */
+ public void setButtonPosition(RelativeLayout.LayoutParams layoutParams) {
+ mEndButton.setLayoutParams(layoutParams);
+ }
+
+ /**
+ * Set the duration of the fading in and fading out of the ShowcaseView
+ */
+ private void setFadeDurations(long fadeInMillis, long fadeOutMillis) {
+ this.fadeInMillis = fadeInMillis;
+ this.fadeOutMillis = fadeOutMillis;
+ }
+
+ /**
+ * @see com.github.amlcurran.showcaseview.ShowcaseView.Builder#hideOnTouchOutside()
+ */
+ public void setHideOnTouchOutside(boolean hideOnTouch) {
+ this.hideOnTouch = hideOnTouch;
+ }
+
+ /**
+ * @see com.github.amlcurran.showcaseview.ShowcaseView.Builder#doNotBlockTouches()
+ */
+ public void setBlocksTouches(boolean blockTouches) {
+ this.blockTouches = blockTouches;
+ }
+
+ /**
+ * @see com.github.amlcurran.showcaseview.ShowcaseView.Builder#setStyle(int)
+ */
+ public void setStyle(int theme) {
+ TypedArray array = getContext().obtainStyledAttributes(theme, R.styleable.ShowcaseView);
+ updateStyle(array, true);
+ }
+
+ private void updateStyle(TypedArray styled, boolean invalidate) {
+ int backgroundColor = styled.getColor(R.styleable.ShowcaseView_sv_backgroundColor, Color.argb(128, 80, 80, 80));
+ int showcaseColor = styled.getColor(R.styleable.ShowcaseView_sv_showcaseColor, HOLO_BLUE);
+ String buttonText = styled.getString(R.styleable.ShowcaseView_sv_buttonText);
+ if (TextUtils.isEmpty(buttonText)) {
+ buttonText = getResources().getString(android.R.string.ok);
+ }
+ boolean tintButton = styled.getBoolean(R.styleable.ShowcaseView_sv_tintButtonColor, true);
+
+ int titleTextAppearance = styled.getResourceId(R.styleable.ShowcaseView_sv_titleTextAppearance,
+ R.style.TextAppearance_ShowcaseView_Title);
+ int detailTextAppearance = styled.getResourceId(R.styleable.ShowcaseView_sv_detailTextAppearance,
+ R.style.TextAppearance_ShowcaseView_Detail);
+
+ styled.recycle();
+
+ showcaseDrawer.setShowcaseColour(showcaseColor);
+ showcaseDrawer.setBackgroundColour(backgroundColor);
+ tintButton(showcaseColor, tintButton);
+ mEndButton.setText(buttonText);
+ textDrawer.setTitleStyling(titleTextAppearance);
+ textDrawer.setDetailStyling(detailTextAppearance);
+ hasAlteredText = true;
+
+ if (invalidate) {
+ invalidate();
+ }
+ }
+
+ private void tintButton(int showcaseColor, boolean tintButton) {
+ if (tintButton) {
+ mEndButton.getBackground().setColorFilter(showcaseColor, PorterDuff.Mode.MULTIPLY);
+ } else {
+ mEndButton.getBackground().setColorFilter(HOLO_BLUE, PorterDuff.Mode.MULTIPLY);
+ }
+ }
+
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/StandardShowcaseDrawer.java b/showcaseview/java/com/github/amlcurran/showcaseview/StandardShowcaseDrawer.java
new file mode 100644
index 0000000..843b75b
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/StandardShowcaseDrawer.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Created by curraa01 on 13/10/2013.
+ */
+class StandardShowcaseDrawer implements ShowcaseDrawer {
+
+ protected final Paint eraserPaint;
+ protected final Drawable showcaseDrawable;
+ private final Paint basicPaint;
+ private final float showcaseRadius;
+ protected int backgroundColour;
+
+ public StandardShowcaseDrawer(Resources resources) {
+ PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);
+ eraserPaint = new Paint();
+ eraserPaint.setColor(0xFFFFFF);
+ eraserPaint.setAlpha(0);
+ eraserPaint.setXfermode(xfermode);
+ eraserPaint.setAntiAlias(true);
+ basicPaint = new Paint();
+ showcaseRadius = resources.getDimension(R.dimen.showcase_radius);
+ showcaseDrawable = resources.getDrawable(R.drawable.cling_bleached);
+ }
+
+ @Override
+ public void setShowcaseColour(int color) {
+ showcaseDrawable.setColorFilter(color, PorterDuff.Mode.MULTIPLY);
+ }
+
+ @Override
+ public void drawShowcase(Bitmap buffer, float x, float y, float scaleMultiplier) {
+ Canvas bufferCanvas = new Canvas(buffer);
+ bufferCanvas.drawCircle(x, y, showcaseRadius, eraserPaint);
+ int halfW = getShowcaseWidth() / 2;
+ int halfH = getShowcaseHeight() / 2;
+ int left = (int) (x - halfW);
+ int top = (int) (y - halfH);
+ showcaseDrawable.setBounds(left, top,
+ left + getShowcaseWidth(),
+ top + getShowcaseHeight());
+ showcaseDrawable.draw(bufferCanvas);
+ }
+
+ @Override
+ public int getShowcaseWidth() {
+ return showcaseDrawable.getIntrinsicWidth();
+ }
+
+ @Override
+ public int getShowcaseHeight() {
+ return showcaseDrawable.getIntrinsicHeight();
+ }
+
+ @Override
+ public float getBlockedRadius() {
+ return showcaseRadius;
+ }
+
+ @Override
+ public void setBackgroundColour(int backgroundColor) {
+ this.backgroundColour = backgroundColor;
+ }
+
+ @Override
+ public void erase(Bitmap bitmapBuffer) {
+ bitmapBuffer.eraseColor(backgroundColour);
+ }
+
+ @Override
+ public void drawToCanvas(Canvas canvas, Bitmap bitmapBuffer) {
+ canvas.drawBitmap(bitmapBuffer, 0, 0, basicPaint);
+ }
+
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/TextDrawer.java b/showcaseview/java/com/github/amlcurran/showcaseview/TextDrawer.java
new file mode 100644
index 0000000..3936d7b
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/TextDrawer.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.text.DynamicLayout;
+import android.text.Layout;
+import android.text.SpannableString;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.TextAppearanceSpan;
+
+/**
+ * Draws the text as required by the ShowcaseView
+ */
+class TextDrawer {
+
+ private final TextPaint titlePaint;
+ private final TextPaint textPaint;
+ private final Context context;
+ private final ShowcaseAreaCalculator calculator;
+ private final float padding;
+ private final float actionBarOffset;
+
+ private CharSequence mTitle, mDetails;
+ private float[] mBestTextPosition = new float[3];
+ private DynamicLayout mDynamicTitleLayout;
+ private DynamicLayout mDynamicDetailLayout;
+ private TextAppearanceSpan mTitleSpan;
+ private TextAppearanceSpan mDetailSpan;
+ private boolean hasRecalculated;
+
+ public TextDrawer(Resources resources, ShowcaseAreaCalculator calculator, Context context) {
+ padding = resources.getDimension(R.dimen.text_padding);
+ actionBarOffset = resources.getDimension(R.dimen.action_bar_offset);
+
+ this.calculator = calculator;
+ this.context = context;
+
+ titlePaint = new TextPaint();
+ titlePaint.setAntiAlias(true);
+
+ textPaint = new TextPaint();
+ textPaint.setAntiAlias(true);
+ }
+
+ public void draw(Canvas canvas) {
+ if (shouldDrawText()) {
+ float[] textPosition = getBestTextPosition();
+
+ if (!TextUtils.isEmpty(mTitle)) {
+ canvas.save();
+ if (hasRecalculated) {
+ mDynamicTitleLayout = new DynamicLayout(mTitle, titlePaint,
+ (int) textPosition[2], Layout.Alignment.ALIGN_NORMAL,
+ 1.0f, 1.0f, true);
+ }
+ if (mDynamicTitleLayout != null) {
+ canvas.translate(textPosition[0], textPosition[1]);
+ mDynamicTitleLayout.draw(canvas);
+ canvas.restore();
+ }
+ }
+
+ if (!TextUtils.isEmpty(mDetails)) {
+ canvas.save();
+ if (hasRecalculated) {
+ mDynamicDetailLayout = new DynamicLayout(mDetails, textPaint,
+ (int) textPosition[2],
+ Layout.Alignment.ALIGN_NORMAL,
+ 1.2f, 1.0f, true);
+ }
+ float offsetForTitle = mDynamicTitleLayout != null ? mDynamicTitleLayout.getHeight() :
+ 0;
+ if (mDynamicDetailLayout != null) {
+ canvas.translate(textPosition[0], textPosition[1] + offsetForTitle);
+ mDynamicDetailLayout.draw(canvas);
+ canvas.restore();
+ }
+
+ }
+ }
+ hasRecalculated = false;
+ }
+
+ public void setContentText(CharSequence details) {
+ if (details != null) {
+ SpannableString ssbDetail = new SpannableString(details);
+ ssbDetail.setSpan(mDetailSpan, 0, ssbDetail.length(), 0);
+ mDetails = ssbDetail;
+ }
+ }
+
+ public void setContentTitle(CharSequence title) {
+ if (title != null) {
+ SpannableString ssbTitle = new SpannableString(title);
+ ssbTitle.setSpan(mTitleSpan, 0, ssbTitle.length(), 0);
+ mTitle = ssbTitle;
+ }
+ }
+
+ /**
+ * Calculates the best place to position text
+ * @param canvasW width of the screen
+ * @param canvasH height of the screen
+ * @param shouldCentreText
+ */
+ public void calculateTextPosition(int canvasW, int canvasH, ShowcaseView showcaseView, boolean shouldCentreText) {
+
+ Rect showcase = showcaseView.hasShowcaseView() ?
+ calculator.getShowcaseRect() :
+ new Rect();
+
+ int[] areas = new int[4]; //left, top, right, bottom
+ areas[0] = showcase.left * canvasH;
+ areas[1] = showcase.top * canvasW;
+ areas[2] = (canvasW - showcase.right) * canvasH;
+ areas[3] = (canvasH - showcase.bottom) * canvasW;
+
+ int largest = 0;
+ for(int i = 1; i < areas.length; i++) {
+ if(areas[i] > areas[largest])
+ largest = i;
+ }
+
+ // Position text in largest area
+ switch(largest) {
+ case 0:
+ mBestTextPosition[0] = padding;
+ mBestTextPosition[1] = padding;
+ mBestTextPosition[2] = showcase.left - 2 * padding;
+ break;
+ case 1:
+ mBestTextPosition[0] = padding;
+ mBestTextPosition[1] = padding + actionBarOffset;
+ mBestTextPosition[2] = canvasW - 2 * padding;
+ break;
+ case 2:
+ mBestTextPosition[0] = showcase.right + padding;
+ mBestTextPosition[1] = padding;
+ mBestTextPosition[2] = (canvasW - showcase.right) - 2 * padding;
+ break;
+ case 3:
+ mBestTextPosition[0] = padding;
+ mBestTextPosition[1] = showcase.bottom + padding;
+ mBestTextPosition[2] = canvasW - 2 * padding;
+ break;
+ }
+ if(shouldCentreText) {
+ // Center text vertically or horizontally
+ switch(largest) {
+ case 0:
+ case 2:
+ mBestTextPosition[1] += canvasH / 4;
+ break;
+ case 1:
+ case 3:
+ mBestTextPosition[2] /= 2;
+ mBestTextPosition[0] += canvasW / 4;
+ break;
+ }
+ } else {
+ // As text is not centered add actionbar padding if the text is left or right
+ switch(largest) {
+ case 0:
+ case 2:
+ mBestTextPosition[1] += actionBarOffset;
+ break;
+ }
+ }
+
+ hasRecalculated = true;
+ }
+
+ public void setTitleStyling(int styleId) {
+ mTitleSpan = new TextAppearanceSpan(this.context, styleId);
+ setContentTitle(mTitle);
+ }
+
+ public void setDetailStyling(int styleId) {
+ mDetailSpan = new TextAppearanceSpan(this.context, styleId);
+ setContentText(mDetails);
+ }
+
+ public CharSequence getContentTitle() {
+ return mTitle;
+ }
+
+ public CharSequence getContentText() {
+ return mDetails;
+ }
+
+ public float[] getBestTextPosition() {
+ return mBestTextPosition;
+ }
+
+ public boolean shouldDrawText() {
+ return !TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mDetails);
+ }
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionBarReflector.java b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionBarReflector.java
new file mode 100644
index 0000000..6022b5a
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionBarReflector.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview.targets;
+
+import android.app.Activity;
+import android.view.View;
+import android.view.ViewParent;
+
+/**
+ * Reflector which finds action items in the standard API 11 ActionBar implementation
+ */
+class ActionBarReflector implements Reflector {
+
+ private Activity mActivity;
+
+ public ActionBarReflector(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public ViewParent getActionBarView() {
+ return getHomeButton().getParent().getParent();
+ }
+
+ @Override
+ public View getHomeButton() {
+ View homeButton = mActivity.findViewById(android.R.id.home);
+ if (homeButton == null) {
+ throw new RuntimeException(
+ "insertShowcaseViewWithType cannot be used when the theme " +
+ "has no ActionBar");
+ }
+ return homeButton;
+ }
+
+ @Override
+ public void showcaseActionItem(int itemId) {
+
+ }
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionBarViewWrapper.java b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionBarViewWrapper.java
new file mode 100644
index 0000000..dd09d44
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionBarViewWrapper.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview.targets;
+
+import android.util.Log;
+import android.view.View;
+import android.view.ViewParent;
+
+import java.lang.reflect.Field;
+
+/**
+ * Class which wraps round the many implementations of ActionBarView and allows finding of Action
+ * items
+ */
+class ActionBarViewWrapper {
+
+ private ViewParent mActionBarView;
+ private Class mActionBarViewClass;
+ private Class mAbsActionBarViewClass;
+
+ public ActionBarViewWrapper(ViewParent actionBarView) {
+ if (!actionBarView.getClass().getName().contains("ActionBarView")) {
+ String previousP = actionBarView.getClass().getName();
+ actionBarView = actionBarView.getParent();
+ String throwP = actionBarView.getClass().getName();
+ if (!actionBarView.getClass().getName().contains("ActionBarView")) {
+ throw new IllegalStateException("Cannot find ActionBarView for " +
+ "Activity, instead found " + previousP + " and " + throwP);
+ }
+ }
+ mActionBarView = actionBarView;
+ mActionBarViewClass = actionBarView.getClass();
+ mAbsActionBarViewClass = actionBarView.getClass().getSuperclass();
+ }
+
+ /**
+ * Return the view which represents the spinner on the ActionBar, or null if there isn't one
+ */
+ public View getSpinnerView() {
+ try {
+ Field spinnerField = mActionBarViewClass.getDeclaredField("mSpinner");
+ spinnerField.setAccessible(true);
+ return (View) spinnerField.get(mActionBarView);
+ } catch (NoSuchFieldException e) {
+ Log.e("TAG", "Failed to find actionbar spinner", e);
+ } catch (IllegalAccessException e) {
+ Log.e("TAG", "Failed to access actionbar spinner", e);
+ }
+ return null;
+ }
+
+ /**
+ * Return the view which represents the title on the ActionBar, or null if there isn't one
+ */
+ public View getTitleView() {
+ try {
+ Field mTitleViewField = mActionBarViewClass.getDeclaredField("mTitleView");
+ mTitleViewField.setAccessible(true);
+ return (View) mTitleViewField.get(mActionBarView);
+ } catch (NoSuchFieldException e) {
+ Log.e("TAG", "Failed to find actionbar title", e);
+ } catch (IllegalAccessException e) {
+ Log.e("TAG", "Failed to access actionbar title", e);
+ }
+ return null;
+ }
+
+ /**
+ * Return the view which represents the overflow action item on the ActionBar, or null if there isn't one
+ */
+ public View getOverflowView() {
+ try {
+ Field actionMenuPresenterField = mAbsActionBarViewClass.getDeclaredField("mActionMenuPresenter");
+ actionMenuPresenterField.setAccessible(true);
+ Object actionMenuPresenter = actionMenuPresenterField.get(mActionBarView);
+ Field overflowButtonField = actionMenuPresenter.getClass().getDeclaredField("mOverflowButton");
+ overflowButtonField.setAccessible(true);
+ return (View) overflowButtonField.get(actionMenuPresenter);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Return the view which represents the MediaRouterButton action item on the ActionBar, or null
+ * if there isn't one
+ */
+ public View getMediaRouterButtonView() {
+ try {
+ Field actionMenuPresenterField =
+ mAbsActionBarViewClass.getDeclaredField("mActionMenuPresenter");
+ actionMenuPresenterField.setAccessible(true);
+ Object actionMenuPresenter = actionMenuPresenterField.get(mActionBarView);
+ Field mediaRouteButtonField = actionMenuPresenter.getClass()
+ .getDeclaredField("mScrapActionButtonView");
+ mediaRouteButtonField.setAccessible(true);
+ return (View) mediaRouteButtonField.get(actionMenuPresenter);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public View getActionItem(int actionItemId) {
+ try {
+ Field actionMenuPresenterField = mAbsActionBarViewClass.getDeclaredField("mActionMenuPresenter");
+ actionMenuPresenterField.setAccessible(true);
+ Object actionMenuPresenter = actionMenuPresenterField.get(mActionBarView);
+
+ Field menuViewField = actionMenuPresenter.getClass().getSuperclass().getDeclaredField("mMenuView");
+ menuViewField.setAccessible(true);
+ Object menuView = menuViewField.get(actionMenuPresenter);
+
+ Field mChField;
+ if (menuView.getClass().toString().contains("com.actionbarsherlock")) {
+ // There are thousands of superclasses to traverse up
+ // Have to get superclasses because mChildren is private
+ mChField = menuView.getClass().getSuperclass().getSuperclass()
+ .getSuperclass().getSuperclass().getDeclaredField("mChildren");
+ } else if (menuView.getClass().toString().contains("android.support.v7")) {
+ mChField = menuView.getClass().getSuperclass().getSuperclass()
+ .getSuperclass().getDeclaredField("mChildren");
+ } else {
+ mChField = menuView.getClass().getSuperclass().getSuperclass()
+ .getDeclaredField("mChildren");
+ }
+ mChField.setAccessible(true);
+ Object[] mChs = (Object[]) mChField.get(menuView);
+ for (Object mCh : mChs) {
+ if (mCh != null) {
+ View v = (View) mCh;
+ if (v.getId() == actionItemId) {
+ return v;
+ }
+ }
+ }
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionItemTarget.java b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionItemTarget.java
new file mode 100644
index 0000000..9abfab0
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionItemTarget.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview.targets;
+
+import android.app.Activity;
+import android.graphics.Point;
+import android.view.ViewParent;
+
+/**
+ * Represents an Action item to showcase (e.g., one of the buttons on an ActionBar).
+ * To showcase specific action views such as the home button, use {@link com.github.amlcurran.showcaseview.targets.ActionItemTarget}
+ *
+ * @see com.github.amlcurran.showcaseview.targets.ActionItemTarget
+ */
+public class ActionItemTarget implements Target {
+
+ private final Activity mActivity;
+ private final int mItemId;
+
+ ActionBarViewWrapper mActionBarWrapper;
+
+ public ActionItemTarget(Activity activity, int itemId) {
+ mActivity = activity;
+ mItemId = itemId;
+ }
+
+ @Override
+ public Point getPoint() {
+ setUp();
+ return new ViewTarget(mActionBarWrapper.getActionItem(mItemId)).getPoint();
+ }
+
+ protected void setUp() {
+ Reflector reflector = ReflectorFactory.getReflectorForActivity(mActivity);
+ ViewParent p = reflector.getActionBarView(); //ActionBarView
+ mActionBarWrapper = new ActionBarViewWrapper(p);
+ }
+
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionViewTarget.java b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionViewTarget.java
new file mode 100644
index 0000000..80e3ac2
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ActionViewTarget.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview.targets;
+
+import android.app.Activity;
+import android.graphics.Point;
+import android.view.ViewParent;
+
+public class ActionViewTarget implements Target {
+
+ private final Activity mActivity;
+ private final Type mType;
+
+ ActionBarViewWrapper mActionBarWrapper;
+ Reflector mReflector;
+
+ public ActionViewTarget(Activity activity, Type type) {
+ mActivity = activity;
+ mType = type;
+ }
+
+ protected void setUp() {
+ mReflector = ReflectorFactory.getReflectorForActivity(mActivity);
+ ViewParent p = mReflector.getActionBarView(); //ActionBarView
+ mActionBarWrapper = new ActionBarViewWrapper(p);
+ }
+
+ @Override
+ public Point getPoint() {
+ Target internal = null;
+ setUp();
+ switch (mType) {
+
+ case SPINNER:
+ internal = new ViewTarget(mActionBarWrapper.getSpinnerView());
+ break;
+
+ case HOME:
+ internal = new ViewTarget(mReflector.getHomeButton());
+ break;
+
+ case OVERFLOW:
+ internal = new ViewTarget(mActionBarWrapper.getOverflowView());
+ break;
+
+ case TITLE:
+ internal = new ViewTarget(mActionBarWrapper.getTitleView());
+ break;
+
+ case MEDIA_ROUTE_BUTTON:
+ internal = new ViewTarget(mActionBarWrapper.getMediaRouterButtonView());
+ break;
+
+ }
+ return internal.getPoint();
+ }
+
+ public enum Type {
+ SPINNER, HOME, TITLE, OVERFLOW, MEDIA_ROUTE_BUTTON
+ }
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/targets/AppCompatReflector.java b/showcaseview/java/com/github/amlcurran/showcaseview/targets/AppCompatReflector.java
new file mode 100644
index 0000000..7f815c8
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/targets/AppCompatReflector.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview.targets;
+
+import android.app.Activity;
+import android.view.View;
+import android.view.ViewParent;
+
+/**
+ * Created by Alex on 27/10/13.
+ */
+class AppCompatReflector implements Reflector {
+
+ private Activity mActivity;
+
+ public AppCompatReflector(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public ViewParent getActionBarView() {
+ return getHomeButton().getParent().getParent();
+ }
+
+ @Override
+ public View getHomeButton() {
+ View homeButton = mActivity.findViewById(android.R.id.home);
+ if (homeButton != null) {
+ return homeButton;
+ }
+ int homeId = mActivity.getResources().getIdentifier("home", "id", mActivity.getPackageName());
+ homeButton = mActivity.findViewById(homeId);
+ if (homeButton == null) {
+ throw new RuntimeException(
+ "insertShowcaseViewWithType cannot be used when the theme " +
+ "has no ActionBar");
+ }
+ return homeButton;
+ }
+
+ @Override
+ public void showcaseActionItem(int itemId) {
+
+ }
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/targets/PointTarget.java b/showcaseview/java/com/github/amlcurran/showcaseview/targets/PointTarget.java
new file mode 100644
index 0000000..bfdaecd
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/targets/PointTarget.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview.targets;
+
+import android.graphics.Point;
+
+/**
+ * Showcase a specific x/y co-ordinate on the screen.
+ */
+public class PointTarget implements Target {
+
+ private final Point mPoint;
+
+ public PointTarget(Point point) {
+ mPoint = point;
+ }
+
+ public PointTarget(int xValue, int yValue) {
+ mPoint = new Point(xValue, yValue);
+ }
+
+ @Override
+ public Point getPoint() {
+ return mPoint;
+ }
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/targets/Reflector.java b/showcaseview/java/com/github/amlcurran/showcaseview/targets/Reflector.java
new file mode 100644
index 0000000..afc3868
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/targets/Reflector.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview.targets;
+
+import android.view.View;
+import android.view.ViewParent;
+
+interface Reflector {
+ View getHomeButton();
+
+ void showcaseActionItem(int itemId);
+
+ ViewParent getActionBarView();
+
+ public enum ActionBarType {
+ STANDARD, APP_COMPAT, ACTIONBAR_SHERLOCK
+ }
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/targets/ReflectorFactory.java b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ReflectorFactory.java
new file mode 100644
index 0000000..833049e
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ReflectorFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview.targets;
+
+import android.app.Activity;
+
+/**
+ * Base class which uses reflection to determine how to showcase Action Items and Action Views.
+ */
+class ReflectorFactory {
+
+ public static Reflector getReflectorForActivity(Activity activity) {
+ switch (searchForActivitySuperClass(activity)) {
+ case STANDARD:
+ return new ActionBarReflector(activity);
+ case APP_COMPAT:
+ return new AppCompatReflector(activity);
+ case ACTIONBAR_SHERLOCK:
+ return new SherlockReflector(activity);
+ }
+ return null;
+ }
+
+ private static Reflector.ActionBarType searchForActivitySuperClass(Activity activity) {
+ Class currentLevel = activity.getClass();
+ while (currentLevel != Activity.class) {
+ if (currentLevel.getSimpleName().equals("SherlockActivity") || currentLevel.getSimpleName().equals("SherlockFragmentActivity")) {
+ return Reflector.ActionBarType.ACTIONBAR_SHERLOCK;
+ }
+ if (currentLevel.getSimpleName().equals("ActionBarActivity")) {
+ return Reflector.ActionBarType.APP_COMPAT;
+ }
+ currentLevel = currentLevel.getSuperclass();
+ }
+ return Reflector.ActionBarType.STANDARD;
+ }
+
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/targets/SherlockReflector.java b/showcaseview/java/com/github/amlcurran/showcaseview/targets/SherlockReflector.java
new file mode 100644
index 0000000..ce6f290
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/targets/SherlockReflector.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview.targets;
+
+import android.app.Activity;
+import android.view.View;
+import android.view.ViewParent;
+
+/**
+ * Created by Alex on 27/10/13.
+ */
+class SherlockReflector implements Reflector {
+
+ private Activity mActivity;
+
+ public SherlockReflector(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public ViewParent getActionBarView() {
+ return getHomeButton().getParent().getParent();
+ }
+
+ @Override
+ public View getHomeButton() {
+ View homeButton = mActivity.findViewById(android.R.id.home);
+ if (homeButton != null) {
+ return homeButton;
+ }
+ int homeId = mActivity.getResources().getIdentifier("abs__home", "id", mActivity.getPackageName());
+ homeButton = mActivity.findViewById(homeId);
+ if (homeButton == null) {
+ throw new RuntimeException(
+ "insertShowcaseViewWithType cannot be used when the theme " +
+ "has no ActionBar");
+ }
+ return homeButton;
+ }
+
+ @Override
+ public void showcaseActionItem(int itemId) {
+
+ }
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/targets/Target.java b/showcaseview/java/com/github/amlcurran/showcaseview/targets/Target.java
new file mode 100644
index 0000000..8132a40
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/targets/Target.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview.targets;
+
+import android.graphics.Point;
+
+public interface Target {
+ Target NONE = new Target() {
+ @Override
+ public Point getPoint() {
+ return new Point(1000000, 1000000);
+ }
+ };
+
+ public Point getPoint();
+}
diff --git a/showcaseview/java/com/github/amlcurran/showcaseview/targets/ViewTarget.java b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ViewTarget.java
new file mode 100644
index 0000000..d59dd0d
--- /dev/null
+++ b/showcaseview/java/com/github/amlcurran/showcaseview/targets/ViewTarget.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 Alex Curran
+ *
+ * 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.github.amlcurran.showcaseview.targets;
+
+import android.app.Activity;
+import android.graphics.Point;
+import android.view.View;
+
+/**
+ * Target a view on the screen. This will centre the target on the view.
+ */
+public class ViewTarget implements Target {
+
+ private final View mView;
+
+ public ViewTarget(View view) {
+ mView = view;
+ }
+
+ public ViewTarget(int viewId, Activity activity) {
+ mView = activity.findViewById(viewId);
+ }
+
+ @Override
+ public Point getPoint() {
+ int[] location = new int[2];
+ mView.getLocationInWindow(location);
+ int x = location[0] + mView.getWidth() / 2;
+ int y = location[1] + mView.getHeight() / 2;
+ return new Point(x, y);
+ }
+}
diff --git a/showcaseview/project.properties b/showcaseview/project.properties
new file mode 100644
index 0000000..91d2b02
--- /dev/null
+++ b/showcaseview/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
diff --git a/showcaseview/res/drawable-hdpi/btn_cling_normal.9.png b/showcaseview/res/drawable-hdpi/btn_cling_normal.9.png
new file mode 100644
index 0000000..aea8beb
--- /dev/null
+++ b/showcaseview/res/drawable-hdpi/btn_cling_normal.9.png
Binary files differ
diff --git a/showcaseview/res/drawable-hdpi/btn_cling_pressed.9.png b/showcaseview/res/drawable-hdpi/btn_cling_pressed.9.png
new file mode 100644
index 0000000..ebefd20
--- /dev/null
+++ b/showcaseview/res/drawable-hdpi/btn_cling_pressed.9.png
Binary files differ
diff --git a/showcaseview/res/drawable-hdpi/cling.png b/showcaseview/res/drawable-hdpi/cling.png
new file mode 100644
index 0000000..d605799
--- /dev/null
+++ b/showcaseview/res/drawable-hdpi/cling.png
Binary files differ
diff --git a/showcaseview/res/drawable-hdpi/hand.png b/showcaseview/res/drawable-hdpi/hand.png
new file mode 100644
index 0000000..bd4f6df
--- /dev/null
+++ b/showcaseview/res/drawable-hdpi/hand.png
Binary files differ
diff --git a/showcaseview/res/drawable-mdpi/btn_cling_normal.9.png b/showcaseview/res/drawable-mdpi/btn_cling_normal.9.png
new file mode 100644
index 0000000..43a407e
--- /dev/null
+++ b/showcaseview/res/drawable-mdpi/btn_cling_normal.9.png
Binary files differ
diff --git a/showcaseview/res/drawable-mdpi/btn_cling_pressed.9.png b/showcaseview/res/drawable-mdpi/btn_cling_pressed.9.png
new file mode 100644
index 0000000..bf0c8cb
--- /dev/null
+++ b/showcaseview/res/drawable-mdpi/btn_cling_pressed.9.png
Binary files differ
diff --git a/showcaseview/res/drawable-mdpi/cling.png b/showcaseview/res/drawable-mdpi/cling.png
new file mode 100644
index 0000000..fba3a07
--- /dev/null
+++ b/showcaseview/res/drawable-mdpi/cling.png
Binary files differ
diff --git a/showcaseview/res/drawable-mdpi/hand.png b/showcaseview/res/drawable-mdpi/hand.png
new file mode 100644
index 0000000..fe5a035
--- /dev/null
+++ b/showcaseview/res/drawable-mdpi/hand.png
Binary files differ
diff --git a/showcaseview/res/drawable-xhdpi/btn_cling_normal.9.png b/showcaseview/res/drawable-xhdpi/btn_cling_normal.9.png
new file mode 100644
index 0000000..35511d6
--- /dev/null
+++ b/showcaseview/res/drawable-xhdpi/btn_cling_normal.9.png
Binary files differ
diff --git a/showcaseview/res/drawable-xhdpi/btn_cling_pressed.9.png b/showcaseview/res/drawable-xhdpi/btn_cling_pressed.9.png
new file mode 100644
index 0000000..a38b40f
--- /dev/null
+++ b/showcaseview/res/drawable-xhdpi/btn_cling_pressed.9.png
Binary files differ
diff --git a/showcaseview/res/drawable-xhdpi/cling.png b/showcaseview/res/drawable-xhdpi/cling.png
new file mode 100644
index 0000000..1cee1de
--- /dev/null
+++ b/showcaseview/res/drawable-xhdpi/cling.png
Binary files differ
diff --git a/showcaseview/res/drawable-xhdpi/cling_bleached.png b/showcaseview/res/drawable-xhdpi/cling_bleached.png
new file mode 100644
index 0000000..9dfecfd
--- /dev/null
+++ b/showcaseview/res/drawable-xhdpi/cling_bleached.png
Binary files differ
diff --git a/showcaseview/res/drawable-xhdpi/hand.png b/showcaseview/res/drawable-xhdpi/hand.png
new file mode 100644
index 0000000..35b678c
--- /dev/null
+++ b/showcaseview/res/drawable-xhdpi/hand.png
Binary files differ
diff --git a/showcaseview/res/drawable/button.xml b/showcaseview/res/drawable/button.xml
new file mode 100644
index 0000000..bbc580f
--- /dev/null
+++ b/showcaseview/res/drawable/button.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2014 Alex Curran
+ ~
+ ~ 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/button_normal" />
+</selector> \ No newline at end of file
diff --git a/showcaseview/res/drawable/button_normal.xml b/showcaseview/res/drawable/button_normal.xml
new file mode 100644
index 0000000..ab33c99
--- /dev/null
+++ b/showcaseview/res/drawable/button_normal.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2014 Alex Curran
+ ~
+ ~ 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item>
+ <shape android:shape="rectangle">
+ <padding android:bottom="2dp" />
+ <solid android:color="#99FFFFFF" />
+ </shape>
+ </item>
+
+ <!--<item>-->
+ <!--<shape android:shape="rectangle">-->
+ <!--<padding android:top="2dp" />-->
+ <!--<solid android:color="#33FFFFFF" />-->
+ <!--</shape>-->
+ <!--</item>-->
+
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="#FFFFFF" />
+ </shape>
+ </item>
+
+</layer-list> \ No newline at end of file
diff --git a/showcaseview/res/drawable/cling_button_bg.xml b/showcaseview/res/drawable/cling_button_bg.xml
new file mode 100644
index 0000000..38751c6
--- /dev/null
+++ b/showcaseview/res/drawable/cling_button_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2014 Alex Curran
+ ~
+ ~ 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/btn_cling_pressed"/>
+ <item android:drawable="@drawable/btn_cling_normal"/>
+</selector>
diff --git a/showcaseview/res/layout/handy.xml b/showcaseview/res/layout/handy.xml
new file mode 100644
index 0000000..585964f
--- /dev/null
+++ b/showcaseview/res/layout/handy.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright 2014 Alex Curran
+ ~
+ ~ 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.
+ -->
+
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/hand" /> \ No newline at end of file
diff --git a/showcaseview/res/layout/showcase_button.xml b/showcaseview/res/layout/showcase_button.xml
new file mode 100644
index 0000000..1ce45cd
--- /dev/null
+++ b/showcaseview/res/layout/showcase_button.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2014 Alex Curran
+ ~
+ ~ 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.
+ -->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/ShowcaseButton"
+ android:text="@string/ok"
+ android:id="@id/showcase_button" /> \ No newline at end of file
diff --git a/showcaseview/res/values/attrs.xml b/showcaseview/res/values/attrs.xml
new file mode 100644
index 0000000..3eff0a4
--- /dev/null
+++ b/showcaseview/res/values/attrs.xml
@@ -0,0 +1,35 @@
+<!--
+ ~ Copyright 2014 Alex Curran
+ ~
+ ~ 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.
+ -->
+
+<resources>
+
+ <declare-styleable name="ShowcaseView">
+ <attr name="sv_backgroundColor" format="color|reference" />
+ <attr name="sv_detailTextColor" format="color|reference" />
+ <attr name="sv_titleTextColor" format="color|reference" />
+ <attr name="sv_buttonBackgroundColor" format="color|reference" />
+ <attr name="sv_buttonForegroundColor" format="color|reference" />
+ <attr name="sv_buttonText" format="string|reference" />
+ <attr name="sv_detailTextAppearance" format="reference" />
+ <attr name="sv_titleTextAppearance" format="reference" />
+ <attr name="sv_showcaseColor" format="color|reference" />
+ <attr name="sv_tintButtonColor" format="boolean|reference" />
+ </declare-styleable>
+ <declare-styleable name="CustomTheme">
+ <attr name="showcaseViewStyle" format="reference" />
+ </declare-styleable>
+
+</resources> \ No newline at end of file
diff --git a/showcaseview/res/values/dimens.xml b/showcaseview/res/values/dimens.xml
new file mode 100644
index 0000000..2906b72
--- /dev/null
+++ b/showcaseview/res/values/dimens.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2014 Alex Curran
+ ~
+ ~ 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.
+ -->
+
+<resources>
+ <dimen name="button_margin">12dp</dimen>
+ <dimen name="text_padding">24dp</dimen>
+ <dimen name="action_bar_offset">66dp</dimen>
+ <dimen name="showcase_radius">94dp</dimen>
+ <dimen name="showcase_radius_inner">96dp</dimen>
+ <dimen name="showcase_radius_outer">128dp</dimen>
+</resources> \ No newline at end of file
diff --git a/showcaseview/res/values/ids.xml b/showcaseview/res/values/ids.xml
new file mode 100644
index 0000000..2602369
--- /dev/null
+++ b/showcaseview/res/values/ids.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ Copyright 2014 Alex Curran
+ ~
+ ~ 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.
+ -->
+
+<resources>
+ <item type="id" name="showcase_button" />
+ <item type="id" name="showcase_title_text" />
+ <item type="id" name="showcase_sub_text" />
+</resources> \ No newline at end of file
diff --git a/showcaseview/res/values/strings.xml b/showcaseview/res/values/strings.xml
new file mode 100644
index 0000000..1708db8
--- /dev/null
+++ b/showcaseview/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2014 Alex Curran
+ ~
+ ~ 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.
+ -->
+
+<resources>
+ <string name="ok">OK</string>
+</resources>
diff --git a/showcaseview/res/values/styles.xml b/showcaseview/res/values/styles.xml
new file mode 100644
index 0000000..56ea6c4
--- /dev/null
+++ b/showcaseview/res/values/styles.xml
@@ -0,0 +1,61 @@
+<!--
+ ~ Copyright 2014 Alex Curran
+ ~
+ ~ 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.
+ -->
+
+<resources>
+
+ <style name="ShowcaseButton">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:minWidth">96dp</item>
+ <item name="android:gravity">center</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:textColor">#FFFFFF</item>
+ <item name="android:background">@drawable/button</item>
+ </style>
+
+ <style name="ShowcaseView.Light">
+ <item name="sv_titleTextAppearance">@style/TextAppearance.ShowcaseView.Title</item>
+ <item name="sv_detailTextAppearance">@style/TextAppearance.ShowcaseView.Detail.Light</item>
+ <item name="sv_backgroundColor">#3333B5E5</item>
+ <item name="sv_buttonText">@string/ok</item>
+ </style>
+
+ <style name="ShowcaseView">
+ <item name="sv_titleTextAppearance">@style/TextAppearance.ShowcaseView.Title</item>
+ <item name="sv_detailTextAppearance">@style/TextAppearance.ShowcaseView.Detail</item>
+ <item name="sv_backgroundColor">#3333B5E5</item>
+ <item name="sv_buttonText">@string/ok</item>
+ </style>
+
+ <style name="TextAppearance.ShowcaseView.Title" parent="android:style/TextAppearance.Large">
+ <item name="android:textColor">#33B5E5</item>
+ </style>
+
+ <style name="TextAppearance.ShowcaseView.Detail" parent="android:style/TextAppearance">
+ <item name="android:textColor">#FFFFFF</item>
+ </style>
+
+ <style name="TextAppearance.ShowcaseView.Detail.Light">
+ <item name="android:textColor">#333333</item>
+ <item name="android:shadowColor">#FF0000</item>
+ <item name="android:shadowRadius">0</item>
+ </style>
+
+ <style name="TextAppearance.ShowcaseView.Title.Light">
+ <item name="android:shadowRadius">0</item>
+ </style>
+
+</resources> \ No newline at end of file