diff options
author | Bananeweizen <bananeweizen@gmx.de> | 2014-08-14 22:28:19 +0200 |
---|---|---|
committer | Bananeweizen <bananeweizen@gmx.de> | 2014-08-14 22:28:19 +0200 |
commit | cfd75411787da9809a4f7e4e5e1f0be6a9a0fa77 (patch) | |
tree | 94687c96ea7cb15507e7310e419e371071d219ae /showcaseview | |
parent | 8c468294662cb57a1d7b8ec8b9a9195e277bc339 (diff) | |
download | cgeo-cfd75411787da9809a4f7e4e5e1f0be6a9a0fa77.zip cgeo-cfd75411787da9809a4f7e4e5e1f0be6a9a0fa77.tar.gz cgeo-cfd75411787da9809a4f7e4e5e1f0be6a9a0fa77.tar.bz2 |
initial showcase implementation
Diffstat (limited to 'showcaseview')
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 Binary files differnew file mode 100644 index 0000000..aea8beb --- /dev/null +++ b/showcaseview/res/drawable-hdpi/btn_cling_normal.9.png diff --git a/showcaseview/res/drawable-hdpi/btn_cling_pressed.9.png b/showcaseview/res/drawable-hdpi/btn_cling_pressed.9.png Binary files differnew file mode 100644 index 0000000..ebefd20 --- /dev/null +++ b/showcaseview/res/drawable-hdpi/btn_cling_pressed.9.png diff --git a/showcaseview/res/drawable-hdpi/cling.png b/showcaseview/res/drawable-hdpi/cling.png Binary files differnew file mode 100644 index 0000000..d605799 --- /dev/null +++ b/showcaseview/res/drawable-hdpi/cling.png diff --git a/showcaseview/res/drawable-hdpi/hand.png b/showcaseview/res/drawable-hdpi/hand.png Binary files differnew file mode 100644 index 0000000..bd4f6df --- /dev/null +++ b/showcaseview/res/drawable-hdpi/hand.png diff --git a/showcaseview/res/drawable-mdpi/btn_cling_normal.9.png b/showcaseview/res/drawable-mdpi/btn_cling_normal.9.png Binary files differnew file mode 100644 index 0000000..43a407e --- /dev/null +++ b/showcaseview/res/drawable-mdpi/btn_cling_normal.9.png diff --git a/showcaseview/res/drawable-mdpi/btn_cling_pressed.9.png b/showcaseview/res/drawable-mdpi/btn_cling_pressed.9.png Binary files differnew file mode 100644 index 0000000..bf0c8cb --- /dev/null +++ b/showcaseview/res/drawable-mdpi/btn_cling_pressed.9.png diff --git a/showcaseview/res/drawable-mdpi/cling.png b/showcaseview/res/drawable-mdpi/cling.png Binary files differnew file mode 100644 index 0000000..fba3a07 --- /dev/null +++ b/showcaseview/res/drawable-mdpi/cling.png diff --git a/showcaseview/res/drawable-mdpi/hand.png b/showcaseview/res/drawable-mdpi/hand.png Binary files differnew file mode 100644 index 0000000..fe5a035 --- /dev/null +++ b/showcaseview/res/drawable-mdpi/hand.png diff --git a/showcaseview/res/drawable-xhdpi/btn_cling_normal.9.png b/showcaseview/res/drawable-xhdpi/btn_cling_normal.9.png Binary files differnew file mode 100644 index 0000000..35511d6 --- /dev/null +++ b/showcaseview/res/drawable-xhdpi/btn_cling_normal.9.png diff --git a/showcaseview/res/drawable-xhdpi/btn_cling_pressed.9.png b/showcaseview/res/drawable-xhdpi/btn_cling_pressed.9.png Binary files differnew file mode 100644 index 0000000..a38b40f --- /dev/null +++ b/showcaseview/res/drawable-xhdpi/btn_cling_pressed.9.png diff --git a/showcaseview/res/drawable-xhdpi/cling.png b/showcaseview/res/drawable-xhdpi/cling.png Binary files differnew file mode 100644 index 0000000..1cee1de --- /dev/null +++ b/showcaseview/res/drawable-xhdpi/cling.png diff --git a/showcaseview/res/drawable-xhdpi/cling_bleached.png b/showcaseview/res/drawable-xhdpi/cling_bleached.png Binary files differnew file mode 100644 index 0000000..9dfecfd --- /dev/null +++ b/showcaseview/res/drawable-xhdpi/cling_bleached.png diff --git a/showcaseview/res/drawable-xhdpi/hand.png b/showcaseview/res/drawable-xhdpi/hand.png Binary files differnew file mode 100644 index 0000000..35b678c --- /dev/null +++ b/showcaseview/res/drawable-xhdpi/hand.png 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 |